2012-07-16 7 views
6

Stiamo utilizzando Spring TransactionInterceptor per impostare alcune informazioni sulla partizione del database utilizzando ThreadLocal ogni volta che viene eseguito un metodo DAO contrassegnato con l'annotazione @Transactional. Abbiamo bisogno di questo per poter indirizzare le nostre query a diverse partizioni di database.Come può un bean Spring rilevare se esso stesso è stato avvolto in un proxy AOP?

Questo funziona bene per la maggior parte dei metodi 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 { 

Il problema è quando occorre fare riferimento DAO delega oggetto stesso all'interno della DAO. In genere dobbiamo avere passare il chiamante nel proxy-dao:

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

Questo è simile al seguente nel codice che è ovviamente lordo.

fooDao.getPager(fooDao); 

Il problema è che quando siamo all'interno della FooDao, il this è non DAO proxy che abbiamo bisogno.

C'è un meccanismo migliore per un bean per scoprire che ha un wrapper proxy attorno ad esso? Ho esaminato lo Spring AOPUtils ma non vedo alcun modo per trovare il proxy per un oggetto. Ad esempio, non voglio isAopProxy(...). Ho letto anche lo Spring AOP docs ma non riesco a vedere una soluzione lì a meno che non implementi il ​​mio codice nativo AOP che speravo di evitare.

Ho il sospetto che potrei essere in grado di iniettare il DAO in se stesso con un bean utility ApplicationContextAware e un metodo setProxyDao(...), ma anche quello sembra un hack. Qualche altra idea su come posso rilevare il proxy in modo da poterlo utilizzare dall'interno del bean stesso? Grazie per qualsiasi aiuto.

+0

L'utilizzo di Aspectj nativo carica/compila tempo tessere non un'opzione affatto - quindi il consiglio si intrometterà nel proxy e non si dovrebbe avere un problema di proxy e questo riferimento all'interno del proxy? –

+0

'this' non farà @ Thorbjørn perché, come afferma il post, ho bisogno del proxy _non_ il bean stesso. – Gray

+0

Scrivere il mio AOP nativo può essere la mia unica soluzione @Biju. Speravo di evitarlo se posso. Grazie mille. – Gray

risposta

4

Una soluzione hacky lungo le linee di quello che avete suggerito, se si considera che AspectJ tempo o di tessitura tempo di caricamento di compilazione non funzionerà per voi:

creare un'interfaccia in questo senso:

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

Let il vostro Dao di implementare l'attuazione ProxyAware, ora creare un BeanPostProcessor con un'interfaccia ordinato di correre scorso, in questo senso:

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; 
    } 
} 

E ' è brutto, ma funziona.

+0

Ooooh. Yummy. Mi piace l'aspetto di @Biju. Fammi provare ... – Gray

+0

Alla fine ho rimosso il comando "Ordinato" perché sembrava avere un effetto negativo sul mio AOP per qualche motivo. Ma per il resto funziona. Forse dovresti eliminare l'opzione "Ordinato"? Grazie ancora. – Gray

2

C'è un pratico programma di utilità statica AopContext.currentProxy() fornito da Spring che restituisce un proxy all'oggetto da cui è stato chiamato.

Anche se l'utilizzo è considerato una cattiva pratica, semanticamente lo stesso metodo esiste anche in Java EE: SessionContext.getBusinessObject().

Ho scritto alcuni articoli su questo metodo di utilità e varie insidie: 1, 2, 3.

+0

Ho iniziato a dire che non ero _in_ un proxy quando effettuo la chiamata in modo che non ci sia un proxy corrente. Ma non c'è motivo per cui non potrei contrassegnare il metodo 'getPager()' come '@ Transactional', nel qual caso lo sarei. Quindi questo è utile @Tomasz. Grazie! – Gray

2

Usa Spring per iniettare un riferimento bean nel bean, anche lo stesso bean, proprio come faresti per qualsiasi altro riferimento bean. Non è richiesta alcuna azione speciale.

La presenza di tale variabile riconosce esplicitamente nel progetto di classe che la classe si aspetta di essere sottoposta a proxy in qualche modo.Questo non è necessariamente un aspetto negativo, poiché un cambiamento può cambiare il comportamento che infrange il contratto di classe.

Il riferimento del bean in genere è per un'interfaccia e tale interfaccia potrebbe anche essere diversa per i metodi interni autoreferenziali.

Semplicità. In questo modo giace la follia. :-)

Ancora più importante, assicurarsi che la semantica abbia un senso. La necessità di fare questo può essere un odore di codice che la classe sta mescolando in più responsabilità meglio scomposto in fagioli separati.

+0

Grazie Kent. Speravo di non dover fare quell'iniezione in tutti i miei DAO. Il 'BeanPostProcessor' sembra funzionare, ma terrò questo a mente. – Gray