web-dev-qa-db-fra.com

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?

39
Anand

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)

50
Brian Agnew

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é.

27
Konstantin Zyubin

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.

5
pavan

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();
2
Rashmingadhvi

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

2
Raghuram

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 ().

0
Arjun Patil

Ce que vous voulez réaliser n'est pas possible. Une explication se trouve dans Documentation de référence Spring .

0
Uwe Plonus

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() {

    }

}
0
Fai

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 

0
shiva kumar

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.

0
Qianyue