2012-07-16 4 views
6

Wir verwenden Spring TransactionInterceptor einige Datenbankpartitionsinformationen mit ThreadLocal zu setzen, wenn eine DAO-Methode mit der @Transactional Annotation markierte ausgeführt wird. Wir benötigen dies, um unsere Abfragen an verschiedene Datenbankpartitionen weiterleiten zu können.Wie kann eine Spring-Bean feststellen, ob sie selbst in einen AOP-Proxy eingebettet wurde?

Dies funktioniert für die meisten DAO-Methoden:

// 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 { 

Das Problem ist, wenn wir die DAO Proxy Objekt selbst innerhalb des DAO verweisen müssen. Normalerweise müssen wir die Anrufer in dem Proxy-dao passieren müssen:

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

Dies sieht aus wie die in folgenden Code, die offensichtlich grob ist.

fooDao.getPager(fooDao); 

Das Problem ist, dass, wenn wir innerhalb von FooDao sind, die this ist nicht der Proxy-DAO, die wir brauchen.

Gibt es einen besseren Mechanismus für eine Bohne zu entdecken, dass es sich um einen Proxy-Wrapper hat? Ich habe an den Spring AOPUtils aussehen, aber ich sehe keine Möglichkeit, den Proxy für ein Objekt zu finden. Ich möchte nicht isAopProxy(...) zum Beispiel. Ich habe auch gelesen, die Spring AOP docs aber ich kann nicht eine Lösung dort sehen, wenn ich meinen eigenen AOP nativen Code implementieren, die ich hatte gehofft, zu vermeiden.

Ich vermute, dass ich in der Lage sein könnte, die DAO in sich selbst mit einer ApplicationContextAware-Anwendung Bean und eine setProxyDao(...)-Methode zu injizieren, aber das scheint wie ein Hack auch. Irgendwelche anderen Ideen, wie ich den Proxy erkennen kann, damit ich ihn innerhalb der Bohne selbst verwenden kann? Danke für jede Hilfe.

+0

Ist native Aspectj Lade/Kompilierzeit Weben überhaupt keine Option - dann wird der Rat in den Proxy weben und Sie sollten keine Frage von Proxy und dieser Referenz innerhalb des Proxy haben? –

+0

'This' wird @ Thorbjørn nicht tun, weil wie die Post Staaten, ich brauche den Proxy _not_ die Bohne selbst. – Gray

+0

Schreiben meiner eigenen AOP kann meine einzige Lösung @Biju sein. Ich hatte gehofft, es zu vermeiden, wenn ich kann. Danke tho. – Gray

Antwort

4

A hacky Lösung entlang der Linien von dem, was Sie vorgeschlagen haben, wenn man bedenkt, dass AspectJ Zeit oder die Ladezeit Weben kompilieren wird für Sie nicht:

entlang dieser Linien eine Schnittstelle erstellen:

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

Let Ihre Dao die ProxyAware Implementierung umzusetzen, hat jetzt eine BeanPostProcessor mit einer geordneten Schnittstelle erstellen zuletzt in dieser Richtung laufen:

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

Es ist hässlich, aber funktioniert.

+0

Ooooh. Lecker. Ich mag das Aussehen von @Biju. Lass es mich ausprobieren ... – Gray

+0

Ich habe das 'Ordered' entfernt, weil es aus irgendeinem Grund einen negativen Einfluss auf mein AOP hatte. Aber ansonsten funktioniert es. Vielleicht solltest du das 'Ordered' löschen? Danke noch einmal. – Gray

2

Es ist ein praktisches Dienstprogramm statischen AopContext.currentProxy() Verfahren von der Feder, die einen Proxy zurück zum Objekt, von dem es hieß.

Obwohl es mit einer schlechten Praxis betrachtet wird, semantisch die gleiche Methode existiert auch in Java EE: SessionContext.getBusinessObject().

Ich schrieb einige Artikel über diese Hilfsmethode und verschiedene Fallstricke: 1, 2, 3.

+0

Ich begann zu sagen, dass ich nicht in einem Proxy war, wenn ich den Anruf mache, so dass es keinen aktuellen Proxy gibt. Aber es gibt keinen Grund, warum ich die 'getPager()' Methode nicht als '@ Transactional' bezeichnen könnte, in welchem ​​Fall ich es wäre. Das ist also hilfreich @Tomasz. Vielen Dank! – Gray

2

Verwenden Sie Spring, um eine Bean-Referenz in die Bean zu injizieren, sogar die gleiche Bean, genau wie für jede andere Bean-Referenz. Keine spezielle Aktion erforderlich.

Das Vorhandensein einer solchen Variable bestätigt im Klassenentwurf explizit, dass die Klasse erwartet, in irgendeiner Weise proxiediert zu werden.Dies ist nicht unbedingt eine schlechte Sache, da aop Verhalten ändern kann, das den Klassenvertrag bricht.

Die Bean-Referenz würde normalerweise für eine Schnittstelle gelten, und diese Schnittstelle könnte sogar eine andere für die selbstreferenzierten internen Methoden sein.

Halten Sie es einfach. So liegt der Wahnsinn. :-)

Noch wichtiger, stellen Sie sicher, dass die Semantik sinnvoll ist. Die Notwendigkeit, dies zu tun, kann ein Code-Geruch sein, den die Klasse in mehrere Verantwortlichkeiten mischt, die am besten in separate Beans zerlegt werden.

+0

Danke Kent. Ich hatte gehofft, dass ich diese Injektion nicht in alle meine DAOs machen musste. Der 'BeanPostProcessor' scheint zu funktionieren, aber ich werde es mir merken. – Gray

Verwandte Themen