Primavera - @Transactional - ¿Qué sucede en segundo plano?
Quiero saber qué sucede realmente cuando anotas un método con @Transactional
. Por supuesto, sé que Spring incluirá ese método en una Transacción.
Pero tengo las siguientes dudas:
- ¿Escuché que Spring crea una clase de proxy ? ¿Alguien puede explicar esto con más profundidad ? ¿Qué reside realmente en esa clase de proxy? ¿Qué pasa con la clase real? ¿Y cómo puedo ver la clase proxy creada por Spring?
- También leí en documentos de primavera que:
Nota: Dado que este mecanismo se basa en servidores proxy, solo se interceptarán las llamadas a métodos "externos" que lleguen a través del proxy . Esto significa que la 'autoinvocación', es decir, un método dentro del objeto de destino que llama a algún otro método del objeto de destino, no conducirá a una transacción real en tiempo de ejecución incluso si el método invocado está marcado con
@Transactional
!
Fuente: http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html
¿Por qué solo las llamadas a métodos externos estarán en Transacción y no en los métodos de autoinvocación?
Este es un gran tema. El documento de referencia de Spring le dedica varios capítulos. Recomiendo leer los de Transacciones y programación orientada a aspectos , ya que el soporte de transacciones declarativas de Spring utiliza AOP como base.
Pero en un nivel muy alto, Spring crea proxies para clases que declaran @Transactional
sobre la clase misma o sobre los miembros. El proxy es prácticamente invisible en tiempo de ejecución. Proporciona una forma para que Spring inyecte comportamientos antes, después o alrededor de las llamadas a métodos en el objeto que se está representando. La gestión de transacciones es sólo un ejemplo de los comportamientos que se pueden incorporar. Los controles de seguridad son otro. Y también puedes proporcionar el tuyo propio para cosas como el registro. Entonces, cuando anotas un método con @Transactional
, Spring crea dinámicamente un proxy que implementa las mismas interfaces que la clase que estás anotando. Y cuando los clientes realizan llamadas a su objeto, las llamadas se interceptan y los comportamientos se inyectan a través del mecanismo de proxy.
Por cierto, las transacciones en EJB funcionan de manera similar.
Como observó, el mecanismo de proxy solo funciona cuando las llamadas provienen de algún objeto externo. Cuando realiza una llamada interna dentro del objeto, en realidad está realizando una llamada a través de la this
referencia, que omite el proxy. Sin embargo, hay formas de solucionar ese problema. Explico un enfoque en esta publicación del foro en el que uso a BeanFactoryPostProcessor
para inyectar una instancia del proxy en clases "autorreferenciadas" en tiempo de ejecución. Guardo esta referencia en una variable miembro llamada me
. Luego, si necesito realizar llamadas internas que requieren un cambio en el estado de la transacción del hilo, dirijo la llamada a través del proxy (p. ej me.someMethod()
.). La publicación del foro lo explica con más detalle.
Tenga en cuenta que el BeanFactoryPostProcessor
código sería un poco diferente ahora, ya que se escribió en el período de tiempo Spring 1.x. Pero espero que te dé una idea. Tengo una versión actualizada que probablemente podría poner a disposición.
Cuando Spring cargue sus definiciones de beans y se haya configurado para buscar @Transactional
anotaciones, creará estos objetos proxy alrededor de su bean real . Estos objetos proxy son instancias de clases que se generan automáticamente en tiempo de ejecución. El comportamiento predeterminado de estos objetos proxy cuando se invoca un método es simplemente invocar el mismo método en el bean "destino" (es decir, su bean).
Sin embargo, los servidores proxy también pueden contar con interceptores y, cuando estén presentes, el proxy invocará estos interceptores antes de invocar el método del bean objetivo. Para los beans de destino anotados con @Transactional
, Spring creará un TransactionInterceptor
y lo pasará al objeto proxy generado. Entonces, cuando llamas al método desde el código del cliente, estás llamando al método en el objeto proxy, que primero invoca TransactionInterceptor
(que comienza una transacción), que a su vez invoca el método en tu bean objetivo. Cuando finaliza la invocación, TransactionInterceptor
confirma/revierte la transacción. Es transparente para el código del cliente.
En cuanto al "método externo", si su bean invoca uno de sus propios métodos, entonces no lo hará a través del proxy. Recuerde, Spring envuelve su bean en el proxy, su bean no lo sabe. Sólo las llamadas desde "fuera" de su bean pasan por el proxy.
¿Eso ayuda?
Como persona visual, me gusta opinar con un diagrama de secuencia del patrón proxy. Si no sabes leer las flechas, leo la primera así: Client
ejecuta Proxy.method()
.
- El cliente llama a un método en el objetivo desde su perspectiva y es interceptado silenciosamente por el proxy.
- Si se define un aspecto anterior, el proxy lo ejecutará.
- Luego, se ejecuta el método real (objetivo).
- El retorno posterior y el lanzamiento posterior son aspectos opcionales que se ejecutan después de que el método regresa y/o si el método arroja una excepción.
- Después de eso, el proxy ejecuta el aspecto posterior (si está definido)
- Finalmente el proxy regresa al cliente que llama.
(Se me permitió publicar la foto con la condición de mencionar sus orígenes. Autor: Noel Vaes, sitio web: https://www.noelvaes.eu )