大家好,欢迎来到IT知识分享网。
GitHub:https://github.com/JDawnF/learning_note
目录
1、简介
AOP(Aspect-Oriented Programming),即面向切面编程, 它与 OOP( Object-Oriented Programming, 面向对象编程) 相辅相成, 提供了与 OOP 不同的抽象软件结构的视角。
- 在 OOP 中,以类( Class )作为基本单元
- 在 AOP 中,以切面( Aspect )作为基本单元。
所谓”切面”,简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
使用”横切”技术,AOP 把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP 的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。
1.1 应用场景
- Authentication 权限
- Caching 缓存
- Context passing 内容传递
- Error handling 错误处理
- Lazy loading 懒加载
- Debugging 调试
- logging, tracing, profiling and monitoring 记录跟踪 优化 校准
- Performance optimization 性能优化
- Persistence 持久化
- Resource pooling 资源池
- Synchronization 同步
- Transactions 事务
2、Aspect简介
Aspect 由 PointCut 和 Advice 组成。
- 它既包含了横切逻辑的定义,也包括了连接点的定义。
- Spring AOP 就是负责实施切面的框架,它将切面所定义的横切逻辑编织到切面所指定的连接点中。
AOP 的工作重心在于如何将增强编织目标对象的连接点上, 这里包含两个工作:
- 如何通过 PointCut 和 Advice 定位到特定的 JoinPoint 上。
- 如何在 Advice 中编写切面代码。
可以简单地认为, 使用 @Aspect 注解的类就是切面
3、AOP相关术语
Joinpoint(连接点):
所谓连接点是指那些被拦截到的点。在Spring中,这些点指的是方法,因为Spring只支持方法类型的连接点。(实际上连接点还可以是字段或者构造器)
Pointcut(切入点):
所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。
两者区别:
首先,Advice 通过 PointCut 查询需要被织入的 JoinPoint 。
然后,Advice 在查询到 JoinPoint 上执行逻辑。
Advice(通知/增强):
所谓通知是指拦截到Joinpoint之后所要做的事情就是通知,通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
- Before – 这些类型的 Advice 在 JoinPoint 方法之前执行,并使用
@Before
注解标记进行配置。 - After Returning – 这些类型的 Advice 在连接点方法正常执行后执行,并使用
@AfterReturning
注解标记进行配置。 - After Throwing – 这些类型的 Advice 仅在 JoinPoint 方法通过抛出异常退出并使用
@AfterThrowing
注解标记配置时执行。 - After Finally – 这些类型的 Advice 在连接点方法之后执行,无论方法退出是正常还是异常返回,并使用
@After
注解标记进行配置。 - Around – 这些类型的 Advice 在连接点之前和之后执行,并使用
@Around
注解标记进行配置。
Introduction(引介):
引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field。
Target(目标对象):
织入 Advice 的目标对象,代理的目标对象
- 因为 Spring AOP 使用运行时代理的方式来实现 Aspect ,因此 Advised Object 总是一个代理对象(Proxied Object) 。
- 注意, Advised Object 指的不是原来的对象,而是织入 Advice 后所产生的代理对象。
- Advice + Target Object = Advised Object = Proxy 。
Weaving(织入):
是指把增强应用到目标对象来创建新的代理对象的过程。Spring采用动态代理织入,而AspectJ采用编译期织入和类装在期织入。
Proxy(代理):
一个类被AOP织入增强后,就产生一个结果代理类。
Aspect(切面):
是切入点和通知(引介)的结合
4、AOP实现方式
实现 AOP 的技术,主要分为两大类:
- ① 静态代理 – 指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强。
- 编译时编织(特殊编译器实现)
- 类加载时编织(特殊的类加载器实现)。
- ② 动态代理 – 在运行时在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强。目前 Spring 中使用了两种动态代理库:
- JDK 动态代理
- CGLIB
From 《Spring 源码深度解析》P172
Spring AOP 部分使用 JDK 动态代理或者 CGLIB 来为目标对象创建代理。(建议尽量使用 JDK 的动态代理)
如果被代理的目标对象实现了至少一个接口,则会使用 JDK 动态代理。所有该目标类型实现的接口都将被代理。
若该目标对象没有实现任何接口,则创建一个 CGLIB 代理。
如果你希望强制使用 CGLIB 代理,(例如希望代理目标对象的所有方法,而不只是实现自接口的方法)那也可以。但是需要考虑以下两个方法: 1> 无法通知(advise) Final 方法,因为它们不能被覆盖。 2> 你需要将 CGLIB 二进制发型包放在 classpath 下面。
为什么 Spring 默认使用 JDK 的动态代理呢?笔者猜测原因如下: 1> 使用 JDK 原生支持,减少三方依赖 2> JDK8 开始后,JDK 代理的性能差距 CGLIB 的性能不会太多。可参见:https://www.cnblogs.com/haiq/p/4304615.html
或者,我们来换一个解答答案:
Spring AOP 中的动态代理主要有两种方式,
- JDK 动态代理(接口)
JDK 动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是 InvocationHandler 接口和 Proxy 类。
JDK 动态代理主要涉及到 java.lang.reflect 包中的两个类:Proxy 和 InvocationHandler。InvocationHandler 是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一起。Proxy 利用 InvocationHandler 动态创建一个符合某一接口的实例,生成目标类的代理对象。
- CGLIB 动态代理(类)
如果目标类没有实现接口,那么 Spring AOP 会选择使用 CGLIB 来动态代理目标类。当然,Spring 也支持配置,强制使用 CGLIB 动态代理,可以在运行期扩展 Java 类与实现 Java 接口,CGLib 封装了 asm,可以再运行期动态生成新的 class。
CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB 是通过继承的方式做的动态代理,因此如果某个类被标记为
final
,那么它是无法使用 CGLIB 做动态代理的。
5、AspectJ
切点的定义:
@Pointcut("execution(* cn.itcast.spring3.demo1.UserDao.find(..))") private void myPointcut(){}
AspectJ表达式:
* 语法:execution(表达式)
execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)
* execution(“* cn.itcast.spring3.demo1.dao.*(..)”) —只检索当前包
* execution(“* cn.itcast.spring3.demo1.dao..*(..)”) —检索包及当前包的子包.
* execution(* cn.itcast.dao.GenericDAO+.*(..)) —检索GenericDAO及子类
AspectJ增强:
@Before 前置通知,相当于BeforeAdvice
@AfterReturning 后置通知,相当于AfterReturningAdvice
@Around 环绕通知(前后到通知),相当于MethodInterceptor
@AfterThrowing 抛出通知,相当于ThrowAdvice
@After 最终final通知,不管是否异常,该通知都会执行
@DeclareParents 引介通知,相当于IntroductionInterceptor
具体例子可参照:彻底征服 Spring AOP 之 实战篇
@Aspect public class TransactionDemo { @Pointcut(value = "execution(* com.yangxin.core.service.*.*.*(..))") public void point() { } @Before(value = "point()") public void before() { System.out.println("transaction begin"); } @AfterReturning(value = "point()") public void after() { System.out.println("transaction commit"); } @Around("point()") public void around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("transaction begin"); joinPoint.proceed(); System.out.println("transaction commit"); } }
参照:芋道源码
黑马
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/156925.html