There is one interface Foo
, having two default methods m1
, m2
.
There are 3 classes Foo1
, Foo2
, Foo3
implementing Foo
. Foo3
does not override method m1
, but Foo1
, Foo2
do.
I want to create an aspect which targets Foo3
's m1
method.
I tried @Pointcut("bean(foo3) && execution(* *.m1(..))
, and it works properly while Spring Boot is running. But when I create a proxy object programmatically for unit testing, it does not work.
I know that bean(foo3)
makes it work under Spring Boot, but I cannot find any alternative way to express the pointcut, which still works in the test. How can I make it work in this situation?
public interface Foo {
default Object m1() {
// doSomething
return Something;
}
default Object m2() {
// doSomething
return Something;
}
}
public class Foo3 implements Foo {
// not overriding any method.
}
@Component @Aspect
public class ExampleAspect {
@Pointcut("bean(foo3) && execution(* *.m1(..))")
public void exampleAspect() {}
@Around("exampleAspect()")
public Object proxyMethod(ProceedingJoinPoint joinPoint) throws Throwable {
// ...
Object result = joinPoint.proceed();
// ...
}
}
public Foo3Test {
Foo instance = new Foo3();
AspectJProxyFactory factory = new AspectJProxyFactory(instance);
factory.addAspect(new ExampleAspect());
Foo proxy = factory.getProxy();
proxy.m1(); // it didn't use proxy. invoke real method.
}
I tried these pointcuts:
target(Foo3) && execution(* Foo.m1(..))
does not work even under Spring Bootwithin(Foo3) && execution(* Foo.m1(..))
does not work even under Spring Bootexecution(Foo.m1(..))
works but all m1
methods are proxiedI am not a Spring expert, but in order to make the special Spring AOP pointcut designator bean
work, I guess you might need an application context in your test. Hoewever, I understand the need to create quickly running tests without all that ceremony. Fortunately, this is not difficult to achieve. Simply change your pointcut to:
@Pointcut("target(Foo3) && execution(* m1(..))")
public void exampleAspect() {}
I tried, it works beautifully. Here is how I tested it:
package de.scrum_master.spring.q76824399;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class ExampleAspect {
@Pointcut("target(Foo3) && execution(* m1(..))")
public void exampleAspect() {}
@Around("exampleAspect()")
public Object proxyMethod(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println(joinPoint + " -> " + joinPoint.getTarget());
return joinPoint.proceed();
}
}
package de.scrum_master.spring.q76824399;
import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;
public class Foo3Test {
public static void main(String[] args) {
ExampleAspect aspect = new ExampleAspect();
Foo[] instances = { new Foo1(), new Foo2(), new Foo3() };
for (Foo instance : instances) {
AspectJProxyFactory factory = new AspectJProxyFactory(instance);
factory.addAspect(aspect);
Foo proxy = factory.getProxy();
proxy.m1();
}
}
}
And this is the console log:
10:45:11.823 [main] DEBUG org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public java.lang.Object de.scrum_master.spring.q76824399.ExampleAspect.proxyMethod(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable
10:45:12.055 [main] DEBUG org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public java.lang.Object de.scrum_master.spring.q76824399.ExampleAspect.proxyMethod(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable
10:45:12.071 [main] DEBUG org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory - Found AspectJ method: public java.lang.Object de.scrum_master.spring.q76824399.ExampleAspect.proxyMethod(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable
execution(Object de.scrum_master.spring.q76824399.Foo.m1()) -> de.scrum_master.spring.q76824399.Foo3@6eda5c9
Please note how the aspect finds all 3 instances of Foo1
, Foo2
, Foo3
, but the advice is only triggered for the Foo3
instance.