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

相关文章
|
29天前
|
前端开发 Java 数据库连接
Spring Boot 详细简介!
Spring Boot 是什么?能干啥?
248 0
Spring Boot 详细简介!
|
29天前
|
JSON fastjson Java
阿里巴巴为什么不建议 boolean 类型变量用 isXXX
为什么不推荐使用 isXXX 来命名呢?到底是用基本类型的数据好呢还是用包装类好呢?
阿里巴巴为什么不建议 boolean 类型变量用 isXXX
|
29天前
|
Java API Maven
Spring Boot 创建项目详细介绍
如何创建一个 Spring Boot 项目,以及自动生成的目录文件作用。
123 2
|
29天前
|
SQL 缓存 Java
Mybatis 技术内幕:执行一个Sql命令的完整流程
如果不是使用Mapper接口调用,而是直接调用SqlSession的方法,那么,流程图从SqlSession的地方开始即可,后续都是一样的。
Mybatis 技术内幕:执行一个Sql命令的完整流程
|
29天前
|
XML 人工智能 前端开发
把 GLM-5.3 接入到 DeepSeek Harness,夯爆了!
GLM-5.3 + Kimi K3 + DeepSeek V4 Pro 模型同时接入 DeepSeek Harness,前端 + 后端全栈 2 大任务横评测试,到底谁是 AI 编程之王?
488 0
|
29天前
|
缓存 编译器 Android开发
[Android 从零到一] Compose LazyColumn 性能优化:key、稳定性与重组治理
本文深入剖析 LazyColumn 性能问题三大根源:key 配置错误、数据类型不稳定、重组作用域过大,结合实例详解 key 选型、@Immutable 应用、derivedStateOf 隔离、状态下沉等可落地优化方案,助你彻底解决列表闪烁、卡顿与无效重组。
94 7
|
29天前
|
人工智能 JavaScript API
阿里云百炼CLI全解:命令行工具接入AI Agent实操与完整能力指南
在AI Agent快速迭代的开发环境中,开发者经常会遇到一个现实难题:不同AI智能体框架对接云端大模型、知识库、多模态生成工具时,需要反复编写接口代码,处理鉴权、请求封装、返回解析、异常重试等大量重复逻辑。每切换一套Agent框架,就要重新适配一整套API调用逻辑,不仅消耗大量开发时间,还容易出现鉴权不一致、参数不兼容、多工具能力无法复用等问题。百炼CLI作为官方开源的命令行工具,把平台上百款大模型、知识库检索、联网搜索、图像视频生成、语音处理等能力全部封装为终端可直接调用的指令,原生面向各类AI Agent做适配,支持脚本调用、CI流水线集成、本地Agent框架插件接入,大幅降低AI智能体的
136 1
|
29天前
|
Linux Windows
|
29天前
|
人工智能 安全 API
DeepSeek V4 Pro暴涨近50分,我却更想押GLM-5.3
DeepSeek V4 Pro正式版和GLM-5.3前后脚上线。一个把Agent成绩拉得像换了一代模型,一个连基座都没换,只靠后训练继续往前拱。老金来给你详细说说。
|
1月前
|
消息中间件 存储 人工智能
免费送 10 张三天通票!邀你共赴 ApacheCon 2026
Community Over Code 是 Apache 软件基金会(ASF)官方全球系列大会,今年,Community Over Code Asia 2026 将于 8 月 7-9 日在北京市海淀区中关村国家自主创新示范区会议中心举行,覆盖 AI、云原生、大数据、开源社区治理等热点领域。阿里云云原生应用平台团队将携手开源社区贡献者和用户们,在消息、微服务、Web 应用与框架三大领域带来 11 个议题,欢迎大家报名参加。关注阿里云云原生公众号,在本文章评论区留言回复 ASFC+你想听的议题,前 10 名参与互动的用户将获得大会三天通票一张(价值 999 元)。

热门文章

最新文章