spring_03_AOP
简介
-
AOP(Aspect Oriented Programing)面向切面编程,一种编程范式,属于软工范畴,指导开发者如何组织程序结构
-
AOP弥补了OOP的不足,基于OOP基础之上进行横向开发
- OOP规定程序开发以类为主体模型,一切围绕对象进行,完成某个任务先构建模型
- AOP程序开发主要关注基于OOP开发中的共性功能,一切围绕共性功能进行,完成某个任务先构建可能遇到的所有共性功能(当所有功能都开发出来也就没有共性与非共性之分)
-
目标:将软件开发由手工制作走向半自动化/全自动化阶段,实现“插拔式组件体系结构”搭建
-
优势
- 提高代码的可重用性
- 业务代码编码更简洁
- 业务代码维护更高效
- 业务功能扩展更便捷
AOP相关概念
- Joinpoint(连接点):就是方法
- Pointcut(切入点):就是挖掉共性功能的方法
- Advice(通知):就是共性功能,最终以一个方法的形式呈现
- Aspect(切面):就是共性功能与挖的位置的对应关系
- Target(目标对象):就是挖掉功能的方法对应的类产生的对象,这种对象是无法直接完成最终工作的
- Weaving(织入):就是将挖掉的功能回填的动态过程
- Proxy(代理):目标对象无法直接完成工作,需要对其进行功能回填,通过创建原始对象的代理对象实现
- Introduction(引入/引介) :就是对原始对象无中生有的添加成员变量或成员方法
AOP配置
-
aop:config标签
作用:设置AOP
-
aop:aspect标签
作用:设置具体的AOP通知对应的切入点
ref属性:通知所在的bean的id
-
aop:pointcut标签
作用:设置切入点
id属性:识别切入点的名称
expression属性:切入点表达式
-
切入点表达式,通配符
切入点描述的是某个方法
-
语法
关键字(访问修饰符 返回值 包名.类名.方法名(参数)异常名)
1 关键字:描述表达式的匹配模式(参看关键字列表)
2 访问修饰符:方法的访问控制权限修饰符
3类名:方法所在的类(此处可以配置接口名称)
4异常:方法定义中指定抛出的异常
-
通配符
1 * :单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现
execution(public * com.it.*.UserService.find*(*))
匹配com.it包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法
2 … :多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写
execution(public User com..UserService.findById(..))
匹配com包下的任意包中的UserService类或接口中所有名称为findById的方法
3 +:专用于匹配子类类型
execution(* *..*Service+.*(..))
-
逻辑运算符
&& :连接两个切入点表达式,表示两个切入点表达式同时成立的匹配
|| :连接两个切入点表达式,表示两个切入点表达式成立任意一个的匹配
! :连接单个切入点表达式,表示该切入点表达式不成立的匹配
-
切入点的三种配置方式
<aop:config>
<!--配置公共切入点-->
<aop:pointcut id="pt1" expression="execution(* *(..))"/>
<aop:aspect ref="myAdvice">
<!--配置局部切入点-->
<aop:pointcut id="pt2" expression="execution(* *(..))"/>
<!--引用公共切入点-->
<aop:before method="logAdvice" pointcut-ref="pt1"/>
<!--引用局部切入点-->
<aop:before method="logAdvice" pointcut-ref="pt2"/>
<!--直接配置切入点-->
<aop:before method="logAdvice" pointcut="execution(* *(..))"/>
</aop:aspect>
</aop:config>
五种通知类型
当同一个切入点配置了多个通知时,通知会存在运行的先后顺序,该顺序以通知配置的顺序为准
-
前置通知:原始方法执行前执行,如果通知中抛出异常,阻止原始方法运行
aop:before标签
- method属性::在通知类中设置当前通知类别对应的方法
- pointcut属性 :设置当前通知对应的切入点表达式,与pointcut-ref属性冲突
- pointcut-ref 属性:设置当前通知对应的切入点id,与pointcut属性冲突
应用:数据校验
-
后置通知:原始方法执行后执行,无论原始方法中是否出现异常,都将执行通知
aop:after标签(属性同上)
应用:现场清理
-
返回后通知:原始方法正常执行完毕并返回结果后执行,如果原始方法中抛出异常,无法执行
aop:after-returning标签(属性同上)
应用:返回值相关数据处理
-
抛出异常后通知:原始方法抛出异常后执行,如果原始方法没有抛出异常,无法执行
aop:after-throwing标签(属性同上)
应用:对原始方法中出现的异常信息进行处理
-
环绕通知:在原始方法执行前后均有对应执行执行,还可以阻止原始方法的执行
aop:around标签(属性同上)
应用:十分强大,可以做任何事情
<!----------------五种通知类型----------------->
<aop:config>
<aop:pointcut id="pt" expression="execution(* *..*(..))"/>
<aop:aspect ref="myAdvice">
<!--<aop:before method="before" pointcut-ref="pt"/>-->
<!--<aop:after method="after" pointcut-ref="pt"/>-->
<!--<aop:after-returning method="afterReturing" pointcut-ref="pt"/>--
<!--<aop:after-throwing method="afterThrowing" pointcut-ref="pt"/>-->
<aop:around method="around" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
通知中获取参数,返回值,异常对象
<!----------------通知获取原始方法参数信息----------------->
<aop:config>
<aop:pointcut id="pt" expression="execution(* *..*(..))"/>
<aop:aspect ref="myAdvice">
<aop:before method="before" pointcut-ref="pt"/>
<aop:around method="around" pointcut-ref="pt"/>
<aop:after-returning method="afterReturing" pointcut-ref="pt"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="pt"/>
<aop:after method="after" pointcut-ref="pt"/>
<aop:before
method="before1"
<!-- 控制方法中x,y的对应 -->
arg-names="y,x"
<!-- x,y与方法中的参数强绑定,参数一致-->
pointcut="execution(* *..*(int,int)) && args(x,y)"/>
</aop:aspect>
</aop:config>
<!----------------通知获取原始方法返回值信息----------------->
<aop:config>
<aop:pointcut id="pt" expression="execution(* *..*(..))"/>
<aop:aspect ref="myAdvice">
<!-- 对应通知类方法中的ret -->
<aop:after-returning method="afterReturing" pointcut-ref="pt" returning="ret"/>
<aop:around method="around" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
<!----------------通知获取原始方法异常信息----------------->
<aop:config>
<aop:pointcut id="pt" expression="execution(* *..*(..))"/>
<aop:aspect ref="myAdvice">
<aop:around method="around" pointcut-ref="pt"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="pt" throwing="t"/>
</aop:aspect>
</aop:config
public class AOPAdvice {
//通过JoinPoint获取参数
public void before(JoinPoint jp){
Object[] args = jp.getArgs();
System.out.println("before..."+args[0]);
}
//直接获取参数,需修改配置文件
public void before1(int x,int y){
System.out.println("before(int)..."+x+","+y);
}
public void after(JoinPoint jp){
Object[] args = jp.getArgs();
System.out.println("after..."+args[0]);
}
//获取返回值,ret对应配置文件中的ret
public void afterReturing(Object ret){
System.out.println("afterReturing..."+ret);
}
//获取异常对象
public void afterThrowing(Throwable t){
System.out.println("afterThrowing..."+t.getMessage());
}
//环绕通知获取返回值
public Object around(ProceedingJoinPoint pjp) {
System.out.println("around before...");
Object ret = null;
try {
//对原始方法的调用
ret = pjp.proceed();
} catch (Throwable throwable) {
System.out.println("around...exception...."+throwable.getMessage());
}
System.out.println("around after..."+ret);
return ret;
}
注解开发
AOP注解
- 类注解@Aspect:设置当前类为切面类
- 方法注解@Pointcut:使用当前方法名作为切入点引用名称
- 方法注解@Before:标注当前方法作为前置通知
- 方法注解@After:标注当前方法作为后置通知
- 方法注解@AfterReturning:标注当前方法作为返回后通知
- 方法注解@AfterThrowing:标注当前方法作为异常后通知
- 方法注解@Arround:标注当前方法作为环绕通知
<!-- 开启spring注解驱动 -->
<context:component-scan base-package="com"/>
<!-- 开始AOP注解驱动支持 -->
<aop:aspectj-autoproxy/>
@Component
@Aspect
public class AOPAdvice {
@Pointcut("execution(* *..*(..))")
public void pt1(){}
@Before("AOPPointcut.pt1()")
public void before(){
System.out.println("前置before...");
}
@After("AOPPointcut.pt1()")
public void after(){
System.out.println("后置after...");
}
@AfterReturning("AOPPointcut.pt1()")
public void afterReturing(){
System.out.println("返回后afterReturing...");
}
@AfterThrowing("AOPPointcut.pt1()")
public void afterThrowing(){
System.out.println("抛出异常后afterThrowing...");
}
@Around("AOPPointcut.pt1()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前around before...");
Object ret = pjp.proceed();
System.out.println("环绕后around after...");
return ret;
}
}
注解驱动
Spring注解配置类@EnableAspectAutoProxy:设置当前类开启AOP注解驱动的支持,加载AOP注解