AOP
概念
- AOP(Aspect Oriented Programing)面向切面编程,一种编程范式,隶属于软工范畴,指导开发者如何组织程序结构
- AOP弥补了OOP的不足,基于OOP基础之上进行横向开发
◆ OOP规定程序开发以类为主体模型,一切围绕对象进行,完成某个任务先构建模型
◆ AOP程序开发主要关注基于OOP开发中的共性功能,一切围绕共性功能进行,完成某个任务先构建可能遇到的所有共性功能(当所有功能都开发出来也就没有共性与非共性之分)
作用
- 伴随着AOP时代的降临,可以从各个行业的标准化、规范化开始入手,一步一步将所有共性功能逐一开
发完毕,最终以功能组合来完成个别业务模块乃至整体业务系统的开发 - 目标:将软件开发由手工制作走向半自动化/全自动化阶段,实现“插拔式组件体系结构”搭建
AOP优势
- 提高代码的可重用性
- 业务代码编码更简洁
- 业务代码维护更高效
- 业务功能扩展更便捷
AOP相关概念
- Joinpoint(连接点):就是方法
- Pointcut(切入点):就是挖掉共性功能的方法
- Advice(通知):就是共性功能,最终以一个方法的形式呈现
- Aspect(切面):就是共性功能与挖的位置的对应关系
- Target(目标对象):就是挖掉功能的方法对应的类产生的对象,这种对象是无法直接成最终工作的
- Weaving(织入):就是将挖掉的功能回填的动态过程
- Proxy(代理):目标对象无法直接完成工作,需要对其进行功能回填,通过创建原始对象的代理对象实现
- Introduction(引入/引介) :就是对原始对象无中生有的添加成员变量或成员方法
切入点表达式的组成
- 切入点描述的是某个方法
- 切入点表达式是一个快速匹配方法描述的通配格式,类似于正则表达式
关键字(访问修饰符 返回值 包名.类名.方法名(参数)异常名)
◆ 关键字:描述表达式的匹配模式(参看关键字列表)
◆ 访问修饰符:方法的访问控制权限修饰符
◆ 类名:方法所在的类(此处可以配置接口名称)
◆ 异常:方法定义中指定抛出的异常 - exp:
execution(public User com.itheima.service.UserService.findById(int))
切入点表达式——通配符
- /* :单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现
匹配com.itheima包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法
execution(public * com.itheima.*.UserService.find*(*))
- … :多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写
匹配com包下的任意包中的UserService类或接口中所有名称为findById的方法
execution(public User com..UserService.findById(..))
- +:专用于匹配子类类型
execution(* *..*Service+.*(..))
切入点表达式——逻辑运算符
- && :连接两个切入点表达式,表示两个切入点表达式同时成立的匹配
- || :连接两个切入点表达式,表示两个切入点表达式成立任意一个的匹配
- ! :连接单个切入点表达式,表示该切入点表达式不成立的匹配
AOP的通知类型
- 前置通知:原始方法执行前执行,如果通知中抛出异常,阻止原始方法运行,一般应用于数据校验
⚫ 名称:aop:before
⚫ 类型:标签
⚫ 归属:aop:aspect标签
⚫ 作用:设置前置通知
⚫ 说明:一个aop:aspect标签中可以配置多个aop:before标签
⚫ 基本属性:
◆ method :在通知类中设置当前通知类别对应的方法
◆ pointcut :设置当前通知对应的切入点表达式,与pointcut-ref属性冲突
◆ pointcut-ref :设置当前通知对应的切入点id,与pointcut属性冲突
⚫ 格式:
<aop:aspect ref="adviceId">
<aop:before method="methodName" pointcut="……"/>
</aop:aspect>
- 后置通知:原始方法执行后执行,无论原始方法中是否出现异常,都将执行通知;一般应用于现场清理
⚫ 名称:aop:after
⚫ 类型:标签
⚫ 归属:aop:aspect标签
⚫ 作用:设置后置通知
⚫ 说明:一个aop:aspect标签中可以配置多个aop:after标签
⚫ 基本属性:
◆ method :在通知类中设置当前通知类别对应的方法
◆ pointcut :设置当前通知对应的切入点表达式,与pointcut-ref属性冲突
◆ pointcut-ref :设置当前通知对应的切入点id,与pointcut属性冲突
⚫ 格式:
<aop:aspect ref="adviceId">
<aop:after method="methodName" pointcut="……"/>
</aop:aspect>
- 返回后通知:原始方法正常执行完毕并返回结果后执行,如果原始方法中抛出异常,无法执行;一般应用于返回值相关数据处理
⚫ 名称:aop:after-returning
⚫ 类型:标签
⚫ 归属:aop:aspect标签
⚫ 作用:设置返回后通知
⚫ 说明:一个aop:aspect标签中可以配置多个aop:after-returning标签
⚫ 基本属性:
◆ method :在通知类中设置当前通知类别对应的方法
◆ pointcut :设置当前通知对应的切入点表达式,与pointcut-ref属性冲突
◆ pointcut-ref :设置当前通知对应的切入点id,与pointcut属性冲突
⚫ 格式:
<aop:aspect ref="adviceId">
<aop:after-returning method="methodName" pointcut="……"/>
</aop:aspect>
- 抛出异常后通知:原始方法抛出异常后执行,如果原始方法没有抛出异常,无法执行;一般应用于对原始方法中出现的异常信息进行处理
⚫ 名称:aop:after-throwing
⚫ 类型:标签
⚫ 归属:aop:aspect标签
⚫ 作用:设置抛出异常后通知
⚫ 说明:一个aop:aspect标签中可以配置多个aop:after-throwing标签
⚫ 基本属性:
◆ method :在通知类中设置当前通知类别对应的方法
◆ pointcut :设置当前通知对应的切入点表达式,与pointcut-ref属性冲突
◆ pointcut-ref :设置当前通知对应的切入点id,与pointcut属性冲突
⚫ 格式:
<aop:aspect ref="adviceId">
<aop:after-throwing method="methodName" pointcut="……"/>
</aop:aspect>
- 环绕通知:在原始方法执行前后均有对应执行执行,还可以阻止原始方法的执行;一般应用于十分强大,可以做任何事情
⚫ 名称:aop:around
⚫ 类型:标签
⚫ 归属:aop:aspect标签
⚫ 作用:设置环绕通知
⚫ 说明:一个aop:aspect标签中可以配置多个aop:around标签
⚫ 基本属性:
◆ method :在通知类中设置当前通知类别对应的方法
◆ pointcut :设置当前通知对应的切入点表达式,与pointcut-ref属性冲突
◆ pointcut-ref :设置当前通知对应的切入点id,与pointcut属性冲突
⚫ 格式:
<aop:aspect ref="adviceId">
<aop:around method="methodName" pointcut="……"/>
</aop:aspect>
环绕通知开发方式
- 环绕通知是在原始方法的前后添加功能,在环绕通知中,存在对原始方法的显式调用
public Object around(ProceedingJoinPoint pjp) throws Throwable {
Object ret = pjp.proceed();
return ret;
}
- 环绕通知方法相关说明:
◆ 方法须设定Object类型的返回值,否则会拦截原始方法的返回。如果原始方法返回值类型为
void,通知方法也可以设定返回值类型为void,最终返回null
◆ 方法需在第一个参数位置设定ProceedingJoinPoint对象,通过该对象调用proceed()方法,实
现对原始方法的调用。如省略该参数,原始方法将无法执行
◆ 使用proceed()方法调用原始方法时,因无法预知原始方法运行过程中是否会出现异常,强制抛
出Throwable对象,封装原始方法中可能出现的异常信息
当同一个切入点配置了多个通知时,通知会存在运行的先后顺序,该顺序以通知配置的顺序为准
AOP开发过程
- 开发阶段(开发者完成)
◆ 正常的制作程序
◆ 将非共性功能开发到对应的目标对象类中,并制作成切入点方法
◆ 将共性功能独立开发出来,制作成通知
◆ 在配置文件中,声明切入点
◆ 在配置文件中,声明切入点与通知间的关系(含通知类型),即切面 - 运行阶段(AOP完成)
◆ Spring容器加载配置文件,监控所有配置的切入点方法的执行
◆ 当监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置将通知对应的功能织入,完成完整的代码逻辑并运行
通知获取参数数据
通知获取返回值数据
通知获取异常数据
AOP配置
- XML配置
⚫ 名称:aop:config
⚫ 类型:标签
⚫ 归属:beans标签
⚫ 作用:设置AOP
⚫ 说明:一个beans标签中可以配置多个aop:config标签
⚫ 格式:
<beans>
<aop:config>……</aop:config>
<aop:config>……</aop:config>
</beans>
◆名称:aop:aspect
◆ 类型:标签
◆ 归属:aop:config标签
◆作用:设置具体的AOP通知对应的切入点
◆ 说明:一个aop:config标签中可以配置多个aop:aspect标签
◆ 基本属性:ref :通知所在的bean的id
◆ 格式:
<aop:config>
<aop:aspect ref="beanId">……</aop:aspect>
<aop:aspect ref="beanId">……</aop:aspect>
</aop:config>
⚫ 名称:aop:pointcut
⚫ 类型:标签
⚫ 归属:aop:config标签、aop:aspect标签
⚫ 作用:设置切入点
⚫ 说明:一个aop:config标签中可以配置多个aop:pointcut标签,且该标签可以配置在aop:aspect标签内
⚫ 基本属性:
◆ id :识别切入点的名称
◆ expression :切入点表达式
⚫ 格式:
<aop:config>
<aop:pointcut id="pointcutId" expression="……"/>
<aop:aspect>
<aop:pointcut id="pointcutId" expression="……"/>
</aop:aspect>
</aop:config>
注解开发AOP制作步骤
-
在XML格式基础上
◆ 导入坐标(伴随spring-context坐标导入已经依赖导入完成)
◆ 开启AOP注解支持
◆ 配置切面@Aspect
◆ 定义专用的切入点方法,并配置切入点@Pointcut
◆ 为通知方法配置通知类型及对应切入点@Before -
注意事项
- 切入点最终体现为一个方法,无参无返回值,无实际方法体内容,但不能是抽象方法
- 引用切入点时必须使用方法调用名称,方法后面的()不能省略
- 切面类中定义的切入点只能在当前类中使用,如果想引用其他类中定义的切入点使用“类名.方法名()”引用
- 可以在通知类型注解后添加参数,实现XML配置中的属性,例如after-returning后的returning属性
-
@Aspect
⚫ 类型:注解
⚫ 位置:类定义上方
⚫ 作用:设置当前类为切面类
⚫ 说明:一个beans标签中可以配置多个aop:config标签
⚫ 格式:
@Aspect
public class AopAdvice {
}
- @Pointcut
⚫ 类型:注解
⚫ 位置:方法定义上方
⚫ 作用:使用当前方法名作为切入点引用名称
⚫ 说明:被修饰的方法忽略其业务功能,格式设定为无参无返回值的方法,方法体内空实现(非抽象)
⚫ 格式:
@Pointcut("execution(* *(..))")
public void pt() {
}
- @Before
⚫类型:注解
⚫ 位置:方法定义上方
⚫ 作用:标注当前方法作为前置通知
⚫ 格式:
@Before("pt()")
public void before(){
}
- @After
⚫ 类型:注解
⚫ 位置:方法定义上方
⚫ 作用:标注当前方法作为后置通知
⚫ 格式:
@After("pt()")
public void after(){
}
- @AfterReturning
⚫ 类型:注解
⚫ 位置:方法定义上方
⚫ 作用:标注当前方法作为返回后通知
⚫ 特殊参数:
◆ returning :设定使用通知方法参数接收返回值的变量名
⚫ 格式:
@AfterReturning(value="pt()",returning = "ret")
public void afterReturning(Object ret) {
}
- @AfterThrowing
⚫ 类型:注解
⚫ 位置:方法定义上方
⚫ 作用:标注当前方法作为异常后通知
⚫ 特殊参数:
◆ throwing :设定使用通知方法参数接收原始方法中抛出的异常对象名
⚫ 格式:
@AfterThrowing(value="pt()",throwing = "t")
public void afterThrowing(Throwable t){
}
- @Around
⚫ 类型:注解
⚫ 位置:方法定义上方
⚫ 作用:标注当前方法作为环绕通知
⚫ 格式:
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
Object ret = pjp.proceed();
return ret;
}
AOP注解驱动
⚫ 名称:@EnableAspectJAutoProxy
⚫ 类型:注解
⚫ 位置:Spring注解配置类定义上方
⚫ 作用:设置当前类开启AOP注解驱动的支持,加载AOP注解
⚫ 格式:
@Configuration
@ComponentScan("com.itheima")
@EnableAspectJAutoProxy
public class SpringConfig {
}