Advisor
是Spring AOP中的独有术语,它是一种特殊的切面 (Aspect);Advisor有两个分支,分别是PointcutAdvisor
与IntroductionAdvisor
。
PointcutAdvisor持有一个通知(Advice)和一个切入点(Pointcut),Spring AOP 将通知建模为org.aopalliance.intercept.MethodInterctptor
拦截器,切入点用于声明应该在哪些连接点(Joinpoint)处应用切面逻辑,而连接点在SpringAOP 中专指方法的执行,因此,PointcutAdvisor中的通知是方法级的拦截器;IntroductionAdvisor仅持有一个通知和一个类过滤器(ClassFilter),显然,IntroductionAdvisor中的通知是类级的拦截器。
IntroductionAdvisor 用于为目标对象引入一个或多个接口,在不改动现有目标对象的情况下,目标对象即可以自动持有所引入接口中的行为。IntroductionAdvisor 所持有的通知就是用来拦截目标对象所引入的接口的。
1入门
代码语言:javascript复制需求
CustomPrintService
提供普通黑白打印服务,而ColorfulPrintService
则提供彩色打印服务;在不改动现有CustomPrintService的情况下,如何使其具备彩色打印的能力呢?
public interface CustomPrintService {
public void doPrint(String content);
}
public class CustomPrintServiceImpl implements CustomPrintService {
@Override
public void doPrint(String content) {
System.out.println("黑白打印---" content);
}
}
代码语言:javascript复制public interface ColorfulPrintService {
public void doColorfulPrint(String content);
}
public class ColorfulPrintServiceImpl implements ColorfulPrintService {
@Override
public void doColorfulPrint(String content) {
System.out.println("彩色打印---" content);
}
}
在Spring AOP中,能否为目标对象生成代理取决于该目标对象是否有匹配的Advisor,那自然应该从IntroductionAdvisor入手了。DefaultIntroductionAdvisor实现了IntroductionAdvisor接口,一般使用它就行了;在构造DefaultIntroductionAdvisor实例前,需要先构造一个IntroductionInterceptor
实例,体贴的官方开发人员为大家预置了DelegatingIntroductionInterceptor
,基本可以满足绝大多数场景;当然,大家也可以自行实现IntroductionInterceptor或继承 DelegatingIntroductionInterceptor。
public class DelegatingIntroductionInterceptor extends IntroductionInfoSupport implements IntroductionInterceptor {
// delegate,意为“委派”,即委派引入接口的实现类处理
private Object delegate;
public DelegatingIntroductionInterceptor(Object delegate) {
init(delegate);
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
// 如果当前方法调用指向引入接口,如ColorfulPriintService#doColorfulPrint()
if (isMethodOnIntroducedInterface(mi)) {
// 则通过反射调用引入接口的实现类中的对应方法
return AopUtils.invokeJoinpointUsingReflection(this.delegate, mi.getMethod(), mi.getArguments());
}
// 如果当前方法调用没有指向引入接口,那相当于啥都没发生,继续执行拦截器链中下一个拦截器
return doProceed(mi);
}
protected Object doProceed(MethodInvocation mi) throws Throwable {
return mi.proceed();
}
}
DelegatingIntroductionInterceptor中invoke()
方法会判断当前的方法调用是否指向引入接口,若是,则直接通过反射调用引入接口的实现类中相关方法。
1.1 基于JDK动态代理
代码语言:javascript复制public static void main(String[] args) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setInterfaces(CustomPrintService.class);
proxyFactory.setTargetSource(new SingletonTargetSource(new CustomPrintServiceImpl()));
proxyFactory.setProxyTargetClass(false);
// DelegatingIntroductionInterceptor构造方法中必须是所引入接口的实现类,
// 其内部会通过ClassUtils.getAllInterfacesAsSet(delegate)来获取引入接口;
// DelegatingIntroductionInterceptor实现了IntroductionInfo接口,
// 所以DefaultIntroductionAdvisor可以通过它的getInterfaces()获取引入的接口。
DelegatingIntroductionInterceptor delegatingIntroductionInterceptor = new DelegatingIntroductionInterceptor(new ColorfulPrintServiceImpl());
DefaultIntroductionAdvisor defaultIntroductionAdvisor = new DefaultIntroductionAdvisor(delegatingIntroductionInterceptor);
// ProxyFactory内部维护了一个interfaces列表,后期所生成的代理类将实现这些接口;
// 当向ProxyFactory注册IntroductionAdvisor时,会通过IntroductionAdvisor#getInterfaces()来填充interfaces列表;
proxyFactory.addAdvisor(defaultIntroductionAdvisor);
CustomPrintService customPrintService = (CustomPrintService) proxyFactory.getProxy();
customPrintService.doPrint("introduction advisor");
ColorfulPrintService colorfulPrintService = (ColorfulPrintService) proxyFactory.getProxy();
colorfulPrintService.doColorfulPrint("introduction advisor");
}
1.2 基于CGLIB代理
代码语言:javascript复制public static void main(String[] args) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTargetSource(new SingletonTargetSource(new CustomPrintServiceImpl()));
proxyFactory.setProxyTargetClass(true);
// DelegatingIntroductionInterceptor构造方法中必须是所引入接口的实现类,
// 其内部会通过ClassUtils.getAllInterfacesAsSet(delegate)来获取引入接口;
// DelegatingIntroductionInterceptor实现了IntroductionInfo接口,
// 所以DefaultIntroductionAdvisor可以通过它的getInterfaces()获取引入的接口。
DelegatingIntroductionInterceptor delegatingIntroductionInterceptor = new DelegatingIntroductionInterceptor(new ColorfulPrintServiceImpl());
DefaultIntroductionAdvisor defaultIntroductionAdvisor = new DefaultIntroductionAdvisor(delegatingIntroductionInterceptor);
// ProxyFactory内部维护了一个interfaces列表,后期所生成的代理类将实现这些接口;
// 当向ProxyFactory注册IntroductionAdvisor时,会通过IntroductionAdvisor#getInterfaces()来填充interfaces列表;
proxyFactory.addAdvisor(defaultIntroductionAdvisor);
CustomPrintService customPrintService = (CustomPrintService) proxyFactory.getProxy();
customPrintService.doPrint("introduction advisor");
ColorfulPrintService colorfulPrintService = (ColorfulPrintService) proxyFactory.getProxy();
colorfulPrintService.doColorfulPrint("introduction advisor");
}
执行结果
黑白打印---introduction advisor
彩色打印---introduction advisor
2IntroductionAdvisor应用案例解读
天哪!IntroductionAdvisor实在是太冷门了,找了一圈,目前只看到Spring Retry
模块用到了它。关于Spring Retry的相关知识,请参考《初探Spring Retry》。
RetryConfiguration
既继承了AbstractPointcutAdvisor,又实现了IntroductionAdvisor;讲道理,仅仅继承AbstractPointcutAdvisor就足够了,可为啥还要实现IntroductionAdvisor呢?从其源码来看,IntroductionAdvisor相关的两个方法,RetryConfiguration只真正实现了getInterfaces()
方法,该方法表明RetryConfiguration为目标对象引入了Retryable
接口。
public class RetryConfiguration extends AbstractPointcutAdvisor implements IntroductionAdvisor {
@Override
public Class<?>[] getInterfaces() {
return new Class[] { org.springframework.retry.interceptor.Retryable.class };
}
@Override
public void validateInterfaces() throws IllegalArgumentException {}
}
可跟进到Retryable的源码后,发现这只是一个标记接口,类似java.io.Serializable
;难道RetryConfiguration实现IntroductionAdvisor接口,只是为了标识所生成的代理类具备重试能力?
public interface Retryable {}
验证上述猜想最有效的方法就是找到Spring Retry模块中针对IntroductionInterceptor接口的实现类!这对Intellij IDEA
来说简直小菜一碟嘛,这个类就是AnnotationAwareRetryOperationsInterceptor
。实锤,上述猜想成立!!!
public class AnnotationAwareRetryOperationsInterceptor implements IntroductionInterceptor {
@Override
public boolean implementsInterface(Class<?> intf) {
return org.springframework.retry.interceptor.Retryable.class.isAssignableFrom(intf);
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
MethodInterceptor delegate = getDelegate(invocation.getThis(), invocation.getMethod());
if (delegate != null) {
return delegate.invoke(invocation);
}
else {
return invocation.proceed();
}
}
private MethodInterceptor getDelegate(Object target, Method method) {
MethodInterceptor delegate = null;
Retryable retryable = findAnnotationOnTarget(target, method, Retryable.class);
if (retryable.stateful()) {
// StatefulRetryOperationsInterceptor
delegate = getStatefulInterceptor(target, method, retryable);
} else {
// RetryOperationsInterceptor
delegate = getStatelessInterceptor(target, method, retryable);
}
return delegate;
}
}
3总结
由于IntroductionAdvisor所持有的通知是类级的拦截器,所以IntroductionAdvisor的应用场景很少,一般多使用PointcutAdvisor。另外,无论是基于JDK动态代理还是基于CGLIB代理,最终所生成的代理类都会实现引入的接口,否则无法强制类型转换啊。