首页 > 其他分享 >【Spring -- 09 AOP 实现方式】

【Spring -- 09 AOP 实现方式】

时间:2024-07-19 11:55:17浏览次数:16  
标签:-- Spring 09 代理 HelloServiceImpl 对象 切面 CGLIB 动态

在 Java Spring 框架中,AOP (面向切面编程) 的代理实现主要通过以下几种方式:

  1. JDK 动态代理:

    • JDK 动态代理是基于 Java 的 java.lang.reflect.Proxy 类实现的.
    • 当需要代理的目标对象实现了一个或多个接口时,Spring 会选择使用 JDK 动态代理.
    • JDK 动态代理的工作原理如下:
      1. 首先,Spring 会为目标对象的接口创建一个动态代理类.
      2. 这个动态代理类会实现目标对象的所有接口,并且包含与目标对象相同的方法签名.
      3. 在动态代理类的方法实现中,会首先执行切面逻辑,然后再调用目标对象的对应方法.
      4. 最后,Spring 会在运行时生成动态代理类的实例,并将其作为目标对象的代理使用.
    • 使用 JDK 动态代理的优点是无需依赖第三方库,可以在纯 Java 环境下运行.但缺点是只能代理接口,不能代理具体的实现类.
  2. CGLIB 代理:

    • CGLIB (Code Generation Library) 是一个第三方字节码生成库.
    • 当需要代理的目标对象没有实现任何接口时,Spring 会选择使用 CGLIB 代理.
    • CGLIB 代理的工作原理如下:
      1. CGLIB 会为目标对象生成一个子类.
      2. 在子类中,CGLIB 会重写目标对象的所有非 final 方法.
      3. 在这些重写方法的实现中,CGLIB 会先执行切面逻辑,然后再调用父类(目标对象)的对应方法.
      4. 最后,Spring 会使用 CGLIB 生成的子类作为目标对象的代理.
    • 使用 CGLIB 代理的优点是可以代理具体的实现类,而不仅限于接口.但缺点是需要依赖第三方库,并且对于 final 方法无法进行代理.
  3. AspectJ 编译时织入:

    • 除了 JDK 动态代理和 CGLIB 代理,Spring 还支持使用 AspectJ 进行编译时织入.
    • 在编译阶段,AspectJ 会将切面逻辑织入到目标类中,生成包含切面逻辑的字节码文件.
    • 这种方式的优点是执行效率高,但需要在编译时进行特殊处理.

Spring 会根据目标对象的情况自动选择合适的代理方式。如果目标对象实现了接口,则使用 JDK 动态代理;否则使用 CGLIB 代理。开发者也可以通过配置显式指定使用哪种代理方式。

在实现 AOP 功能时,Spring 会在运行时动态地为目标对象生成代理对象,并在代理对象中注入切面逻辑。这些代理对象会替代原始的目标对象,从而实现在不修改目标对象源码的情况下添加切面功能。

JDK 动态代理示例

假设有一个接口 HelloService,以及它的实现类 HelloServiceImpl:

public interface HelloService {
    void sayHello();
}

public class HelloServiceImpl implements HelloService {
    @Override
    public void sayHello() {
        System.out.println("Hello from HelloServiceImpl!");
    }
}

使用 JDK 动态代理来创建 HelloService 的代理对象:

public class JdkProxyExample {
    public static void main(String[] args) {
        // 创建目标对象
        HelloService helloService = new HelloServiceImpl();

        // 创建 InvocationHandler 实现类
        InvocationHandler handler = (proxy, method, args1) -> {
            System.out.println("Before method invocation");
            method.invoke(helloService, args1);
            System.out.println("After method invocation");
            return null;
        };

        // 使用 Proxy 生成动态代理对象
        HelloService proxy = (HelloService) Proxy.newProxyInstance(
                HelloService.class.getClassLoader(),
                new Class[]{HelloService.class},
                handler
        );

        // 调用代理对象的方法
        proxy.sayHello();
    }
}

在这个示例中:

  1. 创建了一个 InvocationHandler 实现类,它包含了切面逻辑(打印方法调用前后的信息)。
  2. 然后使用 Proxy.newProxyInstance() 方法创建了 HelloService 的动态代理对象。
  3. 最后,我们调用代理对象的 sayHello() 方法,它会先执行切面逻辑,然后再调用目标对象的对应方法。

CGLIB 代理示例

假设有一个 HelloServiceImpl 类,但它没有实现任何接口:

public class HelloServiceImpl {
    public void sayHello() {
        System.out.println("Hello from HelloServiceImpl!");
    }
}

使用 CGLIB 代理来创建 HelloServiceImpl 的代理对象:

public class CglibProxyExample {
    public static void main(String[] args) {
        // 创建 CGLIB 增强器
        Enhancer enhancer = new Enhancer();

        // 设置需要代理的目标类
        enhancer.setSuperclass(HelloServiceImpl.class);

        // 设置回调函数
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                System.out.println("Before method invocation");
                Object result = proxy.invokeSuper(obj, args);
                System.out.println("After method invocation");
                return result;
            }
        });

        // 生成代理对象
        HelloServiceImpl proxy = (HelloServiceImpl) enhancer.create();

        // 调用代理对象的方法
        proxy.sayHello();
    }
}

在这个示例中:

  1. 创建了一个 CGLIB 的 Enhancer 对象,并设置需要代理的目标类 HelloServiceImpl
  2. 然后,我们设置了一个 MethodInterceptor 回调函数,它包含了切面逻辑(打印方法调用前后的信息)。
  3. 最后,我们使用 Enhancer.create() 方法生成了 HelloServiceImpl 的代理对象,并调用了它的 sayHello() 方法。

在这个示例中,CGLIB 动态生成了 HelloServiceImpl 的子类,并在子类中重写了 sayHello() 方法,在方法实现中添加了切面逻辑。

总的来说,JDK 动态代理和 CGLIB 代理都是通过动态生成代理类的方式,在不修改目标对象源码的情况下,实现了切面功能的添加。开发者可以根据实际需求选择合适的代理方式。

标签:--,Spring,09,代理,HelloServiceImpl,对象,切面,CGLIB,动态
From: https://blog.csdn.net/m0_72328778/article/details/140536311

相关文章

  • 有趣的求和(前缀和)
    描述给出n个数排成一排,你可以任意选出连续的L个数字求和。例如:n=5L=4-2030805040连续取L个数的方法有两种。1、取前4个数-20308050和为140。2、取后4个数30805040和为200。请你找出最大和是多少,上例结果应该为200。输入描述第1行为两正整数n和L表示数......
  • 基于javaweb jsp ssm校内互助交易平台+vue录像(源码+lw+部署文档+讲解等)
    前言......
  • 如何用电脑录屏带声音?电脑录屏怎么把声音录进去!
    对于想要学习如何在电脑上录制屏幕视频并带有声音的人来说,这里是你需要的信息!在现代的混合工作环境中,屏幕录制已经无处不在。通过精确地分享屏幕内容,屏幕录制可以使沟通变得更加清晰和高效。无论是为培训视频、教程还是演示文稿,屏幕录制都是不可或缺的工具。它们能够快速捕捉......
  • 造海船(二分基础)
    描述明朝郑和下西洋,需要建造庞大的海船,需要足够的木料,因为那时候没有钢铁制造的船,现在有n根原木,现在想把这些木头切割成k段长度均为l的小段木头(木头有可能有剩余),用来制造船的部件。当然,工匠希望得到的小段木头越长越好,这样可以让船更大一些不浪费木料,请求出l的最大值......
  • 暑假集训CSP提高模拟1考试题解
    A.Start洛谷原题链接一道大模拟,赛时20pts。教授の高光时刻-输出没加句号、空格。-C++向0取整。-DOUBLE没传递。--9操作成-1(复制粘贴导致的)。-负数位运算卡常。其实这题还是比较简单的,细节在题目中讲的很详细,跟着它说的去做就好了。我的方法是把每个玩家用一个结构......
  • P9032 [COCI2022-2023#1] Neboderi
    题意给长度为\(n\)的数组\(a\),求长度不小于\(k\)的区间\([l,r]\)使得\(\gcd_{i=l}^ra_i\times\sum_{i=l}^ra_i\)最大,输出这个最大值。\(1\lek\len\le10^6,1\lea_i\le10^6\qquad\text{2.5s512MB}\)题解考虑分治(这是套路,想不到只能说做题少别打我)。......
  • [AGC012E] Camel and Oases 题解
    题目链接题目链接题目解法可能并没有那么难(?首先\(V\)的取值只有\(\logV\)种,即\(\lfloor\frac{V}{2^k}\rfloor\)称\(\lfloor\frac{V}{2^k}\rfloor\)为第\(k\)层,先预处理出每一层的极大连通区间我们可以把问题抽象成:每一层中选一个区间,要求覆盖\([1,n]\),且第\(0......
  • 中电金信:语言服务游戏行业解决方案
    01方案概述我们的全球母语译员团队,即是资深的狂热游戏玩家,又拥有丰富的游戏本地化经验,可以针对不同平台发布的各种类型游戏,提供50+种语言的高质量一站式本地化服务,能够以完美贴切的本地化语言让全球玩家拥有同等沉浸式的游戏体验,让您的游戏作品吸引全球受众,并获得更多的忠实玩家......
  • 整顿职场?安全体系建设
    本文由ChatMoney团队出品00后整顿职场,职场到底怎么了?无压力、无忧虑的00后可以直接开整,那绝大部分打工人寒窗苦读、闯过高考,艰辛毕业,几轮面试杀入职场,结婚买房、上有老下有小,就活该再被割韭菜被整顿吗?整顿不如解决二字,针对职场痛点,我整理出相关系列,充分利用chatmoney全能知识......
  • 2061:【例1.2】梯形面积 题解
    题目描述在梯形中阴影部分面积是150平方厘米,求梯形面积。解题思路简单的数学题。三角形面积公式为\(S=\frac{ah}{2}\),反推可得\(h=\frac{2S}{a}\),将\(a=15cm\)和\(S=150cm^2\)代入公式\(h=\frac{2S}{a}\),解得\(h=20cm\),又由于\(h_{▲}=h_{梯形}\),所以\(h_{梯形}=h_{▲}=20c......