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

简介: 一个 Java 类,没有实现A接口,在不修改Java类的情况下,使其具备A接口的功能。

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 CeremenyServiceEnhancerByCGLIB*E*n*h*a*n*c*e*r*B*y**C\G\L\I\B\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.

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

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

相关文章
|
存储 缓存 文件存储
如何保证分布式文件系统的数据一致性
分布式文件系统需要向上层应用提供透明的客户端缓存,从而缓解网络延时现象,更好地支持客户端性能水平扩展,同时也降低对文件服务器的访问压力。当考虑客户端缓存的时候,由于在客户端上引入了多个本地数据副本(Replica),就相应地需要提供客户端对数据访问的全局数据一致性。
33252 201
如何保证分布式文件系统的数据一致性
|
设计模式 存储 监控
设计模式(C++版)
看懂UML类图和时序图30分钟学会UML类图设计原则单一职责原则定义:单一职责原则,所谓职责是指类变化的原因。如果一个类有多于一个的动机被改变,那么这个类就具有多于一个的职责。而单一职责原则就是指一个类或者模块应该有且只有一个改变的原因。bad case:IPhone类承担了协议管理(Dial、HangUp)、数据传送(Chat)。good case:里式替换原则定义:里氏代换原则(Liskov 
36821 22
设计模式(C++版)
|
存储 编译器 C语言
抽丝剥茧C语言(初阶 下)(下)
抽丝剥茧C语言(初阶 下)
|
机器学习/深度学习 人工智能 自然语言处理
带你简单了解Chatgpt背后的秘密:大语言模型所需要条件(数据算法算力)以及其当前阶段的缺点局限性
带你简单了解Chatgpt背后的秘密:大语言模型所需要条件(数据算法算力)以及其当前阶段的缺点局限性
24905 16
|
机器学习/深度学习 弹性计算 监控
重生之---我测阿里云U1实例(通用算力型)
阿里云产品全线降价的一力作,2023年4月阿里云推出新款通用算力型ECS云服务器Universal实例,该款服务器的真实表现如何?让我先测为敬!
36824 15
重生之---我测阿里云U1实例(通用算力型)
|
SQL 存储 弹性计算
Redis性能高30%,阿里云倚天ECS性能摸底和迁移实践
Redis在倚天ECS环境下与同规格的基于 x86 的 ECS 实例相比,Redis 部署在基于 Yitian 710 的 ECS 上可获得高达 30% 的吞吐量优势。成本方面基于倚天710的G8y实例售价比G7实例低23%,总性价比提高50%;按照相同算法,相对G8a,性价比为1.4倍左右。
|
存储 算法 Java
【分布式技术专题】「分布式技术架构」手把手教你如何开发一个属于自己的限流器RateLimiter功能服务
随着互联网的快速发展,越来越多的应用程序需要处理大量的请求。如果没有限制,这些请求可能会导致应用程序崩溃或变得不可用。因此,限流器是一种非常重要的技术,可以帮助应用程序控制请求的数量和速率,以保持稳定和可靠的运行。
29949 52

热门文章

最新文章