我如何(或应该)将 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();
}
}
请注意,Something
s 必须是让 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
假设我有 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();
}
}
请注意,Something
s 必须是让 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