Spring AOP 概念及动态代理模式
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 概念及动态代理模式相关推荐
- AOP、ASPECT、Spring AOP、JDK动态代理、CGLib动态代理
AOP.ASPECT.Spring AOP.JDK动态代理.CGLib动态代理 1 AOP介绍 1.1 基本定义 AOP(Aspect Oriented Programming)称为面向切面编程,它是 ...
- Spring Boot实践——Spring AOP实现之动态代理
Spring AOP 介绍 AOP的介绍可以查看 Spring Boot实践--AOP实现 与AspectJ的静态代理不同,Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改 ...
- ❤️Spring的静态、动态代理模式
❤️Spring的静态/动态代理模式 为什么要学习代理模式,因为AOP的底层机制就是动态代理! 代理模式: 静态代理 动态代理 1.静态代理 静态代理角色分析 抽象角色 : 一般使用接口或者抽象类来实 ...
- 关于Spring AOP,除了动态代理、CGLIB,你还知道什么?
来源 | 草捏子 责编 | Carol 封图 | CSDN 付费下载于视觉中国 Spring 作为 Java 中最流行的框架,主要归功于其提供的 IOC 和 AOP 功能.本文将讨论 Spring A ...
- Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:
① JDK动态代理只提供接口的代理,不支持类的代理,要求被代理类实现接口.JDK动态代理的核心是InvocationHandler接口和Proxy类,在获取代理对象时,使用Proxy类来动态创建目标类 ...
- Spring——AOP操作 AspectJ动态代理方式
有几天没有学习spring了,今天继续学习尚硅谷spring aop,讲的挺详细的,推荐去看看. 文章目录 一.准备工作 1.导入AspectJ依赖 2.切入点表达式 二 .配置文件实现 1.创建类( ...
- Spring AOP基础—JDK动态代理
JDK动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler.其中InvocationHandler动态创建一个符合某一接口的实例,生成目标类 ...
- Spring AOP源码分析(四)Spring AOP的JDK动态代理
2019独角兽企业重金招聘Python工程师标准>>> 本篇文章将会介绍上一个例子中的源码执行情况,从中熟悉整个SpringAOP的一些概念和接口设计. 首先整个SpringAOP的 ...
- java Spring的AOP理解和动态代理
Spring的AOP OOP面向对象,允许开发者定义纵向的关系,但并不适用于定义横向的关系,会导致大量代码的重复,而不利于各个模块的重用. AOP,一般称为面向切面,作为面向对象的一种补充,用于将那些 ...
最新文章
- POJ - 3417 Network LCA+树上差分
- 南大和中大“合体”拯救手残党:基于GAN的PI-REC重构网络,“老婆”画作有救了 | 技术头条...
- android android:process=,Android app启动流程
- Apicloud自定义模块
- html使标签为正方形,W3C CSS测试答案
- linux安装向日葵命令行版
- 上下文无关文法(例题+计算)
- 计算机怎么发音乐,有哪些方法可以把歌曲发送到远方的朋友的手机上?
- 【Java】基础09
- HbuilderX使用方法
- 移动安全--37--说说Android软件壳
- 用matlab演奏周杰伦的《七里香》
- 关于morden c++ design中的kdl问题(singleton)的解决
- java break递归_如何利用Java递归解决“九连环”公式
- libcurl库使用详情、libcurl库的制作
- 毛边效果 html,详解Html5 Canvas画线有毛边解决方法
- 【异常】‘dependencies.dependency.(groupId:artifactId:type:classifier)‘ must be unique:duplicate declarat
- Matlab GUI编程技巧(九):详解 uitable 函数显示表格及相关操作(创建表用户界面)
- 上海落户计算机水平毕业研究生,2020年上海落户有哪些新规定?附研究生落户分值表!...
- 网站在微博上发布后打不开,怎么办?
热门文章
- Gis 之OpenLayers入门简介
- Call to undefined method Illuminate\Database\Query\Builder::trashed()
- 操作系统进程的软中断通信
- 一首诗概括单田芳的绝大部分作品
- 将Listbox的SelectedItem绑定到一个UserControl中
- 计算机与日常使用计算器的本质区别,计算器和计算机的本质区别是什么?
- 小伙伴们,赶紧,免费的视频托管。
- IDS(***检测)要“退休”了吗?
- Linux内存从0到1学习笔记(6.6,物理内存初始化之预留内存)
- 系统服务器 lis6.0,LIS系统条形码生成接口说明(Service)