Aspect does not work with Spring boot application with external jar
Asked Answered
G

9

20

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.

Giovannagiovanni answered 14/8, 2016 at 14:20 Comment(7)
Do you also have spring-aspects dependency?Rebekahrebekkah
I tried adding it and it didn't work as well :/Giovannagiovanni
Why are you using reflection to get the annotation? You can simply add it to the parameters of the around advice method and access it directly.Aerator
@kriegaex - I actually created this kind of code in the past many times and it worked. The main difference is the use of spring boot which I mentioned :/Giovannagiovanni
Is timerPointcut() method gets invoked ?Notional
Hi @Avi! How did you fix this problem? I just have the same issue in my project. Thank you!Whig
@Whig - Unfortunately, I didn't :(Giovannagiovanni
T
15

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

Traject answered 14/8, 2016 at 14:39 Comment(3)
github.com/spring-projects/spring-boot/tree/master/… official example of how to use AspectJ in spring bootTraject
Thanks for your answer Albert. It's not the @Component issue though. I tried for the gist of it but I create the bean in MetricsConfiguration anyway. It's not the issue. I suspect that's because the aspect and the bean are in an external jar and I'm using spring boot.Giovannagiovanni
adding @Component annotation solves the problem and it does makes sense as well cuz the aspect might need to be injected at runtime, any thoughts?Inhalator
A
5

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();
    }
}
Aldric answered 16/8, 2016 at 22:26 Comment(3)
Thanks for your thorough answer. I still couldn't find no difference between my code and yours. My guess is that it's related to the fact that spring loads a @Configuration class (actually more than one) automagically and something there gets lost. I will keep digging.Giovannagiovanni
@Giovannagiovanni could you show how do you import MetricsConfiguration in your application? Is it differs from my example?Aldric
@Giovannagiovanni well... it seems the same for me :) can you reproduce your problem with small amount of code and post it go github?Aldric
H
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

Heavensent answered 26/6, 2018 at 16:10 Comment(0)
G
2
  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
Greece answered 10/1, 2019 at 8:31 Comment(0)
H
0

Add this componentScan to resolve the issue.

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

}
Homeomorphism answered 29/7, 2019 at 11:15 Comment(0)
L
0

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

Lemar answered 21/4, 2020 at 8:56 Comment(0)
H
0

Adding

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

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

Hartwell answered 3/3, 2022 at 17:31 Comment(0)
C
0

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

Caudad answered 16/3, 2022 at 3:34 Comment(0)
C
-2

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>
Conciliator answered 19/9, 2019 at 8:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.