Почему рекомендации AspectJ @Around выполняются дважды?

#java #aop #aspectj

#java #aop #aspectj

Вопрос:

У меня есть следующий пример AspectJ, который я сделал в качестве доказательства концепции в стиле «hello world». Код рекомендации в the StyleAspect , похоже, выполняется дважды, хотя фактический код в SomeClass выполняется только один раз (по мере необходимости).

Вот код:

Во-первых, аннотация, вызываемая WithStyle:

 @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface WithStyle {  
}
 

Затем аспект, который перехватывает любой код с аннотацией @WithStyle

 @Aspect
public class StyleAspect {

    @Around("@annotation(WithStyle)")
    public Object doItWithStyle(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("Doing it in style...");
        Object result = pjp.proceed();
        System.out.println("Done");
        
        return resu<
    }
}
 

и, наконец, некоторый код с аннотацией

 public class SomeClass {
    
    @WithStyle
    public void doIt() {
        System.out.println("I'm doing it....");
    }
}
 

Когда я запускаю это, я получаю следующий вывод:

 --- exec-maven-plugin:1.2.1:exec (default-cli) @ AspectJTest ---
Doing it in style...
Doing it in style...
I'm doing it....
Done
Done
 

Таким образом, кажется, что, хотя сам код выполняется только один раз, код в аспекте выполняется дважды.

Вот вызывающий код:

 public class Main {
    
    public static void main(String[] args) {
        SomeClass someClass = new SomeClass();
        someClass.doIt();
    }
}
 

и для полноты картины я включаю pom в конфигурацию плагина AspectJ

 <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>ie.philb</groupId>
    <artifactId>AspectJTest</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <project.build.java.target>1.8</project.build.java.target>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.9.6</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.6</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.11</version>
                <configuration>
                    <complianceLevel>1.8</complianceLevel>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>       <!-- use this goal to weave all your main classes -->
                            <goal>test-compile</goal>  <!-- use this goal to weave all your test classes -->
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
      
</project>
 

Ответ №1:

Ваш around() совет перехватывает как call точки соединения, так и execution точки соединения метода, аннотированного с @WithStyle помощью (т.Е. doIt() ). Если вы добавите a System.out.println(pjp); в свой аспект:

 @Aspect
public class StyleAspect {

    @Around("@annotation(WithStyle) ") 
    public Object doItWithStyle(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println(pjp);
        System.out.println("Doing it in style...");
        Object resu<
        try{
            result = pjp.proceed();
        }
        finally{
            System.out.println("Done");
        }
        return resu<
    }
}
 

вы получите следующий вывод:

 call(public void SomeClass.doIt()) <----
Doing it in style...
execution(public void SomeClass.doIt()) <----
Doing it in style...
I'm doing it....
Done
Done
 

Вы можете ясно видеть, что точки соединения call и execution метод SomeClass.doIt() of перехватываются around советом doItWithStyle .

Исходя из перехвата call , around совет плетет код следующим образом:

 // around advice code  before the pjp.proceed();
someClass.doIt(); // during the pjp.proceed();
// around advice code  after the pjp.proceed();
 

следовательно:

  System.out.println("Doing it in style...");.
 someClass.doIt();
 System.out.println("Done");
 

Из выполнения:

 @WithStyle
public void doIt() {
    // around advice code  before the pjp.proceed();
    System.out.println("I'm doing it....");
  // around advice code  after the pjp.proceed();
}
 

следовательно:

 @WithStyle
public void doIt() {
    System.out.println("Doing it in style...");
    System.out.println("I'm doing it....");
    System.out.println("Done");
}
 

в результате чего вывод:

 Doing it in style... 
Doing it in style...
I'm doing it....
Done
Done
 

Теперь, если вы хотите избежать того, чтобы around совет перехватывал как call метод, так и execution метод doIt() . Вам необходимо дополнительно ограничить точки соединения, перехваченные вашим around советом. Чтобы просто перехватить метод call , вы можете сделать:

  @Around("@annotation(WithStyle) amp;amp; call(* *(..))") 
 

для метода execution :

 @Around("@annotation(WithStyle) amp;amp; execution(* *(..))") 
 

Вы можете дополнительно ограничить перехваченные точки соединения на основе количества аргументов метода, его возвращаемого типа, имени и так Далее, настроив сигнатуру call или execution pointcuts .