2011-11-07 18 views
13

Como saben muchos cuando utiliza un objeto proxy, como cuando crea un bean con atributos transaccionales para Spring/EJB o incluso cuando crea un simulacro parcial con algunos frameworks, el objeto proxies no lo sabe, y las llamadas internas son no redirigida, y luego no sea interceptado ...¿Cómo manejar llamadas internas en Spring/EJB/Mockito ... proxies?

es por eso que si lo hace algo así en primavera:

@Transactionnal 
public void doSomething() { 
    doSomethingInNewTransaction(); 
    doSomethingInNewTransaction(); 
    doSomethingInNewTransaction(); 
} 

@Transactional(propagation = Propagation.REQUIRES_NEW) 
public void doSomethingInNewTransaction() { 
    ... 
} 

Cuando se llama a doSomething, que espera tener 3 nuevas transacciones, además de la principal uno, pero en realidad, debido a este problema, solo obtienes uno ...


así que me pregunto ¿cómo se hace para manejar este tipo de problemas ...

estoy realmente en una situación en la que tengo que manejar un sistema transaccional compleja, y no veo ninguna manera mejor que dividiendo mi servicio en muchos servicios pequeños, por lo que estoy seguro de pasar por todos los servidores proxy ...

Eso me molesta mucho porque todo el código pertenece al mismo dominio funcional y no debe dividirse ...

He encontrado esta pregunta relacionada con las respuestas interesantes: Spring - @Transactional - What happens in background?

Rob H dice que podemos inyectar el proxy de primavera dentro del servicio y llamar a proxy.doSomethingInNewTransaction(); en lugar. Es bastante fácil de hacer y funciona, pero no me gusta mucho ...

Yunfeng Hou dice esto:

Así que escribir mi propia versión de CglibSubclassingInstantiationStrategy y proxy de modo que el creador utilizará CGLIB para generar una subclase real que los delegados llaman a su super en lugar de otra instancia, que Spring está haciendo ahora. Así que puedo anotar libremente sobre cualquier método (tan largo como ya que no es privado), y desde cualquier lugar que llame a estos métodos, se ocuparán de . Bueno, todavía tengo que pagar el precio: 1. Debo enumerar todas las anotaciones que deseo habilitar para la creación de la nueva clase CGLIB . 2. No puedo hacer anotaciones sobre un método final ya que ahora estoy generando la subclase, por lo que no se puede interceptar un método final.

¿Qué quiere decir con "which spring is doing now"? ¿Esto significa que las llamadas transaccionales internas ahora se interceptan?


¿Qué crees que es mejor?

¿Divide sus clases cuando necesita cierta granularidad transaccional? ¿O usa alguna solución como la de arriba? (por favor, compártelo)

+0

¿no hay más ideas? por favor, estoy seguro de que ya vio este problema. –

Respuesta

15

Hablaré de Spring y @Transactional pero el consejo se aplica a muchos otros frameworks también.

Este es un problema inherente a los aspectos basados ​​en proxy. Se discute en la documentación de la primavera aquí:

http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-understanding-aop-proxies

Hay una serie de posibles soluciones.

Refacte sus clases para evitar las llamadas de invocación automática que omiten el proxy.

La documentación de Spring describe esto como "El mejor enfoque (el término mejor se utiliza sin apretar aquí)".

Las ventajas de este enfoque son su simplicidad y que no existen vínculos con ningún marco. Sin embargo, puede no ser apropiado para una base de código pesado muy transaccional, ya que terminarías con muchas clases trivialmente pequeñas.

Internamente en la clase obtener una referencia al proxy.

Esto se puede hacer mediante la inyección del proxy o con la llamada codificada "AopContext.currentProxy()" (consulte Spring docs más arriba).

Este método le permite evitar dividir las clases, pero de muchas maneras anula las ventajas de usar la anotación transaccional. Mi opinión personal es que esta es una de esas cosas que es un poco fea, pero la fealdad es independiente y podría ser el enfoque pragmático si se usan muchas transacciones.

cambiar al uso de AspectJ

Como AspectJ no utiliza proxies entonces auto-invocación no es un problema

Este es un método muy limpia - es a expensas de la introducción de otro marco. He trabajado en un gran proyecto donde AspectJ fue presentado por esta misma razón.

No utilice @Transactional en absoluto

Refactor el código para utilizar la demarcación de transacciones manuales - posiblemente utilizando el patrón decorador.

una opción - pero que requiere refactorización moderada, la introducción de los lazos estructurales adicionales y una mayor complejidad - por lo que probablemente no es una opción preferida

mi consejo

Por lo general, dividiendo el código es la mejor respuesta y también puede ser bueno para la separación de preocupaciones también. Sin embargo, si tuviera un framework/aplicación que dependiera en gran medida de las transacciones anidadas, consideraría usar AspectJ para permitir la auto invocación.

+0

gracias la mejor respuesta hasta el momento. Así que creo que usaré AopContext.currentProxy() solo en algunos casos muy específicos ya que no estoy en una aplicación transaccional pesada –

3

Normalmente lo hago simple, así que dividí el código en dos objetos.

La alternativa es demarcar la nueva transacción usted mismo, si necesita guardar todo en el mismo archivo, usando un TransactionTemplate. Algunas líneas más de código, pero no más que definir un nuevo bean. Y a veces hace que el punto sea más obvio.

4

Como siempre al modelar y diseñar casos de uso complejos: concéntrese en un diseño y un código comprensibles y fáciles de mantener. Si prefiere un patrón o diseño determinado pero choca con el marco subyacente, considere si vale la pena una solución compleja para calzar su diseño en el marco, o si debe comprometer y adaptar su diseño al marco cuando sea necesario. No luches contra el marco a menos que sea absolutamente necesario.

Mi consejo: si puede lograr su objetivo con un compromiso tan fácil como dividirse en unas pocas clases de servicio adicionales, hágalo. Suena mucho más barato en términos de tiempo, pruebas y agonía que la alternativa. Y seguro que es mucho más fácil de mantener y menos dolor de cabeza para el siguiente tipo que se haga cargo.

+0

En realidad, inyectar el proxy en la clase proxy me parece bastante fácil y garantiza que sus transacciones funcionen bien para las llamadas internas, ¿no cree que es una solución fácil? –

+0

@SebastienLorber - Creo que es un compromiso perfecto (lo he hecho varias veces para evitar este tipo de restricciones en Spring AOP) siempre y cuando lo documente en javadoc/comentarios para que el próximo tipo entienda lo que están haciendo y por qué. – pap

+0

eso es verdad. el problema es bien conocido, creo, pero para los recién llegados, javadoc es bienvenido ... aún me pregunto qué significa "qué primavera está haciendo ahora". Este problema quizás ya está resuelto? –