我如何(或应该)将 Aspect Advice 应用于动态代理?

How can I (or should I) apply Aspect Advice to a dynamic proxy?

假设我有 Spring AOP 配置为

@Configuration
@EnableAspectJAutoProxy
@ComponentScan
class Config { ... }

我有一些接口,ProxiableInterface。我用

实现它
ProxiableInterface pi = (ProxiableInterface) Proxy.newProxyInstance(
    applicationContext.getClassLoader(),
    new Class[] {ProxiableInterface.class},
    (proxy, method, args) -> { ... });

我还有一个看点:

@Aspect
class SomeAspect {
    @Around("execution(package.ProxiableInterface.*)")
    Object doStuffAround(ProceedingJoinPoint pjp) { ... }
}

当我在 ProxiableInterface 上调用方法时,不会调用 Aspect 方法。有没有办法对 "register" 这个代理进行排序,以便它被告知?我可以简单地执行建议在代理的 InvocationHandler 中执行的操作,但这会导致代码重复,因为此建议也已应用于代码的其他区域。

我找到的解决方案是使用org.springframework.aop.aspectj.annotation.AspectJProxyFactory

在我的情况下,一个更复杂的问题是我想要代理的 class 是抽象的,而不是接口,这意味着 Spring 必须使用 CGLIB 代理它而不是 JDK 代理.然而,事实证明这在某种程度上比烦人更有用。

首先,我有一个摘要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();
    }
}

(我重写了一些 Object 方法并使它们成为 final 因为否则它们会被代理,但在那种情况下我需要为它们编写 Advice。)

然后我有一个 AspectJProxyFactory 和一个具有原型作用域的 bean:

@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();
    }
}

请注意,Somethings 必须是让 Spring AOP 知道它们的 bean。这是看点:

@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");
    }
}

这样我就可以从代理工厂获取不同的实例,AOP 自动代理会建议这些实例。

    Something something1 = applicationContext.getBean(Something.class);
    Something something2 = applicationContext.getBean(Something.class);

    Assert.assertEquals(something1, something1);
    Assert.assertNotEquals(something1, something2);

    something1.doWhatever();
    something2.doWhatever();

这会打印如下内容:

org.luc.Something$$EnhancerBySpringCGLIB$$cadae9a8 638486177: I've been advised org.luc.Something$$EnhancerBySpringCGLIB$$cadae9a8 426019904: I've been advised