Spring AOP底层原理及代理模式

 更新时间:2022年05月30日 11:46:41   作者:把苹果咬哭的测试笔记  
这篇文章主要为大家介绍了Spring AOP底层原理及代理模式详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

Spring AOP底层原理代理模式

一、什么是 AOP

AOP 就是面向切面编程,是 OOP(面向对象编程)的延续。

利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序可用性,同时也提高了开发效率。

通俗一点说,不用修改原代码,可以给原代码增加新的功能。

二、AOP 底层原理

AOP 底层原理是使用动态代理。

那代理是什么?有动态代理,那是不是还有静态代理?

1. 什么是代理?

就是为一个目标对象提供一个代理对象,并由代理对象控制对目标对象的引用。使用代理对象,是为了在不修改目标对象的基础上,增强目标对象的业务逻辑。

比如目标对象 A,代理对象是 B。

  • 那么现在 B 对 A 进行引用,可以实现 A 有的功能。
  • 另外,B 还可以在自身进行一些新功能,最终不需要修改目标对象 A 。

而代理分为静态代理和动态代理,区别是:

静态代理有真实的代理类存在,就是我们会代码中创建一个代理类,并在代理类的方法中调用目标对象的方法,以此来完成代理的工作。动态代理的代理类没有在代码中创建一个代理类,而是在运行时在JVM里面创建代理对象。

2. 什么是静态代理

静态代理是有实实在在的代理类存在,并且和目标类实现相同的接口。

比如,有一个转账业务,现在希望给它增加功能,使在转账之前确认转账人身份,以及转账之后通知收款人。

(1) 接口 AccountServiceDao :

package com.pingguo.spring5.dao;
public interface AccountServiceDao {
    // 主业务逻辑,转账
    void transfer();
}

(2) 接口 AccountServiceDao 的实现类:

package com.pingguo.spring5.dao;
public class AccountServiceImpl implements AccountServiceDao {
    @Override
    public void transfer() {
        System.out.println("调用dao层,完成转账主业务.");
    }
}

(3) 代理类 AccountProxy :

package com.pingguo.spring5.proxy;
import com.pingguo.spring5.dao.AccountServiceDao;
public class AccountProxy implements AccountServiceDao {
    // 目标对象
    private AccountServiceDao target;
    public AccountProxy(AccountServiceDao target) {
        this.target = target;
    }
    /**
     *  代理方法,实现对目标方法的增强
     */
    @Override
    public void transfer() {
        before();
        target.transfer();
        after();
    }
    /**
     *  增强的功能,转账之前使用
     */
    private void before() {
        System.out.println("对转账人身份进行验证.");
    }
    /**
     *  增强的功能,转账之后使用
     */
    private void after() {
        System.out.println("转账完成,已通知收款人.");
    }
}

在代理类中:

  • 添加了添加了目标对象,并且有参构造方法里需要传入目标对象。
  • 代理方法里,调用了目标对象里的转账方法 target.transfer()。
  • before() 和 after() 则是 2个增强的方法,分别作用于 target.transfer() 的前面和后面。

(4) 运行测试新建一个测试方法,运行看下结果:

@Test
    public void testProxy() {
        // 创建目标对象
        AccountServiceDao target = new AccountServiceImpl();
        // 创建代理对象
        AccountProxy proxy = new AccountProxy(target);
        proxy.transfer();
    }
  • 这里先创建了目标对象
  • 再创建代理对象,并且把目标对象传入
  • 最后调用代理对象里的,被增强过的方法 transfer()。

结果:

对转账人身份进行验证.
调用dao层,完成转账主业务.
转账完成,已通知收款人.
Process finished with exit code 0

优点:

  • 效率高,因为所有的类都是已经编写完成的,使用的时候只需要取得代理对象并且执行即可。
  • 同时也可以实现对目标对象中指定的方法进行增强。

缺点:

  • 与目标类实现相同的接口代码,冗余。
  • 如果接口发生改变,代理类中的方法也要修改。
  • 代理类服务于一种类型的对象,如果要服务多类型的对象,那么要为每种类型的对象都生成代理类。

3. 什么是动态代理

与静态代理的硬编码方式相比,动态代理支持运行时动态生成代理对象这种方式。换句话说,动态代理并不存在代理类,代理对象直接由代理生成工具动态生成。

优点:

  • 用很少的代码对一个类的所有方法实现一样的增强效果。
  • 在编码时,代理逻辑与业务逻辑互相独立,各不影响,减少侵入,降低耦合。

缺点:

相对于静态代理,它不能增强其中的某一个方法。

对于动态代理,针对于是否存在接口的情况下,又分为 2 种:

  • 有接口的情况下,使用 JDK 动态代理。
  • 无接口的情况下,使用 CGLIB 动态代理。

使用 JDK 动态代理

使用 JDK 动态代理,创建的是接口实现类的代理对象,以此来实现功能增强。

现在不需要上面创建过的实际代理类了 。

接口,为了后面的一些知识点的说明,里面加个参数,转账的金额:

package com.pingguo.spring5.dao;
public interface AccountServiceDao {
    // 主业务逻辑,转账
    void transfer(int amount);
}

实现类:

package com.pingguo.spring5.dao;
public class AccountServiceImpl implements AccountServiceDao {
    @Override
    public void transfer(int amount) {
        System.out.println("调用dao层,完成转账主业务.金额:" + amount);
    }
}

在测试方法里,直接使用动态代理:

@Test
    public void testDynamicProxy() {
        // 创建目标对象
        AccountServiceDao target = new AccountServiceImpl();
        // 创建代理对象
        AccountServiceDao proxy = (AccountServiceDao) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),  // 目标类使用的类加载器
                target.getClass().getInterfaces(),  // 目标类实现的接口
                new InvocationHandler() {  // 调用处理器
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("对转账人身份进行验证.");
                        Object res =  method.invoke(target, args);
                        System.out.println("转账完成,已通知收款人.");
                        return res;
                    }
                }
        );
        // 让代理工作
        proxy.transfer(10000);
    }

运行结果:

对转账人身份进行验证.
调用dao层,完成转账主业务.金额:10000
转账完成,已通知收款人.
Process finished with exit code 0

动态代理的过程:

  • 创建处理器 InvocationHandler实例。
  • 在调用目标对象时,会调用代理对象。
  • 代理对象去请求目标对象。invoke 方法就是调用目标对象的方法生成代理对象的过程。
  • 同时,在 invoke 方法中进行功能增强。

对于 invoke 中的 3 个参数,分别是:

  • Object proxy:代理对象,一般不会使用。
  • Method method:外面的代理对象调用的方法引用,这里引用的就是 transfer()
  • Object[] args:外面的代理对象调用的方法里面的参数,这里就是参数 amount。

使用 CGLIB 动态代理

CGLIB动态代理的原理是生成目标类的子类,这个子类对象就是代理对象,代理对象是被增强过的。

注意,不管有没有接口都可以使用 CGLIB 动态代理, 而不是只有在无接口的情况下才能使用。

示例就暂时不放了,因为我本地环境问题,有个报错始终未解决,后续再说,不影响继续学习 spring。

以上就是Spring AOP底层原理及代理模式的详细内容,更多关于Spring AOP底层原理的资料请关注脚本之家其它相关文章!

相关文章

  • Java学习笔记之观察者模式

    Java学习笔记之观察者模式

    这篇文章主要为大家详细介绍了Java学习笔记之观察者模式的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-11-11
  • Java排序算法总结之插入排序

    Java排序算法总结之插入排序

    这篇文章主要介绍了Java排序算法总结之插入排序,较为详细的分析了插入排序的原理与java实现技巧,需要的朋友可以参考下
    2015-05-05
  • SpringBoot中整合Minio文件存储的安装部署过程

    SpringBoot中整合Minio文件存储的安装部署过程

    这篇文章主要介绍了SpringBoot整合Minio文件存储的相关知识,详细介绍了Minio安装部署过程,需要的朋友可以参考下
    2022-04-04
  • Java中线程状态+线程安全问题+synchronized的用法详解

    Java中线程状态+线程安全问题+synchronized的用法详解

    这篇文章主要介绍了Java中线程状态+线程安全问题+synchronized的用法详解,本文结合示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-04-04
  • 怎么运行用记事本写的java程序

    怎么运行用记事本写的java程序

    以下小编就为大家介绍一下,怎么运行用记事本写的java程序。需要的朋友可以过来参考下
    2013-08-08
  • springboot如何使用redis的incr创建分布式自增id

    springboot如何使用redis的incr创建分布式自增id

    这篇文章主要介绍了springboot如何使用redis的incr创建分布式自增id,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • Java分代垃圾回收策略原理详解

    Java分代垃圾回收策略原理详解

    这篇文章主要介绍了Java分代垃圾回收策略原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-03-03
  • java序列化与反序列化的使用方法汇总

    java序列化与反序列化的使用方法汇总

    序列化是一种对象持久化的手段,普遍应用在网络传输、RMI等场景中,这篇文章主要给大家总结介绍了关于java序列化与反序列化的使用方法,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2021-07-07
  • 在Java和PostgreSQL枚举之间转换的通用方法

    在Java和PostgreSQL枚举之间转换的通用方法

    枚举类型(enum)是一种方便的数据类型,允许我们指定一个常量列表,对象字段或数据库列可以设置为该列表中的值,在本文中,我将回顾处理Java和PostgreSQL枚举转换的通用方法,需要的朋友可以参考下
    2023-10-10
  • SpringBoot使用slf4j日志并输出到文件中的操作方法

    SpringBoot使用slf4j日志并输出到文件中的操作方法

    这篇文章主要介绍了SpringBoot使用slf4j日志并输出到文件中,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-08-08

最新评论