Spring框架 AOP
AOP(Aspect Oriented Programming)
原理
正常程序执行顺序都为纵向执行流程 ,在某一个步骤的前后,做一个前置通知
和后置通知,这个过程称为切面编程。
即:在原有的纵向执行流程中添加一个横切面
特点
①高扩展性:对原先步骤流程无影响
②原有功能释放了部分逻辑,让职责更加明确
常用概念
①原有功能:切点
②前置/后置通知:在切点之前/之后执行的功能,before/after advice
③若切点执行过程出现异常,会触发异常通知:throws advice
④所有功能总称叫切面
⑤把切面嵌入到原有功能的过程叫织入
依赖的jar包
①四个核心包
②aop和aspects
③tx
④commons-logging
⑤aspectjweaver(https://mvnrepository.com/artifact/org.aspectj/aspectjweaver)
和aopalliance(https://mvnrepository.com/artifact/aopalliance/aopalliance/1.0)
Ps:添加命名空间:xmlns:aop和xsi:schemalocation
实现过程(Spring提供2种方式)
第一种:基于Schema-based
配置文件主要构件
将前置/后置通知使用添加到sping容器中
使用
子元素
子元素
Ps:由于配置文件中添加前/后置等通知时并无明显区别,即无法分辨是什么通知,因此通知类需继承接口,并实现其中方法,使得Spring可以判断出不同的通知
关于expression表达式为:
execution(* 包名.类名.方法名())
若有参数:execution(* 包名.类名.方法名(参数类型,参数类型)) and args(参数别名,参数别名),类型和别名相对应
`*`为通配符,会匹配任意方法名,任意类名,任意一级包名,如:com.*.project.*()
第一个`*`表示声明通配符
若匹配任意类型参数:execution(* 包名.类名.方法名(…))
①前/后置通知
前置/后置通知实现
类需实现MethodBeforeAdvice(前置)或AfterReturningAdvice(后置)接口
再实现接口中的方法,即为前置/后置方法
前置方法的参数:
第一个为切点方法,即反射所得Method
第二个为切点方法参数列表
第三个为切点方法所在类的对象
后置通知增加了一个arg0:切点方法返回值
<!-- 前置通知 -->
<bean id="beforeAdvice" class="com.mfqh.BeforeAdvice"></bean>
<!-- 后置通知 -->
<bean id="afterAdvice" class="com.mfqh.AfterAdvicce"></bean>
<!--切点-->
<bean id="tAService" class="com.mfqh.service.TestAdviceService"></bean>
<!-- 切面配置-->
<aop:config>
<!-- 配置切点 -->
<aop:pointcut expression="* com.mfqh.service.TestAdviceService.demo()" id="demoCut"/>
<!-- 声明前置通知 -->
<aop:advisor advice-ref="beforeAdvice" pointcut-ref="demoCut"/>
<!-- 声明后置通知 -->
<aop:advisor advice-ref="afterAdvice" pointcut-ref="demoCut"/>
</aop:config>
测试结果:
②环绕通知
把前置通知和后置通知放在一个方法中
配置文件,在
方法,实现MethodInterceptor(拦截器)接口:
在方法中依次进行前置处理、调用切点方法(arg0.proceed()),后置处理
<!-- 环绕通知 -->
<bean id="interAdvice" class="com.mfqh.advice.InterceptorAdvice"></bean>
<!-- 切面配置-->
<aop:config>
<aop:pointcut expression="execution(* com.mfqh.service.*.demo1(..))" id="demo1Cut"/>
<aop:advisor advice-ref="interAdvice" pointcut-ref="demo1Cut"/>
</aop:config>
测试结果:
③异常通知
配置文件,在
处理异常方法,继承ThrowsAdvice:
第一个方法,afterThrowing(Exception e)
第二个方法,afterThrowing(Method m,Object[] args,Object target, Exception e)
Ps:若两个方法都定义,执行后面的;
若在方法中将异常进行捕获,则不会进行异常通知
Tips:aop一般用于service中,添加声明式事务后,若出现异常就会自动回滚,
则service不能进行捕获异常,即aop方法中并不会捕获异常
<!-- 异常通知 -->
<bean id="throwAdvice" class="com.mfqh.advice.ThrowAdvice"></bean>
<!-- 切面配置-->
<aop:config>
<aop:pointcut expression="execution(* com.*.service.*.demo2(..))" id="demo2Cut"/>
<aop:advisor advice-ref="throwAdvice" pointcut-ref="demo2Cut"/>
</aop:config>
测试结果(使用一个最简单的异常,在方法中定义5/0):
Ps:此处需要在调用demo3()时try catch捕获异常,不然会报错
第二种:基于AspectJ
配置文件主要构件
使用
子元素
子元素
子元素
区别:出异常时after依旧会执行,after-returning不执行,两者及
Ps:由于在配置文件中不同的通知所使用的属性也不同,Spring可以直接分辨出不同的通知,因此通知类不需要继承特定的接口,编写较为灵活
①前/后置通知
前置/后置通知并不需要实现接口
参数获取:
Ⅰ、不能使用execution(* 包名.类名.方法名()),必须表明参数类型及名称
Ⅱ、在
arg-names=“参数别名,参数别名”,此处必须包含所有参数
Ⅲ、并且所定义的通知方法形参名必须和参数别名相同
<!-- 通知所在方法类 -->
<bean id="allAdvice" class="com.mfqh.advice.AllAdvice"></bean>
<!-- 切面配置 -->
<aop:config>
<aop:aspect ref="allAdvice">
<aop:pointcut expression="execution(* com.mfqh.service.TestAdviceService.demo3(String,int)) and args(name1,num1)" id="demo3Cut"/>
<aop:before method="beforeAdvice" pointcut-ref="demo3Cut" arg-names="name1,num1"/>
<aop:after method="afterAdvice" pointcut-ref="demo3Cut" arg-names="name1,num1"/>
</aop:aspect>
</aop:config>
测试结果:
②环绕通知
方法返回值为Object,参数为ProceedingJoinPoint p,通过p.proceed()调用切点方法
<!-- 通知所在方法类 -->
<bean id="allAdvice" class="com.mfqh.advice.AllAdvice"></bean>
<!-- 切面配置 -->
<aop:config>
<aop:aspect ref="allAdvice">
<aop:pointcut expression="execution(* *.*.*.TestAdviceService.demo4())" id="demo4Cut"/>
<aop:around method="arroundProfiling" pointcut-ref="demo4Cut"/>
</aop:aspect>
</aop:config>
测试结果:
③异常通知
在
子元素
参数获取:
若异常处理方法有参数Exception e,则需添加属性throwing=“e”(必须和参数名一致)
但反过来添加该属性方法时可以不设置参数(未声明)
<!-- 切面配置 -->
<aop:config>
<aop:aspect ref="allAdvice">
<aop:pointcut expression="execution(* *.*.*.TestAdviceService.demo5())" id="demo5Cut"/>
<aop:after-throwing method="dealException" pointcut-ref="demo5Cut" throwing="e"/>
</aop:aspect>
</aop:config>
测试结果:
还没有评论,来说两句吧...