当前位置: 首页 > article >正文

基于Spring AOP和CGLIB代理实现引介增强(Introduction Advice)示例

一、Spring AOP相关概念

1. Spring AOP与AspectJ区别

Spring AOP提供跨Spring IOC的简单AOP实现,以解决程序员面临的最常见问题。它并不打算作为一个完整的AOP解决方案——它只能应用于由Spring容器管理的bean。

AspectJ是最初的AOP技术,提供完整的AOP解决方案。它比Spring AOP更健壮,但也明显更复杂。值得注意的是,AspectJ可以跨所有域对象应用。

Spring AOPAspectJ
用纯Java实现使用Java程序设计语言的扩展实现
不需要单独的编译过程需要AspectJ编译器(ajc),除非设置了LTW
只有运行时编织可用运行时编织不可用。支持编译时、编译后和加载时编织
只支持方法级编织可以编织字段,方法,构造函数,静态初始化器,最终类/方法等
只能在Spring容器管理的bean上实现可以在所有的域对象上实现
只支持方法执行切入点支持全部的切入点
为目标对象创建代理,并在这些代理上应用切面切面在应用程序执行之前(在运行之前)直接编织到代码中
比AspectJ慢得多更好的性能
易于学习和应用相对来说比Spring AOP更复杂
2. Spring AOP的过程

在运行时编织中,切面在应用程序执行期间使用目标对象的代理进行编织。

JDK动态代理:Spring AOP的首选方式。只要目标对象实现了一个接口,就会使用JDK动态代理。
CGLIB代理:如果目标对象没有实现接口,那么可以使用CGLIB代理。

请添加图片描述

3. 引介增强(Introduction Advice)概念

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

二、实现引介增强(Introduction Advice)示例

1. 一个Java类Person,一个接口EnglishLanguageFun
public class Person {
    public void useChaineseLanguage(){
        System.out.println("useChaineseLanguage:你好!");
    }
}
public interface EnglishLanguageFun {
    public void useEnglishLanguage();
}
2. 实现IntroductionInterceptor

IntroductionInterceptor是AOP Alliance MethodInterceptor 的子接口,允许拦截器实现其他接口,并通过使用该拦截器的代理提供。这是一个基本的 AOP 概念,称为介绍。引入通常是混合的,支持构建复合对象,这些对象可以实现 Java 中多重继承的许多目标。

自定义一个PersonLanguageIntroductionInterceptor 实现IntroductionInterceptor。同时实现目标接口EnglishLanguageFun

public class PersonLanguageIntroductionInterceptor implements IntroductionInterceptor, EnglishLanguageFun {
    @Override
    public void useEnglishLanguage() {
      // 覆写接口方法
        System.out.println("useEnglishLanguage:hello!");
    }

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        if (implementsInterface(methodInvocation.getMethod().getDeclaringClass())) {
            // 判断类型是EnglishLanguageFun.class时,调用useEnglishLanguage方法
          return methodInvocation.getMethod().invoke(this, methodInvocation.getArguments());
        }
        return methodInvocation.proceed();
    }

    @Override
    public boolean implementsInterface(Class<?> aClass) {
        return aClass.isAssignableFrom(EnglishLanguageFun.class);
    }
}

3. 基于CGLIB进行引介增强
		@Test
    public void aopTest(){
      // 默认AopProxyFactory实现
        DefaultAopProxyFactory defaultAopProxyFactory = new DefaultAopProxyFactory();
        AdvisedSupport advisedSupport = new AdvisedSupport();

        advisedSupport.setTargetClass(Person.class);
        advisedSupport.setTarget(new Person());
        // true的目的:为给定类创建一个 CGLIB 代理
        advisedSupport.setProxyTargetClass(true);

        DefaultIntroductionAdvisor introductionAdvisor = new DefaultIntroductionAdvisor(new PersonLanguageIntroductionInterceptor(), EnglishLanguageFun.class);
        advisedSupport.addAdvisor(introductionAdvisor);

        AopProxy aopProxy = defaultAopProxyFactory.createAopProxy(advisedSupport);
        Person person = (Person) aopProxy.getProxy();
        person.useChaineseLanguage();
        // 一个Person类,没有实现EnglishLanguageFun接口,在不修改Java类的情况下,使其具备了EnglishLanguageFun接口的功能。
        EnglishLanguageFun aopLanguageFun = (EnglishLanguageFun) aopProxy.getProxy();
        aopLanguageFun.useEnglishLanguage();
    }

单侧执行结果:

useChaineseLanguage:你好!
useEnglishLanguage:hello!

Process finished with exit code 0

其中,setProxyTargetClass需要设置为true,才是CGLIB代理。

advisedSupport.setProxyTargetClass(true);

setProxyTargetClass用来设置是否直接代理目标类,而不仅仅是代理特定接口。

默认值为“false”。将此设置为“true”以强制对 TargetSource 的公开目标类进行代理。

如果该目标类是接口,则将为给定接口创建一个 JDK 代理。如果该目标类是任何其他类,则将为给定类创建一个 CGLIB 代理。


http://www.kler.cn/news/107212.html

相关文章:

  • 外卖小程序:技术实现与关键代码
  • 使用Nginx后如何在web应用中获取用户ip及原理解释
  • Docker的架构与自制镜像的发布
  • 【pytorch】torch.gather()函数
  • CH8571 沁恒微 RISC-V EC 用芯片实验 (一)参考手册 第一章 接脚描述
  • 当线性规划与算法相遇:揭秘单纯形法(Simplex)的独特魅力
  • 最新Python深度学习技术进阶与应用
  • 【广州华锐互动】智能家居设计3D虚拟还原系统
  • FPGA时序分析与约束(7)——通过Tcl扩展SDC
  • WORD中的表格内容回车行距过大无法调整行距
  • 【STM32】STM32中断体系
  • 【期中复习】深度学习
  • Games 103 作业二
  • 10款轻量型的嵌入式GUI库分享
  • C++前缀和算法的应用:使数组相等的最小开销
  • 基于Python Django 的微博舆论、微博情感分析可视化系统(V2.0)
  • 如何快速安装MONAI(莫奈)医学标注软件
  • Xray的简单使用
  • Spring 更简单的读取和存储对象
  • docker - window Docker Desktop升级
  • redis集群的多key原子性操作如何实现?
  • Oracle RU 19.21及 datapatch -sanity_checks
  • js中HTMLCollection如何循环
  • 基于springboot实现校园志愿者管理系统项目【项目源码+论文说明】计算机毕业设计
  • 【ARMv8 SIMD和浮点指令编程】NEON 存储指令——如何将数据从寄存器存储到内存?
  • 系列二十二、如何在Spring中所有的bean都创建完成后做扩展
  • MySQL篇---第四篇
  • 论文阅读——InstructGPT
  • 数据库MySQL(四):表中字段约束和外键约束
  • 磁场设备主要有哪些