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.

Avi
  • 21,182
  • 26
  • 82
  • 121

9 Answers9

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

Andrii Abramov
  • 10,019
  • 9
  • 74
  • 96
ka yu Lai
  • 571
  • 3
  • 13
  • https://github.com/spring-projects/spring-boot/tree/master/spring-boot-samples/spring-boot-sample-aop official example of how to use AspectJ in spring boot – ka yu Lai Aug 14 '16 at 18:39
  • 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. – Avi Aug 15 '16 at 18:43
  • 1
    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? – Aimal Khan Jun 03 '18 at 20:45
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();
    }
}
vsminkov
  • 10,912
  • 2
  • 38
  • 50
  • 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. – Avi Aug 17 '16 at 15:05
  • 1
    @Avi could you show how do you import `MetricsConfiguration` in your application? Is it differs from my example? – vsminkov Aug 17 '16 at 15:09
  • @Avi well... it seems the same for me :) can you reproduce your problem with small amount of code and post it go github? – vsminkov Aug 18 '16 at 21:32
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

FMirza
  • 31
  • 1
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
吴晓铭
  • 21
  • 4
0

Add this componentScan to resolve the issue.

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

}
Asaf Maoz
  • 675
  • 3
  • 6
  • 23
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

Community
  • 1
  • 1
kisna
  • 2,869
  • 1
  • 25
  • 30
0

Adding

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

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

EdNight
  • 21
  • 2
0

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

Procrastinator
  • 2,526
  • 30
  • 27
  • 36
cheng gen
  • 26
  • 2
-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>
Rujal Shrestha
  • 362
  • 2
  • 9