2012-07-16 16 views
6

Estamos utilizando el TransactionInterceptor de Spring para establecer cierta información de partición de base de datos usando ThreadLocal siempre que se ejecute un método DAO marcado con la anotación @Transactional. Necesitamos esto para poder enrutar nuestras consultas a diferentes particiones de base de datos.¿Cómo puede un Spring Bean detectar si se ha envuelto en un proxy AOP?

Esto funciona bien para la mayoría de los métodos de DAO:

// this causes the invoke method to set a thread-local with the host name of 
// the database server the partition is on 
@Transactional 
public int deleteAll() throws LocalDataException { 

El problema es cuando tenemos que hacer referencia a la DAO proxy de objeto en sí mismo dentro de la DAO. Por lo general tenemos que tener la persona que llama pase en el proxy-DAO:

public Pager<Foo, Long> getPager(FooDao proxyDao) { 

Esto se parece a la siguiente en el código que es obviamente bruto.

fooDao.getPager(fooDao); 

El problema es que cuando estamos dentro de FooDao, la this es no la DAO proxy que necesitamos.

¿Hay un mejor mecanismo para que un frijol descubra que tiene un envoltorio de proxy a su alrededor? Miré Spring AOPUtils pero no veo forma de encontrar el proxy para un objeto. No quiero isAopProxy(...) por ejemplo. También he leído el Spring AOP docs pero no veo una solución allí a menos que implemente mi propio código nativo AOP que esperaba evitar.

Sospecho que podría inyectar el DAO en él con un ApplicationContextAware y un método setProxyDao(...), pero eso también parece un truco. ¿Alguna otra idea de cómo puedo detectar el proxy para poder usarlo desde dentro del mismo bean? Gracias por cualquier ayuda.

+0

¿El uso de Aspectj original carga/tiempo de compilación no es una opción en absoluto, entonces el consejo se integrará en el proxy y no debería tener un problema de proxy y esta referencia dentro del proxy? –

+0

'this' no hará @ Thorbjørn porque como dice la publicación, necesito el proxy _no_ el bean en sí. – Gray

+0

Escribir mi propio AOP nativo puede ser mi única solución @Biju. Esperaba evitarlo si puedo. Gracias también. – Gray

Respuesta

4

Una solución hacky lo largo de las líneas de lo que usted ha sugerido, teniendo en cuenta que AspectJ tiempo de compilación o tejer el tiempo de carga no va a funcionar para usted:

crear una interfaz a lo largo de estas líneas:

public interface ProxyAware<T> { 
    void setProxy(T proxy); 
} 

Let el Dao de poner en práctica la implementación ProxyAware, ahora crear un BeanPostProcessor con una interfaz mandado correr último, a lo largo de estas líneas:

public class ProxyInjectingBeanPostProcessor implements BeanPostProcessor, Ordered { 
    @Override 
    public Object postProcessBeforeInitialization(Object bean, String beanName) { 
     return bean; 
    } 

    @Override 
    public Object postProcessAfterInitialization(Object bean, String beanName) { 
     if (AopUtils.isAopProxy((bean))){ 
      try { 
       Object target = ((Advised)bean).getTargetSource().getTarget(); 
       if (target instanceof ProxyAware){ 
        ((ProxyAware) target).setProxy(bean); 
       } 
      } catch (Exception e) { 
       // ignore 
      } 
     } 
     return bean; 
    } 

    @Override 
    public int getOrder() { 
     return Ordered.LOWEST_PRECEDENCE; 
    } 
} 

Se es feo, pero funciona

+0

Ooooh. Sabroso. Me gusta cómo se ve @Biju. Déjame probarlo ... – Gray

+0

Terminé eliminando el 'Ordered' porque parecía tener un efecto adverso en mi AOP por alguna razón. Pero de lo contrario, está funcionando. Tal vez deberías eliminar el 'Ordered'? Gracias de nuevo. – Gray

2

Hay una práctica utilidad estática AopContext.currentProxy() método proporcionado por Spring que devuelve un proxy al objeto desde el que se llamó.

Aunque usarlo se considera una mala práctica, semánticamente existe el mismo método en Java EE: SessionContext.getBusinessObject().

Escribí algunos artículos sobre este método de utilidad y varias trampas: 1, 2, 3.

+0

Empecé a decir que no estaba en un proxy cuando realizo la llamada, por lo que no hay un proxy actual. Pero no hay ninguna razón por la cual no podría marcar el método 'getPager()' como '@ Transactional', en cuyo caso estaría. Entonces esto es útil @Tomasz. ¡Gracias! – Gray

2

Use Spring para inyectar una referencia de bean en el bean, incluso el mismo bean, tal como lo haría con cualquier otra referencia de bean. No se requiere acción especial.

La presencia de dicha variable explícitamente reconoce en el diseño de clase que la clase espera ser proxiada de alguna manera.Esto no es necesariamente algo malo, ya que aop puede cambiar el comportamiento que rompe el contrato de clase.

La referencia de bean sería típicamente para una interfaz, y esa interfaz podría ser incluso una diferente para los métodos internos con referencia propia.

Hazlo simple. De esa manera yace la locura. :-)

Más importante aún, asegúrese de que la semántica tenga sentido. La necesidad de hacer esto puede ser un olor a código que la clase está mezclando en múltiples responsabilidades que se descomponen mejor en frijoles separados.

+0

Gracias Kent. Esperaba no tener que hacer esa inyección en todos mis DAO. El 'BeanPostProcessor' parece funcionar, pero lo tendré en cuenta. – Gray

Cuestiones relacionadas