Reputation: 241
I want to be able to track methods that are anotated with @RequestMapping that are annotated with a certain annotation (for instance @LooseController).
I have two pointcuts: requestMappings() and looseController()
this works well if the method that is annotated with @RequestMapping is in a class that has @LooseController but not if the @LooseController is in a subclass
for instance, when update(id) is called on Controller1 it is not caught by this aspect
Update to include more information:
package de.scrum_master.app;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class MyAspect {
@Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
private static void requestMapping() {}
@Pointcut("@within(de.scrum_master.app.LooseController)")
private static void looseController() {}
// @Pointcut("@this(de.scrum_master.app.LooseController)")
// private static void looseController() {}
@Before("requestMapping() && looseController()")
public void myAdvice(JoinPoint thisJoinPoint) {
System.out.println(thisJoinPoint);
}
}
package de.scrum_master.app;
import org.springframework.web.bind.annotation.RequestMapping;
//@LooseController
public abstract class PutController {
@RequestMapping("/{id}")
public void update(String id) {
}
}
package de.scrum_master.app;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Retention(RUNTIME)
public @interface LooseController {
}
package de.scrum_master.app;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
@Component
@LooseController
@RequestMapping("/something")
public class Controller1 extends PutController {
}
package de.scrum_master.app;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class AspectApplication implements CommandLineRunner {
@Autowired
private Controller1 controller1;
@Autowired
private ConfigurableApplicationContext context;
public static void main(String[] args) {
SpringApplication.run(AspectApplication.class, "--logging.level.root=WARN", "--spring.main.banner-mode=off");
}
@Override
public void run(String... strings) throws Exception {
controller1.update("test");
context.close();
}
}
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>aspects</groupId>
<artifactId>aspects</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
</project>
Upvotes: 1
Views: 3320
Reputation: 67317
As I said in my comment, I have to speculate:
My hypothesis for now is that
@Component
s or otherwise declared as Spring beans in your configuration.But in order to show you what happens I will use a stand-alone Java example with AspectJ, not a Spring application. I only put spring-web.jar
and sprint-context.jar
on my classpath so as to resolve the Spring annotations.
Annotation:
package de.scrum_master.app;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface LooseController {}
Abstract base class:
package de.scrum_master.app;
import org.springframework.web.bind.annotation.RequestMapping;
public abstract class PutController {
@RequestMapping("/{id}")
public void update(String id) {}
}
Subclass + main
method:
package de.scrum_master.app;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
@Component
@LooseController
@RequestMapping("/something")
public class Controller1 extends PutController {
public static void main(String[] args) {
new Controller1().update("foo");
}
}
The aspect which I guess you might use:
Please note that your own pointcut within(@blabla.LooseController)
is syntactically invalid, this is why I changed it to @within(blabla.LooseController)
.
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class MyAspect {
@Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
private static void requestMapping() {}
@Pointcut("@within(de.scrum_master.app.LooseController)")
private static void looseController() {}
@Before("requestMapping() && looseController()")
public void myAdvice(JoinPoint thisJoinPoint) {
System.out.println(thisJoinPoint);
}
}
Console log when running Controller1.main
:
staticinitialization(de.scrum_master.app.Controller1.<clinit>)
call(void de.scrum_master.app.Controller1.update(String))
Now you see the problem: AspectJ can intercept a few joinpoints for the given pointcut, but neither call
nor staticinitialization
are supported by Spring AOP according to the documentation.
So either you need to switch to AspectJ with LTW or devise another pointcut strategy.
To be continued, I have to interrupt the answer here for a while because I have an appointment, but will add some more info later.
Update: Okay, here is your problem and a solution: @within(de.scrum_master.app.LooseController)
looks inside classes with the @LooseController
annotation, but the parent class with the annotated method you are trying to intercept does not have that annotation. Thus, @within()
is not the right pointcut type for you. What you want to express is that the instance, i.e. the current target
object upon which the method is called, belongs to an annotated class. Therefore, you need a @target()
pointcut:
@Pointcut("@target(de.scrum_master.app.LooseController)")
private static void looseController() {}
Alternatively you could also use this workaround (a little ugly, but works too):
@Pointcut("target(@de.scrum_master.app.LooseController Object)")
private static void looseController() {}
Now the console log says:
execution(void de.scrum_master.app.PutController.update(String))
This should also work in Spring AOP because execution()
is a supported pointcut type there.
Upvotes: 2