Say I have Spring AOP configured like
@Configuration
@EnableAspectJAutoProxy
@ComponentScan
class Config { ... }
and I have some interface, ProxiableInterface
. I implement it with
ProxiableInterface pi = (ProxiableInterface) Proxy.newProxyInstance(
applicationContext.getClassLoader(),
new Class[] {ProxiableInterface.class},
(proxy, method, args) -> { ... });
I also have an Aspect:
@Aspect
class SomeAspect {
@Around("execution(package.ProxiableInterface.*)")
Object doStuffAround(ProceedingJoinPoint pjp) { ... }
}
When I call a method on ProxiableInterface
, the Aspect method does not get called. Is there a way to sort of "register" this Proxy so that it becomes advised? I could simply do what the advice does inside the Proxy's InvocationHandler, but it would result in code duplication since this Advice already applies to other areas of the code as well.
The solution I found is to use org.springframework.aop.aspectj.annotation.AspectJProxyFactory
.
An added complication in my case is that the class I want to proxy is abstract, not an interface, meaning Spring must proxy it with CGLIB and not a JDK proxy. However, this proved in a way to be more useful than it is annoying.
First, I have an abstract class:
public abstract class Something {
abstract void doWhatever();
@Override
public final boolean equals(Object o) {
return this == o;
}
@Override
public final int hashCode() {
return System.identityHashCode(this);
}
@Override
public final String toString() {
return getClass().getName() + " " + hashCode();
}
}
(I've overridden some Object methods and made them final
because they would otherwise be proxied, but in that case I would need to write Advice for them.)
Then I have an AspectJProxyFactory
and a bean with prototype scope as such:
@Configuration
@EnableAspectJAutoProxy
class Config {
private static final AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
static {
proxyFactory.setTargetClass(Something.class);
proxyFactory.addAspect(SomeAspect.class);
}
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
Something something() {
return proxyFactory.getProxy();
}
}
Note that the Something
s must be beans for Spring AOP to be made aware of them. And this is the Aspect:
@Aspect
public class SomeAspect {
@Around("execution(void org.luc.Something.doWhatever())")
void around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println(pjp.getThis().toString() + ": I've been advised");
}
}
This way I can get distinct instances from the proxy factory, which will have been advised by the AOP auto-proxy.
Something something1 = applicationContext.getBean(Something.class);
Something something2 = applicationContext.getBean(Something.class);
Assert.assertEquals(something1, something1);
Assert.assertNotEquals(something1, something2);
something1.doWhatever();
something2.doWhatever();
This prints something like:
org.luc.Something$$EnhancerBySpringCGLIB$$cadae9a8 638486177: I've been advised org.luc.Something$$EnhancerBySpringCGLIB$$cadae9a8 426019904: I've been advised