前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >IntroductionAdvisor,真的很冷门!

IntroductionAdvisor,真的很冷门!

作者头像
程序猿杜小头
发布2022-12-01 21:38:35
3300
发布2022-12-01 21:38:35
举报
文章被收录于专栏:程序猿杜小头程序猿杜小头

Advisor是Spring AOP中的独有术语,它是一种特殊的切面 (Aspect);Advisor有两个分支,分别是PointcutAdvisorIntroductionAdvisor

PointcutAdvisor持有一个通知(Advice)和一个切入点(Pointcut),Spring AOP 将通知建模为org.aopalliance.intercept.MethodInterctptor拦截器,切入点用于声明应该在哪些连接点(Joinpoint)处应用切面逻辑,而连接点在SpringAOP 中专指方法的执行,因此,PointcutAdvisor中的通知是方法级的拦截器;IntroductionAdvisor仅持有一个通知和一个类过滤器(ClassFilter),显然,IntroductionAdvisor中的通知是类级的拦截器。

IntroductionAdvisor 用于为目标对象引入一个或多个接口,在不改动现有目标对象的情况下,目标对象即可以自动持有所引入接口中的行为。IntroductionAdvisor 所持有的通知就是用来拦截目标对象所引入的接口的。

1入门

需求CustomPrintService提供普通黑白打印服务,而ColorfulPrintService则提供彩色打印服务;在不改动现有CustomPrintService的情况下,如何使其具备彩色打印的能力呢?

代码语言:javascript
复制
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。

代码语言:javascript
复制
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");
}

执行结果

代码语言:javascript
复制
黑白打印---introduction advisor
彩色打印---introduction advisor

2IntroductionAdvisor应用案例解读

天哪!IntroductionAdvisor实在是太冷门了,找了一圈,目前只看到Spring Retry模块用到了它。关于Spring Retry的相关知识,请参考《初探Spring Retry》。

RetryConfiguration既继承了AbstractPointcutAdvisor,又实现了IntroductionAdvisor;讲道理,仅仅继承AbstractPointcutAdvisor就足够了,可为啥还要实现IntroductionAdvisor呢?从其源码来看,IntroductionAdvisor相关的两个方法,RetryConfiguration只真正实现了getInterfaces()方法,该方法表明RetryConfiguration为目标对象引入了Retryable接口。

代码语言:javascript
复制
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接口,只是为了标识所生成的代理类具备重试能力?

代码语言:javascript
复制
public interface Retryable {}

验证上述猜想最有效的方法就是找到Spring Retry模块中针对IntroductionInterceptor接口的实现类!这对Intellij IDEA来说简直小菜一碟嘛,这个类就是AnnotationAwareRetryOperationsInterceptor。实锤,上述猜想成立!!!

代码语言:javascript
复制
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代理,最终所生成的代理类都会实现引入的接口,否则无法强制类型转换啊。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-07-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序猿杜小头 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1入门
    • 1.1 基于JDK动态代理
      • 1.2 基于CGLIB代理
      • 2IntroductionAdvisor应用案例解读
      • 3总结
      相关产品与服务
      云顾问
      云顾问(Tencent Cloud Smart Advisor)是一款提供可视化云架构IDE和多个ITOM领域垂直应用的云上治理平台,以“一个平台,多个应用”为产品理念,依托腾讯云海量运维专家经验,助您打造卓越架构,实现便捷、灵活的一站式云上治理。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档