Spring AOP 概念及动态代理模式

文章目录

  • Spring AOP 概念及动态代理模式
    • 1 AOP 的概念及相关术语
    • 2 AOP 作用
    • 3 AOP 原理概述
      • 3.1 JDK 动态代理(Proxy)
      • 3.2 CGLIB 动态代理(Enhancer)
    • 4 入门案例
      • 4.1 创建 maven 工程,导入相关依赖
      • 4.2 创建被代理的接口及其实现类
      • 4.3 配置 AOP
      • 4.4 测试类

1 AOP 的概念及相关术语

Spring 官方解释:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#aop

  面向切面编程(Aspect-oriented Programming ,AOP)通过提供另一种思考程序设计结构的方式来补充面向对象程序设计(Object-oriented Programming,OOP)。面向对象编程中,模块化的关键单元是类,而在面向切面编程中,模块化的单元是方面。方面支持对关注点的模块化(比如事务管理)跨多个类型和对象。(这种关注在 AOP 文献中通常被称为 “跨领域”关注)

  AOP 是 Spring 的一个关键组件,尽管 Spring IoC 容器不依赖于 AOP(意味着你不需要用AOP的话,可以不使用),但 AOP 是对 Spring IoC 的补充,提供了功能强大的中间件解决方案。

也可以去看看:百度百科

相关术语:

术语 描述
连接点(Join point) 指的是那些被拦截到的点。在 Spring 中,这些点指的是方法,因为 Spring 只支持方法类型的连接点。
切入点(Point Cut) 指的是我们要对哪些 Join point 进行拦截的定义。被增强的连接点就叫做切入点,切入点一定是连接点,连接点不一定是切入点。
通知 / 增强(Advice) 指拦截到 Join Point 之后要执行的操作。类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
引介(Introduction) 是一种特殊的通知,可以为类动态地添加一些方法 或 Filed。
目标对象(Target object) 代理的目标对象。
织入(Weaving) 指把增强应用到的目标对象来创建新的代理对象的过程。 spring 采用 动态织入,而 AspectJ 采用编译期织入和类装载期织入。
AOP 代理(AOP proxy) 一个类被 AOP 织入增强后,就产生了一个结果代理类。
切面(Aspect) 是切入点和通知 / 引介 的结合。建立切入点方法和通知方法在调用执行时的对应关系就是一个切面

2 AOP 作用

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

通俗描述:不修改源代码的条件下,在主干功能中添加新功能。

案例解释:
  现在有一个账户服务类( AccountService ),类中有一个转账的方法( transferAccounts )。顾名思义,转账操作就是要将一个账户的钱转给另一个账户,这个过程需要查询两次,修改余额两次。如果一个账户在转账的过程中出现异常,导致转账人的钱减少了,而收款人的钱没有到账的情况。这种情况可以通过添加事务来解决,可以在源代码上直接做处理,但如果有很多个类似的方法呢?每个方法都加上事务管理,代码无疑会变得十分冗余,这时就可以使用一个代理类(AccountProxy),将 AccountService 类中所有需要事务管理的方法都拦截放到添加事务管理的方法中,这样就可以做的不修改源代码的条件下,增强功能。这就是 AOP 的过程。

在 Spring 中用 AOP 术语匹配以上案例:

  • 切面(Aspect):代理类就是一个切面,里面有代理增强的方法。
  • 连接点(Join point):被代理对象 AccountService 中被拦截到的方法就叫做连接点。
  • 切入点(Point Cut):被代理对象 AccountService 中被拦截且增强的方法就叫做切入点。(切入点一定是连接点,连接点不一定是切入点。)
  • 通知 / 增强(Advice):添加的事务管理功能就叫做通知或增强。
  • 目标对象(Target object) :指得就是被代理的对象 AccountService 。
  • 织入(Weaving):在代理类 AccountProxy 中为被代理对象 AccountService 添加事务管理功能的过程就叫做织入。
  • AOP 代理(AOP proxy):指的就是 AccountService 的代理对象 AccountProxy。

3 AOP 原理概述

AOP 底层使用到了动态代理模式:

  • 特点:字节码文件随用随创建,随用随加载。
  • 作用:不修改源码的基础上对方法增强。
  • 分类:基于接口的动态代理和基于子类的动态代理。
    • 第一种,基于接口的动态代理,使用 JDK 实现动态代理。
    • 第二种,基于子类的动态代理,使用第三方 CGLIB 库实现动态代理。

菜鸟教程-代理模式

简单理解代理模式:用户购买电脑不是到生产厂家处购买,而是到经销商处购买,经销商又从生产厂家取货。倘若用户电脑出问题了,找的是经销商的售后,经销商售后要么自己处理,要么返还给生产厂家处理,这个过程就叫做代理。


3.1 JDK 动态代理(Proxy)

这是基于接口的动态代理方式,是由 JDK 提供。

使用类和方法:java.lang.reflect.Proxy.newProxyInstance

要求被代理对象必须实现至少一个接口,否则出现以下异常:

java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to xxx

可参考:Proxy API介绍


newProxyInstance参数解读:

  • ClassLoader loader :代理类的类加载器,使用 xxx.class.getClassLoader() 获取类加载器,xxx表示代理对象。
  • Class<?>[] interfaces : 被代理对象的所有接口,使用 xxx.class.getInterfaces() 获取被代理对象的所有接口,xxx 表示被代理对象。目的是让代理对象与被代理对象有相同的方法。
  • InvocationHandler h : 调用代理对象方法的控制器接口,可以在这个接口的实现类中写增强方法的代码。里面有一个唯一的方法 invoke() 需要被重写。通常是匿名内部类的形式。

nvocationHandler 接口中的 invoke 方法:用于控制代理实例上的方法调用和返回结果。


invoke 方法参数解读:

  • Object proxy: 代理对象,方法被调用的地方。
  • Method method: 被代理对象中的方法,可以使用 method.getName() 方法获取当前执行方法的名称。
  • Object[] args: 被代理对象中方法的参数列表,可使用索引的方式获取参数,如args[0] 表示当前执行方法的第一个参数。

代理过程演示:

1 创建接口,定义相关方法

//被代理对象,模拟播放人类从匍匐到奔跑的过程的视频
public interface Person {public void walk();
}

2 创建接口实现类并实现方法

public class PersonImpl implements Person{public void walk(){System.out.println("视频播放:人类行走过程。。。");// int a = 1/0;        //手动制造异常,默认在观看人类行走时产生疑惑}
}

3 使用 Proxy 类创建接口代理对象。

public class PersonProxy {// 创建被代理对象PersonImpl personImpl = new PersonImpl();// 返回代理对象的方法public Object personProxy() {return Proxy.newProxyInstance(PersonProxy.class.getClassLoader(),PersonImpl.class.getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println(proxy.getClass());try {Object obj = null;System.out.println("视频播放:人类匍匐过程。。。");     // 被增强方法执行前的动作obj = method.invoke(personImpl, args);          // 被增强方法被明确调用,保证原有的功能System.out.println("视频播放:人类奔跑奔跑。。。");     // 被增强方法执行后执行的动作return obj;} catch (Exception e) {System.out.println("观众产生疑问。。。");            //被增强方法执行过程中出现异常执行的动作throw new RuntimeException(e);} finally {System.out.println("视频播放结束。。。");         // 无论方法是否正常执行,都会执行的动作}}});}
}

4 测试方法

public static void main(String[] args){PersonProxy pp = new PersonProxy();Person p = (Person) pp.personProxy();p.walk();
}

打印结果:

class com.sun.proxy.$Proxy0
视频播放:人类匍匐过程。。。
视频播放:人类行走过程。。。
视频播放:人类奔跑奔跑。。。
视频播放结束。。。

3.2 CGLIB 动态代理(Enhancer)

这是基于继承方式的动态代理,是由第三方 cglib 库提供。

使用的类和方法:cglib.proxy.Enhancer.create

它要求被代理对象不能是最终类(无法继承),否则出现以下异常;

java.lang.IllegalArgumentException: Cannot subclass final class xxx

可参考以下博客:

CGLIB原理及实现机制
CGLib动态代理的介绍及用法(单回调、多回调、不处理、固定值、懒加载)


create 方法参数解读:

  • Class type :被代理对象的字节码文件。
  • Callback callback :回调函数,可以使用这个接口的子接口提供的实现类中写增强方法的代码。与 JDK 动态代理中的 InvocationHandler 大同小异,这里是用于拦截方法的,所以用到的是方法拦截接口:MethodInterceptor ,接口中有唯一且需要被重写方法 intercept()

intercept() 方法参数解读:

  • Object o :被代理对象。
  • Method method:拦截到的方法
  • Object[] objects:被拦截方法的参数数组,基本类型会被包装成包装类型。
  • MethodProxy methodProxy:用于调用父类中未被拦截的方法的代理。

代理过程演示:

1 创建被代理类

public class Person {public void walk() {System.out.println("视频播放:人类行走前进中。。。");// int a = 1/0;        //手动制造异常,默认在观看人类行走时产生疑惑}
}

2 创建代理对象

public class PersonProxy {Person person = new Person();public Object getPersonProxy(){return Enhancer.create(person.getClass(), new MethodInterceptor() {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println(o.getClass());try{Object obj = null;System.out.println("视频播放:人类匍匐前进中。。。");       // 被增强方法执行前的动作obj = method.invoke(person,objects);                   // 被增强方法被明确调用,保证原有的功能System.out.println("视频播放:人类奔跑前进中。。。");     // 被增强方法执行后执行的动作return obj;}catch (Exception e){System.out.println("观众产生疑问。。。");          //被增强方法执行过程中出现异常执行的动作throw new RuntimeException(e);}finally {System.out.println("视频播放结束。。。");         // 无论方法是否正常执行,都会执行的动作}}});}
}

3 测试方法

public static void main(String[] args) {PersonProxy pp = new PersonProxy();Person p = (Person)pp.getPersonProxy();p.walk();
}

打印结果:

class main.a_proxy.cglib.Person$$EnhancerByCGLIB$$7f3ac652
视频播放:人类匍匐前进中。。。
视频播放:人类行走前进中。。。
视频播放:人类奔跑前进中。。。
视频播放结束。。。

以上动态代理内容作为了解即可,下面用 Spring 中的 AOP 完成上面代理的过程。


4 入门案例

4.1 创建 maven 工程,导入相关依赖

<!--Spring 核心-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.8.RELEASE</version>
</dependency>
<!--解析切点表达式-->
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.5</version>
</dependency>

4.2 创建被代理的接口及其实现类

public interface AccountService {    void saveAccount();         //模拟保存    void updateAccount(int id);   //模拟更新    int deleteAccount();      //模拟删除
}
public class AccountServiceImpl implements AccountService {@Overridepublic void saveAccount() {System.out.println("保存方法执行了。。。");       // int a = 1/0; //制造异常通知}@Overridepublic void updateAccount(int id) {System.out.println("更新方法执行了。。。");}@Overridepublic int deleteAccount() {System.out.println("删除方法执行了。。。");return 0;}
}

4.3 配置 AOP

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><!--配置 Service 对象实例化--><bean id="accountService" class="com.jk.xml.service.impl.AccountServiceImpl"/><!--1 配置通知类--><bean id="aopUtil" class="com.jk.xml.utils.AopUtil"/><aop:config><!--2 配置切面--><aop:aspect id="aopAdvice" ref="aopUtil"><!--3 配置前置通知,并关联 通知(advice) 和 切入点(point cut)(切入点表达式)--><aop:before method="beforeAdvice" pointcut="execution(int com.jk.xml.service.impl.*.*(..))"/></aop:aspect></aop:config>
</beans>
  • 要配置 AOP 需要引入 aop 名称空间。
  • 配置切入点时用到的切入点表达式,如果看不懂没关系,下一篇博客会详细介绍切入点表达式。

4.4 测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:xml/bean.xml"})
public class TestBean1 {@AutowiredAccountService as;//测试通知配置@Testpublic void testBeforeAdvice(){as.saveAccount();as.updateAccount(1);as.deleteAccount();}
}

这里使用 Spring 整合了 Junit 测试单元,关于配置可以看这篇博客:Spring 整合 JUnit 测试单元

执行结果:

保存方法执行了。。。
更新方法执行了。。。
<==before,前置通知加上了==>
删除方法执行了。。。
  • 我这里的切入点表示式指定的是 service 实现类中返回值为 int 的方法,所以只有 int deleteAccount() 被增强了。

以上就是关于 Spring AOP 的概念和动态代理模式的介绍,下一篇将会详细介绍切入点表达式、基于 xml 配置实现 AOP,基于注解实现 AOP,基于完全注解的方式实现 AOP,博客连接:

Spring AOP 的配置及使用

Spring AOP 概念及动态代理模式相关推荐

  1. AOP、ASPECT、Spring AOP、JDK动态代理、CGLib动态代理

    AOP.ASPECT.Spring AOP.JDK动态代理.CGLib动态代理 1 AOP介绍 1.1 基本定义 AOP(Aspect Oriented Programming)称为面向切面编程,它是 ...

  2. Spring Boot实践——Spring AOP实现之动态代理

    Spring AOP 介绍 AOP的介绍可以查看 Spring Boot实践--AOP实现 与AspectJ的静态代理不同,Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改 ...

  3. ❤️Spring的静态、动态代理模式

    ❤️Spring的静态/动态代理模式 为什么要学习代理模式,因为AOP的底层机制就是动态代理! 代理模式: 静态代理 动态代理 1.静态代理 静态代理角色分析 抽象角色 : 一般使用接口或者抽象类来实 ...

  4. 关于Spring AOP,除了动态代理、CGLIB,你还知道什么?

    来源 | 草捏子 责编 | Carol 封图 | CSDN 付费下载于视觉中国 Spring 作为 Java 中最流行的框架,主要归功于其提供的 IOC 和 AOP 功能.本文将讨论 Spring A ...

  5. Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:

    ① JDK动态代理只提供接口的代理,不支持类的代理,要求被代理类实现接口.JDK动态代理的核心是InvocationHandler接口和Proxy类,在获取代理对象时,使用Proxy类来动态创建目标类 ...

  6. Spring——AOP操作 AspectJ动态代理方式

    有几天没有学习spring了,今天继续学习尚硅谷spring aop,讲的挺详细的,推荐去看看. 文章目录 一.准备工作 1.导入AspectJ依赖 2.切入点表达式 二 .配置文件实现 1.创建类( ...

  7. Spring AOP基础—JDK动态代理

    JDK动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler.其中InvocationHandler动态创建一个符合某一接口的实例,生成目标类 ...

  8. Spring AOP源码分析(四)Spring AOP的JDK动态代理

    2019独角兽企业重金招聘Python工程师标准>>> 本篇文章将会介绍上一个例子中的源码执行情况,从中熟悉整个SpringAOP的一些概念和接口设计. 首先整个SpringAOP的 ...

  9. java Spring的AOP理解和动态代理

    Spring的AOP OOP面向对象,允许开发者定义纵向的关系,但并不适用于定义横向的关系,会导致大量代码的重复,而不利于各个模块的重用. AOP,一般称为面向切面,作为面向对象的一种补充,用于将那些 ...

最新文章

  1. POJ - 3417 Network LCA+树上差分
  2. 南大和中大“合体”拯救手残党:基于GAN的PI-REC重构网络,“老婆”画作有救了 | 技术头条...
  3. android android:process=,Android app启动流程
  4. Apicloud自定义模块
  5. html使标签为正方形,W3C CSS测试答案
  6. linux安装向日葵命令行版
  7. 上下文无关文法(例题+计算)
  8. 计算机怎么发音乐,有哪些方法可以把歌曲发送到远方的朋友的手机上?
  9. 【Java】基础09
  10. HbuilderX使用方法
  11. 移动安全--37--说说Android软件壳
  12. 用matlab演奏周杰伦的《七里香》
  13. 关于morden c++ design中的kdl问题(singleton)的解决
  14. java break递归_如何利用Java递归解决“九连环”公式
  15. libcurl库使用详情、libcurl库的制作
  16. 毛边效果 html,详解Html5 Canvas画线有毛边解决方法
  17. 【异常】‘dependencies.dependency.(groupId:artifactId:type:classifier)‘ must be unique:duplicate declarat
  18. Matlab GUI编程技巧(九):详解 uitable 函数显示表格及相关操作(创建表用户界面)
  19. 上海落户计算机水平毕业研究生,2020年上海落户有哪些新规定?附研究生落户分值表!...
  20. 网站在微博上发布后打不开,怎么办?

热门文章

  1. Gis 之OpenLayers入门简介
  2. Call to undefined method Illuminate\Database\Query\Builder::trashed()
  3. 操作系统进程的软中断通信
  4. 一首诗概括单田芳的绝大部分作品
  5. 将Listbox的SelectedItem绑定到一个UserControl中
  6. 计算机与日常使用计算器的本质区别,计算器和计算机的本质区别是什么?
  7. 小伙伴们,赶紧,免费的视频托管。
  8. IDS(***检测)要“退休”了吗?
  9. Linux内存从0到1学习笔记(6.6,物理内存初始化之预留内存)
  10. 系统服务器 lis6.0,LIS系统条形码生成接口说明(Service)