¿Quiero saber qué sucede realmente cuando anotas un método con @Transactional
? Por supuesto, sé que Spring envolverá ese método en una Transacción.
Pero tengo las siguientes dudas:
- ¿Escuché que Spring crea una clase 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 los documentos de Spring que:
Nota: Dado que este mecanismo se basa en proxies, solo se interceptarán las llamadas de método 'externas' que ingresen a través del proxy . Esto significa que 'auto-invocació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 a los métodos de autoinvocación?
Respuestas:
Este es un gran tema. El documento de referencia de Spring le dedica varios capítulos. Recomiendo leer los sobre programación y transacciones orientadas a aspectos , ya que el soporte de transacciones declarativas de Spring utiliza AOP en su base.
Pero a un nivel muy alto, Spring crea proxies para las clases que declaran @Transactional en la clase misma o en los miembros. El proxy es principalmente invisible en tiempo de ejecución. Proporciona una forma para que Spring inyecte comportamientos antes, después o alrededor de las llamadas al método en el objeto que se está representando. La administración de transacciones es solo un ejemplo de los comportamientos que se pueden enganchar. Las verificaciones de seguridad son otro. Y también puede proporcionar el suyo propio para cosas como el registro. Entonces, cuando anota un método con @Transactional , Spring crea dinámicamente un proxy que implementa las mismas interfaces que la clase que está anotando. Y cuando los clientes hacen llamadas a su objeto, las llamadas se interceptan y los comportamientos se inyectan a través del mecanismo proxy.
Las transacciones en EJB funcionan de manera similar, por cierto.
Como observó, a través del mecanismo proxy solo funciona cuando entran llamadas de algún objeto externo. Cuando realiza una llamada interna dentro del objeto, realmente está haciendo una llamada a través de la referencia " this ", 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 un BeanFactoryPostProcessor para inyectar una instancia del proxy en clases de "autorreferencia" en tiempo de ejecución. Guardo esta referencia en una variable miembro llamada " yo ". Luego, si necesito hacer llamadas internas que requieren un cambio en el estado de la transacción del hilo, dirijo la llamada a través del proxy (por ejemplo, " me.someMethod ()".) La publicación del foro explica con más detalle. Tenga en cuenta que el código BeanFactoryPostProcessor sería un poco diferente ahora, ya que fue escrito en el marco temporal de Spring 1.x. Pero espero que le dé una idea. Tengo una versión actualizada que Probablemente podría poner a disposición.
fuente
BeanFactoryPostProcessor
. Sin embargo, hay un método (en mi opinión) muy similar descrito en esta respuesta: stackoverflow.com/a/11277899/3667003 ... y otras soluciones en todo el hilo también.Cuando Spring cargue sus definiciones de bean 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 "objetivo" (es decir, su bean).Sin embargo, los proxies también se pueden suministrar con interceptores, y cuando estén presentes, el proxy invocará estos interceptores antes de que invoque el método de su bean de destino. Para los beans de destino anotados con
@Transactional
, Spring creará unTransactionInterceptor
y lo pasará al objeto proxy generado. Entonces, cuando llama al método desde el código del cliente, está llamando al método en el objeto proxy, que primero invoca elTransactionInterceptor
(que comienza una transacción), que a su vez invoca el método en su bean de destino. Cuando finaliza la invocación, losTransactionInterceptor
commits / retrotraen la transacción. Es transparente para el código del cliente.En cuanto a la cuestión del "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 tiene conocimiento de ello. Solo las llamadas desde "fuera" de su bean pasan por el proxy.
¿Eso ayuda?
fuente
Como persona visual, me gusta considerar un diagrama de secuencia del patrón proxy. Si no sabes cómo leer las flechas, leí la primera de esta manera: se
Client
ejecutaProxy.method()
.(Se me permitió publicar la foto con la condición de que mencionara sus orígenes. Autor: Noel Vaes, sitio web: www.noelvaes.eu)
fuente
La respuesta más simple es:
En cualquier método que declare,
@Transactional
el límite de la transacción comienza y el límite finaliza cuando se completa el método.Si está utilizando una llamada JPA, todas las confirmaciones están dentro de este límite de transacción .
Digamos que está guardando entidad1, entidad2 y entidad3. Ahora, mientras se guarda la entidad3 , se produce una excepción y , a medida que enitiy1 y entity2 entran en la misma transacción, la entidad1 y la entidad2 se revertirán con la entidad3.
Transacción:
Cualquier excepción dará como resultado la reversión de todas las transacciones JPA con DB. Spring utiliza internamente las transacciones JPA.
fuente
Puede ser tarde, pero me encontré con algo que explica su preocupación relacionada con el proxy (solo se interceptarán las llamadas de método 'externas' que ingresen a través del proxy).
Por ejemplo, tienes una clase que se ve así
y tienes un aspecto que se ve así:
Cuando lo ejecutas así:
}
Resultados de llamar a kickOff arriba del código dado arriba.
pero cuando cambias tu código a
Verá, el método llama internamente a otro método para que no se intercepte y la salida se vería así:
Puedes evitar esto haciendo eso
Fragmentos de código tomados de: https://www.intertech.com/Blog/secrets-of-the-spring-aop-proxy/
fuente
Todas las respuestas existentes son correctas, pero creo que no puedo dar solo este tema complejo.
Para obtener una explicación completa y práctica, puede echar un vistazo a esta guía Spring @Transactional In-Depth , que hace todo lo posible para cubrir la gestión de transacciones en ~ 4000 palabras simples, con muchos ejemplos de código.
fuente