'Aspect does not work with Spring boot application with external jar

I am trying to create a timer aspect for measuring methods run time.

I created an annotation named @Timer:

@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD, ElementType.TYPE})
public @interface Timer {
    String value();
}

And then I created the aspect as follows:

@Aspect
public class MetricAspect {

    @Autowired
    private MetricsFactory metricsFactory;

    @Pointcut("@annotation(my.package.Timer)")
    public void timerPointcut() {}

    @Around("timerPointcut() ")
    public Object measure(ProceedingJoinPoint joinPoint) throws Throwable {
       /* Aspect logic here */
    }

    private Timer getClassAnnotation(MethodSignature methodSignature) {
        Timer annotation;
        Class<?> clazz = methodSignature.getDeclaringType();
        annotation = clazz.getAnnotation(Timer.class);
        return annotation;
    }

I have a configuration class as follows:

@Configuration
@EnableAspectJAutoProxy
public class MetricsConfiguration {

    @Bean
    public MetricAspect notifyAspect() {
        return new MetricAspect();
    }
}

Everything up until here is defined in a packaged jar which I use as a dependency in my spring boot application

In my spring boot application I import the MetricsConfiguration and I debugged the code and saw that the MetricAspect bean is created.

I use it in code as follows:

@Service
public class MyService {
    ...

    @Timer("mymetric")
    public void foo() {
       // Some code here...
    }

    ...
}

But my code doesn't reach to the measure method. Not sure what I'm missing.

For completing the picture, I have these dependencies in my pom file added:

<dependencies>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.7.4</version>
    </dependency>

    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.7.4</version>
    </dependency>
</dependencies>

That's the @Configuration class that imports MetricsConfiguration:

@Configuration
@EnableAspectJAutoProxy
@Import(MetricsConfiguration.class)
@PropertySource("classpath:application.properties")
public class ApplicationConfiguration {

}

It's loaded with Spring's automagically configuration loading.



Solution 1:[1]

can @Component or @Configurable solve your issue?

@Aspect
@Component
public class yourAspect {
 ...
}

Enable Spring AOP or AspectJ

EDIT:

I created a project to simulate your issue, seems no problem after all. Is it affected by other issue?

https://github.com/zerg000000/spring-aspectj-test

Solution 2:[2]

I was unable to reproduce your problem using aspectJ 1.8.8 and spring 4.2.5. Here is my maven multi-module approach with aspect in separate jar.

I modified your code slightly but did not change any annotations. The only thing that might be differ is that I've added org.springframework:spring-aop dependency and defined my entrypoint as follows:

@Import(MetricsConfiguration.class)
@SpringBootApplication
public class Application {
    // @Bean definitions here //

    public static void main(String[] args) throws InterruptedException {
        ApplicationContext ctx = 
            SpringApplication.run(Application.class, args);
        ctx.getBean(MyService.class).doWork();
    }
}

Solution 3:[3]

I had a similar problem where the aspect was built in a jar library, and the spring-boot application was else where. Turns out that the packages for the spring-boot application and the jar library were different. Due to which Spring was not looking into the package of the library to autowire into the application context.

So, had to include @ComponentScan({"base.package.application.*", "base.package.library.*"}) in the Application.java

Solution 4:[4]

  1. If the external jar is Spring boot starter, you can config Aspect bean in AutoConfiguration:

(1)

@Aspect
public class MyAspect {
  //....
}

(2)

package a.b.c

@Configuration
public class MyAutoConfiguration {
    @Bean
    MyAspect myAspect() {
        return new MyAspect();
    }   
}

(3)config in spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
a.b.c.MyAutoConfiguration
  1. If the external jar is not a Spring boot starter, just load Aspect as a bean, you can use @ComponentScan

Solution 5:[5]

Add this componentScan to resolve the issue.

@ComponentScan("package.of.aspect")
@Configuration
@EnableAspectJAutoProxy
@Import(MetricsConfiguration.class)
@PropertySource("classpath:application.properties")
public class ApplicationConfiguration {

}

Solution 6:[6]

Debugging spring-boot Aspectj aspects when pointcut itself has problems is not easy even with detailed logging: How to debug Spring AOP

Unfortunately, spring boot + spring-aop with annotations don't have many ways to debug aspects and why some classes, especially non-spring compoment jar classes, are not scanned, such as jar classes whose methods are in abstract classes or static final methods need the right pointcuts to work covering all classes/implementations even if they are component scanned.

The best way to debug an Asepct (or use core AOP and avoid spring-aop) is to enable aop.xml with configuration control using org/aspectj/aop.xml or META-INF/aop.xml, using the LTW aspectj weaver -Daj.weaving.verbose=true -javaagent:~/.m2/repository/org/aspectj/aspectjweaver/1.9.5/aspectjweaver-1.9.5.jar

To debug all aspects/classes with Debug/Verbose logs to see why some classes are not being scanned: ...

this almost always helps figuring out the problem with the pointcut or class not getting picked.

Or, just use LTW aop, see, https://github.com/dsyer/spring-boot-aspectj

Solution 7:[7]

Adding

ComponentScan(basePackages = "com.github.something.annotation")

basePackages is the package where your aspect resides. This solution work for me.

Solution 8:[8]

You need put @ComponentScan on MetricsConfiguration, as @Configuration will not automatically scan and load component. I have tested, and it worked!

Solution 9:[9]

according to mojohaus explaination, you have to add build settings like below to Woven your aspect into all classes implementing your aspect interface.

<build>
    <plugins>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>aspectj-maven-plugin</artifactId>
            <version>1.11</version>
            <configuration>
                <complianceLevel>1.8</complianceLevel>
                <includes>
                    <include>**/*.java</include>
                    <include>**/*.aj</include>
                </includes>
                <aspectDirectory>src/main/aspect</aspectDirectory>
                <testAspectDirectory>src/test/aspect</testAspectDirectory>
                <XaddSerialVersionUID>true</XaddSerialVersionUID>
                <showWeaveInfo>true</showWeaveInfo>
                <aspectLibraries>
                    <aspectLibrary>
                        <groupId>your aspect groupId</groupId>
                        <artifactId>your aspect artifactId</artifactId>
                    </aspectLibrary>
                </aspectLibraries>
            </configuration>
            <executions>
                <execution>
                    <id>compile_with_aspectj</id>
                    <goals>
                        <goal>compile</goal>
                    </goals>
                </execution>
                <execution>
                    <id>test-compile_with_aspectj</id>
                    <goals>
                        <goal>test-compile</goal>
                    </goals>
                </execution>
            </executions>
            <dependencies>
                <dependency>
                    <groupId>org.aspectj</groupId>
                    <artifactId>aspectjtools</artifactId>
                    <version>${aspectj.runtime.version}</version>
                </dependency>
                <dependency>
                    <groupId>your aspect groupId</groupId>
                    <artifactId>youar aspect artifactId</artifactId>
                    <version>1.0.0-SNAPSHOT</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Andrii Abramov
Solution 2
Solution 3 FMirza
Solution 4 ???
Solution 5 Asaf Maoz
Solution 6 Community
Solution 7 EdNight
Solution 8 procrastinator
Solution 9