0

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.

Lucas Ross
  • 1,049
  • 8
  • 17
  • jic, maybe you don't need such complex interface. And since you instantiate with new, it's not proxy, so of course it's not called. just find some way to get it with bean and you should be ok – Andrii Plotnikov Aug 12 '16 at 19:57

1 Answers1

4

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 Somethings 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

Lucas Ross
  • 1,049
  • 8
  • 17
  • This is kinda neat. I do not use Spring AOP but AspectJ, so I never have this proxy problem. But for Spring AOP users it is cool stuff. :-) – kriegaex Aug 20 '16 at 08:15
  • Cool, only downside - can't be used if you have to programmatically setup `@Around("execution(void org.luc.Something.doWhatever())")` – Anand Rockzz Aug 05 '21 at 06:05