科普——Spring中引入增强(IntroductionAdvice)的底层实现原理

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

转载声明:转载请注明出处,本技术博客是本人原创文章

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> 科普——Spring中引入增强(IntroductionAdvice)的底层实现原理

点击上方“后端技术精选”,选择“置顶公众号”

技术文章第一时间送达!

作者:祖大俊 my.oschina.net/zudajun/blog/663962

my.oschina.net/zudajun/blog/663962

Spring中有五种增强:BeforeAdvide(前置增强)、AfterAdvice(后置增强)、ThrowsAdvice(异常增强)、RoundAdvice(环绕增强)、IntroductionAdvice(引入增强)

RoundAdvice(环绕增强):就是BeforeAdvide(前置增强)、AfterAdvice(后置增强)的组合使用叫环绕增强。

前四种增强都比较简单,我们今天要介绍的是IntroductionAdvice(引入增强)的概念及原理。

引入增强(Introduction Advice)的概念:一个Java类,没有实现A接口,在不修改Java类的情况下,使其具备A接口的功能。

1.Cglib实现引入增强

记住,我的目的不是告诉你怎么在Spring中使用引入增强功能(这不是我的风格),而是探究引入增强功能的底层实现原理。


public interface IHello {

    public void sayHello();
}

上面是接口功能,CeremonyService是需要增强的类,在不改变CeremonyService类的情况下,使其具备IHello接口功能。


public class CeremenyService {

    public void sayBye() {
        System.out.println("Say bye from Ceremeny.");
    }

}

看起来要像下面这样:


CeremenyService cs;

IHello ih = (IHello) cs;
ih.sayHello();

即,CeremenyService居然变成了IHello类型。

我们编写一个重要的拦截器,来实现此功能。


import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import x.y.IHello;

public class IntroInterceptor implements MethodInterceptor, IHello {
        // 实现了IHello增强接口的对象
    private Object delegate;

    public IntroInterceptor() {
        this.delegate = this;
    }

    public IntroInterceptor(Object delegate) {
        this.delegate = delegate;
    }

    @Override
    public void sayHello() {
        System.out.println("Say hello from delegate.");
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Class? clz = method.getDeclaringClass();
        if (clz.isAssignableFrom(IHello.class)) {
                // 如果实现了IHello增强接口,则调用实现类delegate的方法
            return method.invoke(delegate, args);
        }
        return methodProxy.invokeSuper(obj, args);
    }
}

我们来编写一个测试类。


public static void main(String[] args) {
    Enhancer en = new Enhancer();
    en.setSuperclass(CeremenyService.class);
    en.setInterfaces(new Class[] { IHello.class });
    en.setCallback(new IntroInterceptor());

    CeremenyService cs = (CeremenyService) en.create();
    cs.sayBye();

    IHello ih = (IHello) cs;
    ih.sayHello();
}

en.setInterfaces(new Class[] { IHello.class });非常重要,表示Cglib生成代理类,将要实现的接口集合。

于是生成的代理类Class,类似于: public class CeremenyService$$EnhancerByCGLIB$$86859be5 extends CeremenyService implements IHello

输出结果:


Say bye from Ceremeny.
Say hello from delegate.

这就是大名鼎鼎的引入增强(Introduction Advice)的底层实现原理。

2. Spring framework引入增强源码解读

Spring的xml文件配置。


bean id="ceremonyService" class="x.y.service.CeremonyService" /
bean id="ceremonyIntroAdvice" class="x.y.advice.CeremonyIntroAdvice" /

bean id="ceremonyProxy" class="org.springframework.aop.framework.ProxyFactoryBean"
        property name="interfaces" value="x.y.IHello"/                   !-- 需要动态实现的接口 --
        property name="target" ref="ceremonyService"/                    !-- 目标类 --
        property name="interceptorNames" value="ceremonyIntroAdvice"/    !-- 引入增强 --
        property name="proxyTargetClass" value="true"/                   !-- 代理目标类(默认为 false,代理接口) --
/bean

我们需要自定义一个拦截器。


import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;

import x.y.IHello;

@SuppressWarnings("serial")
public class CeremonyIntroAdvice extends DelegatingIntroductionInterceptor implements IHello {

    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        return super.invoke(mi);
    }

    @Override
    public void sayHello() {
        System.out.println("Say hello.");
    }

}

在Spring中,要实现引入增强,需要继承自DelegatingIntroductionInterceptor。

下面看看该DelegatingIntroductionInterceptor类的invoke()方法源码。


@Override
    public Object invoke(MethodInvocation mi) throws Throwable {
            // 检测是否是引入增强
        if (isMethodOnIntroducedInterface(mi)) {
            // 执行实现了引入增强接口的delegate对象的增强方法
            Object retVal = AopUtils.invokeJoinpointUsingReflection(this.delegate, mi.getMethod(), mi.getArguments());

            // Massage return value if possible: if the delegate returned itself,
            // we really want to return the proxy.
            if (retVal == this.delegate && mi instanceof ProxyMethodInvocation) {
                Object proxy = ((ProxyMethodInvocation) mi).getProxy();
                if (mi.getMethod().getReturnType().isInstance(proxy)) {
                    retVal = proxy;
                }
            }
            return retVal;
        }

        return doProceed(mi);
    }

AopUtils.invokeJoinpointUsingReflection()方法内部,其实就是反射方法调用。


try {
    ReflectionUtils.makeAccessible(method);
    return method.invoke(target, args);
}

最后写一个测试方法,来测试一下。


public static void main(String[] args) {

    FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext(
            "D:/workspace/Spring4.2.5/bin/applicationContext.xml");

    CeremonyService service = context.getBean("ceremonyProxy", CeremonyService.class);
    service.sayBye();

    IHello hello = (IHello) service;
    hello.sayHello();

    context.close();
}

输出:


Say bye.
Say hello.

总结

介绍如何使用的文章比较多,而介绍原理性的文章少一些,我比较喜欢介绍原理性的文章。希望本篇博文,对您有用。

END

Java面试题专栏

科普:Spring中引入增强(IntroductionAdvice)的底层实现原理

欢迎长按下图关注公众号后端技术精选

科普:Spring中引入增强(IntroductionAdvice)的底层实现原理

原文始发于微信公众号(后端技术精选):

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

转载声明:转载请注明出处,本技术博客是本人原创文章

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> 科普——Spring中引入增强(IntroductionAdvice)的底层实现原理


 上一篇
Spring 体系常用项目一览 Spring 体系常用项目一览
点击上方“Java知音”,选择“置顶公众号” 技术文章第一时间送达! 来源:MageekChiu segmentfault.com/a/1190000011334873 segmentfault.com/a/11900000
2021-04-05
下一篇 
为什么微服务一定要有网关? 为什么微服务一定要有网关?
点击上方“后端技术精选”,选择“置顶公众号” 技术文章第一时间送达! 作者: 赵计刚 cnblogs.com/java-zhao/p/6716059.html cnblogs.com/java-zhao/p/6716059.
2021-04-05