AOP介绍
-
什么是AOP
- 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面
- 作用:在不修改目标类代码的前提下,可以通过AOP技术增强目标类的功能,通过【预编译方式】和【运行期动态代理】实现程序功能的统一维护的一种技术
- AOP是一种编程范式,隶属于软工范畴,指导开发者如何组织程序结构
- AOP最早是由AOP联盟组织提出的,制定了一套规范,spring将AOP思路引入到框架中必须遵守AOP联盟规范
- AOP是OOP的延续,是软件开发中的一个热点,也是spring框架中的一个重要内容,是函数式编程的一种衍生范型
- 利用AOP可以对业务代码中【业务逻辑】和【系统逻辑】进行隔离,从而使得【业务逻辑】和【系统逻辑】之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
-
为什么使用AOP
- 作用:AOP采取横向抽取机制,补充了传统纵向继承体系(OOP)无法解决的重复性代码优化(性能监视、事务管理、安全检查、缓存),将业务逻辑和系统处理代码(关闭连接、事务管理、操作日志记录)解耦
- 优势:重复性代码被抽取出来之后,维护更加方便
- 纵向继承体系
- 横向抽取机制
AOP相关术语
术语解释
-
Joinpoint(连接点)
连接点是指那些被拦截到的点,在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
-
Pointcut(切入点)
切入点是指我们要对哪些Joinpoint进行拦截的定义
-
Advice(通知/增强)
通知是指拦截到Joinpoint之后所要做的事情,通知分为前置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
-
Introduction(引介)
引介是一种特殊的通知在不修改类代码的前提下,Introduction可以在运行期为类动态地添加一些方法或Field
-
Target(目标对象)
代理的目标对象
-
Weaving(织入)
把通知应用到目标对象来创建新的代理对象的过程
-
Proxy(代理)
一个类被AOP织入通知后,就产生一个结果代理类
-
Aspect
是切入点和通知的结合
-
Advisor(通知器、顾问)
和Aspect很相似
AOP之AspectJ
-
AspectJ是一个Java实现的AOP框架,它能够对Java代码进行AOP编译(一般在编译期进行),让Java代码具有AspectJ的AOP功能(需要特殊的编译器)
-
AspectJ是目前实现AOP框架中最成熟,功能最丰富的语言。AspectJ与Java程序完全兼容,几乎是无缝关联
-
AspectJ应用到Java代码的过程(这个过程称为织入),对于织入这个概念,可以简单理解为aspect(切面)应用到目标函数(类)的过程
-
对于织入这个过程,一般分为动态织入和静态织入,动态织入的方式是在运行时动态将要增强的代码织入带目标类中,这样往往是通过动态代理技术完成的,如Java JDK的动态代理(Proxy,底层通过反射实现)或者CGLIB的动态代理(底层通过继承实现),Spring AOP采用的就是基于运行是增强代理技术
-
AspectJ采用的就是静态织入的方式,AspectJ主要采用的是编译期织入,在这个期间使用AspectJ的acj编译器(类似javac)把aspect类编译成class字节码后,在Java目标类编译时织入,即先编译aspect类再编译目标类
AOP实现之Spring AOP
-
JDK动态代理
-
CGLIB动态代理
-
使用
-
使用ProxyFactoryBean创建
-
使用aop:advisor定义通知器的方式,实现AOP则需要通知累实现Advice接口
-
增强(通知)的类型有:
-
前置通知:org.springframework.aop.MethodBeforeAdvice
-
后置通知:org.springframework.aop.AfterReturningAdvice
-
环绕通知:org.aopalliance.intercept.MethodInterceptor
-
异常通知:org.springframework.aop.ThrowsAdcice
-
-
基于AspectJ的AOP使用
其实就是指Spring + AspectJ整合,不过spring已将AspectJ收录到自身的框架中了,并且底层织入采取的是动态织入方式
- 添加依赖
-
编写目标类和目标方法
- 编写接口和实现类(目标类)
UserService接口 UserServiceImpl实现类- 配置目标类,将目标类交给spring
<context:component-scan base-package="com.XXX.XXX" />
使用XML实现
- 编写通知(增强类)
- 配置通知,将通知类交给spring IoC容器管理
- 配置AOP切面
-
切入点表达式
-
切入点表达式的格式
execution([修饰符] 返回值类型 包名.类名.方法名(参数)) -
表达式格式说明
-
execution:必须要
-
修饰符:可省略
-
返回值类型:必须要,但是可以使用*通配符
-
包名
- 多级包之间使用.分割
- 包名可以使用代替,多级包可以使用多个代替
- 如果想省略中间的包名可以使用 ..
-
类名
- 可以使用*代替
- 也可以写出*Impl
-
方法名
- 可以使用*代替
- 也可以写成add*
-
参数
- 参数可以使用*代替
- 如果有多个参数,可以使用 ..代替
-
-
-
通知类型
通知类型(五种):前置通知、后置通知、最终通知、环绕通知、异常抛出通知
-
前置通知
- 执行时机:目标对象方法之前执行通知
- 配置文件<aop:before mothod="brfore" pointcut-ref="myPointCut" />
- 应用场景:方法开始是可以进行校验
-
后置通知
- 执行时机:目标对象方法之后执行通知,有异常则不执行了
- 配置文件<aop:after-returning mothod="afterReturning" pointcut-ref="myPointCut" />
- 应用场景:可以修改方法的返回值
-
最终通知
- 执行时机:目标对象方法之后执行通知,有没有异常都会执行
- 配置文件<aop:after mothod="after" pointcut-ref="myPointCut" />
- 应用场景:可以释放资源
-
环绕通知
- 执行时机:目标对象方法之前和之后都会执行
- 配置文件<aop:around mothod="around" pointcut-ref="myPointCut" />
- 应用场景:事物,统计代码执行时机
-
异常抛出通知
- 执行时机:在抛出异常后通知
- 配置文件<aop:after-throwing mothod="afterThrowing" pointcut-ref="myPointCut" />
- 应用场景:包装异常
-
使用注解方式实现
- 编写切面类(注意不是通知类,因为该类中可以指定切入点)
-
配置切面类
<context:component-scan base-package="com.XXX.XXX.aop"></context:component-scan> -
开启AOP自动代理
<!-- 开启aspectj的自动代理,用于AOP的注解方式 --> <aop:aspectj-autoproxy/>