AOP Spring ne fonctionne pas pour l'appel de méthode dans une autre méthode
Il existe deux méthodes définies dans ABC.Java
public void method1(){
.........
method2();
...........
}
public void method2(){
...............
...............
}
Je souhaite avoir AOP lors de l'appel de method2 . Ainsi, J'ai créé une classe, AOPLogger.Java , la fonctionnalité d'aspect étant fournie dans une méthode checkAccess
Dans le fichier de configuration, j'ai fait quelque chose comme ci-dessous
<bean id="advice" class="p.AOPLogger" />
<aop:config>
<aop:pointcut id="abc" expression="execution(*p.ABC.method2(..))" />
<aop:aspect id="service" ref="advice">
<aop:before pointcut-ref="abc" method="checkAccess" />
</aop:aspect>
</aop:config>
Mais lorsque ma méthode2 est appelée, la fonctionnalité AOP n'est pas appelée, c'est-à-dire/ checkAccess la méthode n'est pas appelée par la classe AOPLogger.
Quelque chose me manque?
L'aspect est appliqué à un proxy entourant le bean. Notez que chaque fois que vous obtenez une référence à un bean, ce n'est pas réellement la classe référencée dans votre configuration, mais une classe synthétique implémentant les interfaces pertinentes, déléguant à la classe réelle et ajoutant des fonctionnalités, telles que votre AOP.
Dans l'exemple ci-dessus, vous appelez directement sur la classe, alors que si cette instance de classe est injectée dans un autre bean Spring, elle est injectée en tant que proxy et, par conséquent, les appels de méthode sont invoqués sur le proxy (et les aspects seront déclenchés)
Si vous souhaitez atteindre les objectifs ci-dessus, vous pouvez scinder le method1
/method2
en beans séparés ou utiliser un cadre AOP non orienté vers le ressort.
Le Spring doc détaille ceci et quelques solutions de contournement (y compris ma première suggestion ci-dessus)
Cela peut être fait par auto-utilisation. Vous pouvez appeler une méthode interne via une instance injectée:
@Component
public class Foo {
@Resource
private Foo foo;
public void method1(){
..
foo.method2();
..
}
public void method2(){
..
}
}
Depuis le printemps 4.3, vous pouvez également le faire avec @Autowired.
À partir de la version 4.3, @Autowired considère également les références auto-injectées, c'est-à-dire renvoie au haricot qui est actuellement injecté.
J'ai eu le même genre de problème et j'ai surmonté en implémentant la variable ApplicationContextAware
, BeanNameAware
et les méthodes correspondantes décrites ci-dessous.
class ABC implements ApplicationContextAware,BeanNameAware{
@Override
public void setApplicationContext(ApplicationContext ac) throws BeansException {
applicationContext=ac;
}
@Override
public void setBeanName(String beanName) {
this.beanName=beanName;
}
private ApplicationContext applicationContext;
private String beanName;
}
puis j'ai remplacé this.
par ((ABC) applicationContext.getBean(beanName)).
tout en appelant les méthodes de la même classe. Cela garantit que les appels aux méthodes de la même classe se font uniquement via le proxy.
Donc method1()
change en
public void method1(){
.........
((ABC) applicationContext.getBean(beanName)).method2();
...........
}
J'espère que cela t'aides.
En utilisant @Autowired
cela fonctionne . Au lieu d'appeler la méthode interne comme this.method()
, vous pouvez faire:
@Autowired
Foo foo;
puis en appelant:
foo.method2();
Le framework Spring AOP est "proxy" et s’explique très bien ici: http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/aop.html#aop-understanding-aop-proxies
Lorsque Spring construit un bean configuré avec un aspect (comme "ABC" dans votre exemple), il crée en fait un objet "proxy" qui agit comme le véritable bean. Le mandataire délègue simplement les appels à l'objet "réel", mais en créant cette indirection, le mandataire a la possibilité de mettre en oeuvre le "conseil". Par exemple, votre conseil peut consigner un message pour chaque appel de méthode. Dans ce schéma, si la méthode de l'objet réel ("method1") appelle d'autres méthodes dans le même objet (par exemple, method2), ces appels se déroulent sans proxy dans l'image, de sorte qu'il n'a aucune chance de mettre en œuvre un conseil.
Dans votre exemple, lorsque method1 () est appelé, le proxy aura la possibilité de faire ce qu'il est censé faire, mais si method1 () appelle method2 (), il n'y a pas d'aspect dans l'image. Quoi qu'il en soit, si method2 est appelé depuis un autre haricot, le proxy pourra exécuter le conseil.
J'espère que cela t'aides.
Merci, Raghu
Je suis surpris que personne ne l’ait mentionné, mais je pense que nous pouvons utiliser ControlFlowPointcut fourni par Spring.
ControlFlowPointcut examine stacktrace et ne fait correspondre le pointcut que s'il trouve une méthode particulière dans stacktrace. essentiellement, pointcut n'est mis en correspondance que lorsqu'une méthode est appelée dans un contexte particulier.
Dans ce cas, nous pouvons créer un cutcut tel que
ControlFlowPointcut cf = new ControlFlowPointcut(MyClass.class, "method1");
maintenant, en utilisant ProxyFactory, créez un proxy sur l’instance MyClass et appelez method1 ().
Dans le cas ci-dessus, seul method2 () sera conseillé puisqu'il est appelé depuis method1 ().
Ce que vous voulez réaliser n'est pas possible. Une explication se trouve dans Documentation de référence Spring .
Vous pouvez effectuer l'auto-injection de cette manière afin que cette classe puisse être utilisée en dehors de l'application Spring.
@Component
public class ABC {
@Resource
private ABC self = this;
public void method1() {
self.method2();
}
public void method2() {
}
}
Annotez les appels avec @EnableAspectJAutoProxy (exposeProxy = true) et appelez les méthodes d'instance avec ((Class) AopContext.currentProxy ()). Method ();
Ceci est strictement déconseillé car cela augmente le couplage
Comme indiqué dans Spring docs , chapitre 5.6.1 Comprendre les proxys AOP, il existe un autre moyen de procéder:
public class SimplePojo implements Pojo {
public void foo() {
// this works, but... gah!
((Pojo) AopContext.currentProxy()).bar();
}
public void bar() {
// some logic...
}
}
Bien que l'auteur ne recommande pas cette façon. Parce que:
Cela associe totalement votre code à Spring AOP et permet à la classe de prendre conscience du fait qu'elle est utilisée dans un contexte AOP, ce qui va à l'encontre de AOP. Il nécessite également une configuration supplémentaire lors de la création du proxy.