java - starter - spring aop mkyong
AOP, Spring y el alcance de las transacciones (3)
imagine una aplicación java transaccional y multiproceso que utiliza spring, jdbc y aop con n clases en m paquetes que participan en las transacciones de la base de datos. Ahora digamos que es necesario delimitar un conjunto arbitrario de clases dentro de una transacción. Además, siempre hay una clase T dentro del alcance que compromete la transacción cuando se llama.
Permítanme dar un ejemplo de claridad: dados los paquetes A, B, Z y las clases A.Foo, B.Bar y ZT. Las siguientes instancias de las clases respectivas son llamadas (posiblemente por diferentes personas que llaman con otras clases intermedias): A. Foo, B.Bar, A.Foo, ZT Las transacciones se realizarán solo después de que se llame a ZT. Si la aplicación se cierra por algún motivo, la transacción nunca se comprometerá a menos que se involucre ZT.
Las instancias pueden llamarse entre sí y, como ya se mencionó, no hay un punto de entrada común que llame a todas las instancias desde un solo punto de entrada (como una capa de servicio) que haría un objetivo fácil para la etiqueta transaccional de primavera.
Ahora la pregunta: ¿se puede resolver este problema usando aspectos? Si es así, ¿cuál podría ser el enfoque básico? Gracias.
Con las transacciones de primavera y aop puedes hacerlo, pero será un poco "hack" ...
Tendrá que poner el inicio de la transacción en todos los puntos de entrada; solo puede comprometer desde el momento en que comenzó la transacción, y necesitará un segundo aspecto dentro de este para controlar si desea realizar la transacción o no.
Ahora, la única forma de decirle a la primavera que haga retroceder una transacción es lanzar una excepción a través del límite de la transacción. Por lo tanto, lo que necesitarás hacer si ingresas en esa área Z que provocará la confirmación, entonces necesitarás poner algo en el hilo local (también posiblemente a través de un aspecto) que ese aspecto "interno" encontrará y por lo tanto no arrojará la excepción para deshacer la transacción. Si no ingresas Z, el hilo local no obtendrá la bandera y cuando vuelvas al aspecto interno se lanzará una excepción para deshacer la transacción. Probablemente tendrás que tragarte esa excepción.
La expresión de Spring recomendaría tener una interfaz de servicio que conozca las unidades de trabajo y una interfaz de persistencia que trate con bases de datos relacionales. Los métodos en la interfaz del servicio deben corresponderse estrechamente con sus casos de uso. La implementación del servicio conoce todos los paquetes y clases de modelos y persistencia que necesita para lograr los objetivos del caso de uso.
"Las instancias pueden llamarse entre sí y, como ya se mencionó, no hay un punto de entrada común que llame a todas las instancias desde un único punto de entrada (como una capa de servicio) que sería un objetivo fácil para la etiqueta transaccional de primavera".
Esta oración me dice que estás haciendo las cosas de una manera que no se presta tan fácilmente al lenguaje de Spring. Es difícil decir exactamente lo que quiere, pero parece que está dejando de lado dos de las capas más importantes que Spring recomienda. Si parece difícil ir contra la corriente, tal vez sea su diseño el que necesite una nueva versión.
"... diferentes personas que llaman con otras clases en el medio ..." - tal vez necesites declarar transacciones individualmente con estas personas.
Puede declarar transacciones en configuración XML usando aspectos, ya sea con Spring AOP o AspectJ. Spring 2.5 y versiones superiores ahora le dan la opción de usar anotaciones si las prefiere a la configuración XML.
Tu descripción es terriblemente confusa para mí. Quizás esa sea en parte la razón por la que también estás teniendo dificultades. Lo reconsideraría o aclararía.
No necesita un solo punto de entrada, pero sí necesita la capacidad de aplicar el interceptor transaccional a todos los puntos de entrada para que las nuevas personas puedan participar en la misma transacción. Asumiendo que puede hacer eso, puede lograr esto con un indicador ThreadLocal y una implementación personalizada de org.springframework.transaction.support.TransactionSynchronization
.
Modificaría ZT para establecer el indicador ThreadLocal cuando un compromiso sea seguro para continuar. En su implementación TransactionSynchronization.beforeCommit()
, que se invoca desde PlatformTransactionManager
, puede verificar el indicador y usarlo para determinar si permite que la confirmación continúe. Puede forzar una reversión lanzando una RuntimeException
si la bandera no está presente.
Una advertencia sería que si tiene otros tipos de transacciones (que no involucran las 3 clases de coordinación que ha descrito), deberá asegurarse de que no se vuelvan a ejecutar inadvertidamente. Para hacer esto, puede marcar esta "transacción especial" en A.Foo, B.Bar y ZT a través de otra bandera ThreadLocal, luego verifique esa bandera en una cláusula guard en el método beforeCommit()
mencionado anteriormente. Pseudocódigo:
void beforeCommit() {
if in special transaction
if commit flag not set
throw new RuntimeException("cancel transaction")
end if
end if
end
Y, obviamente, esto es un truco y no recomendaría hacerlo en un sistema totalmente nuevo :).