环境搭建
在导入ioc包基础上导入这几个包
com.springsource.net.sf.cglib-2.2.0.jar 动态代理包
com.springsource.org.apoaliance-1.0.0.jar
com.springsource.org.aspectj.tools-1.6.6.RELEASE.jar
spring-aop-4.1.0.RELEASE.jar 面向切面编程提供AOP实现
spring-aspects-4.1.0.RELEASE.jar 与AspectJ框架的整合
头文件信息样式
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--开启自动扫描--> <context:component-scan base-package="com.chinasofti"></context:component-scan> <!--spring中使用aspectj包中的@Aspect注解标注当前组件为切面 如果要使用注解必须开启aspectj的自动代理模式--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
aop的核心组件:
Aspect(切面):切面是封装通用业务逻辑的组件,可以作用到其他组件上
Pointcut(切入点):用于指定哪些组件哪些方法使用切面组件,spring提供表达式来实现该指定
Advice(通知):用于指定组件作用到目标组件的具体位置
AOP前置通知:在目标组件的方法执行前执行的程序
步骤:切入点程序 —>切面程序—>通过配置文件将切面程序插入到切入点程序的某个位置上(通知)
切入点程序:可以是Spring组件中的任意方法
package com.chinasofti.bean; public class Target { public String save(String name){ System.out.println("目标函数执行"); System.out.println(10/0); return "保存成功"; } }
<bean id="target" class="com.chinasofti.bean.Target"></bean>
切面程序:是spring组件中的某个方法,无返回类型,参数类型与通知类型有关,前置通知的参数类型为org.aspectj.lang.JoinPoint(可以无参数)
package com.chinasofti.test3_1; import org.aspectj.lang.JoinPoint; public class TestBefore { public void doBefore(JoinPoint jp){ System.out.println("获取目标函数参数" + jp.getArgs()[0]); System.out.println("获取目标函数所在对象" + jp.getTarget().getClass().getName()); System.out.println("获取目标函数的java反射对象" + jp.getSignature()); System.out.println("执行前置通知"); } }
配置aop前置通知
<bean id="testBefore" class="com.chinasofti.test3_1.TestBefore"></bean> <aop:aspect> <!--配置aop切入点:expression为切入点表达式,代表那些切入点程序执行时被插入切面程序--> <!--execution表达式中第一个* 代表什么返回类型都可以 第二个* 代表有无参数都可以--> <aop:pointcut id="targetPintcut" expression="execution(* com.chinasofti.bean.Target.save(*))" /> <!--配置切面 ref:切面对象--> <aop:aspect ref="testBefore"> <!--设定通知为前置 method:切面的方法 pointcut-ref:指定通知作用在那些切入点上--> <aop:before method="doBefore" pointcut-ref="targetPintcut" /> </aop:aspect>
AOP后置通知 (只能包含2个参数)
AOP后置通知:在目标组件的方法正常执行并返回参数后执行的程序。
切面程序:后置通知的切面程序中可以获取到目标方法返回参数,单需要在配置文件中声明参数名,依赖spring容器注入参数值
package com.chinasofti.test3_1; import org.aspectj.lang.JoinPoint; public class TestAfterReturn { public void testAfterReturn(JoinPoint jp,Object oRet){ System.out.println("目标函数的返回值:" + oRet); System.out.println("后置通知在目标函数正常执行并返回参数后执行"); } }
<aop:aspect ref="testAfterReturn"> <!--设定通知为后置 method:切面的方法 pointcut-ref:指定通知作用在那些切入点上--> <aop:after-returning method="testAfterReturn" pointcut-ref="targetPintcut" returning="oRet"/> </aop:aspect>
AOP异常通知
AOP异常通知:在目标组件的方法抛出异常信息后执行的程序(只能包含2个参数)
切面编程:异常通知的切面程序中可以获取到目标组件抛出的异常信息,需要在配置文件上声明异常参数名(与后置通知很类似),依赖spring容器注入参数值
package com.chinasofti.test3_1; import org.aspectj.lang.JoinPoint; public class TestThrowing { public void doThrow(JoinPoint jp,Throwable e){ System.out.println(e.getMessage()); System.out.println("在目标组件出现异常后执行"); } }
<aop:aspect ref="testThrowing"> <!--设定通知为异常 method:切面的方法 pointcut-ref:指定通知作用在那些切入点上--> <aop:after-throwing method="doThrow" pointcut-ref="targetPintcut" throwing="e"/> </aop:aspect>
AOP最终通知:无论目标组件是否有无异常都会执行 方法正常执行后执行或在异常通知前执行(只能包含1个参数)
package com.chinasofti.test3_1; import org.aspectj.lang.JoinPoint; public class TestAfter { public void doAfter(JoinPoint jp){ System.out.println("无论目标组件时候正常执行,都会执行"); } }
<aop:aspect ref="testAfter"> <!--设定通知为最终 method:切面的方法 pointcut-ref:指定通知作用在那些切入点上--> <aop:after method="doAfter" pointcut-ref="targetPintcut"/> </aop:aspect>
总结
通知执行流程
before:组件参数(JoinPoint)
try{
Target(目标组件)
}catch(e){
after-throwing:组件参数(JoinPoint ,Throwable)
}
after:组件参数(JoinPoint)
if(Target正常结束){//如果抛出异常则不执行,最后执行
after-returning:组件参数(JoinPoint ,Object)
}
环绕通知
环绕通知:切面程序负责调用目标组件的运行,与Struts中的拦截器功能类似,可以完全替代之前的几个通知
切面程序:在类型为环绕通知的切面程序函数中,参数为org.aspectj.lang.ProceedingJoinPoint是JoinPoint的子类,扩展了JoinPoint类,提供了proceed()函数,该函数调用目标组件,并返回目标组件返回的值
package com.chinasofti.test3_1; import org.aspectj.lang.ProceedingJoinPoint; public class TestAround { public void doAround(ProceedingJoinPoint pjp){ System.out.println("前置通知"); try { Object ob = pjp.proceed(); System.out.println("目标函数的返回值"); System.out.println("后置通知"); } catch (Throwable throwable) { System.out.println("异常通知"); } System.out.println("最终通知"); } }
<aop:aspect ref="testAround"> <aop:around method="doAround" pointcut-ref="targetPointCut"/> </aop:aspect>
切入点表达式:用于声明spring容器那些组件的函数是目标函数
常用切入点表达式分为:
1.匹配java类中全部函数作为目标函数,使用within关键字
2.按函数匹配:匹配的函数作为目标函数,使用execution关键字
3.按bean的id匹配:匹配的bean中全部函数作为目标组件,使用bean关键字
1 <!--按类匹配 复合规则的类下所有函数都作为切入点--> 2 <aop:config> 3 <!--按类匹配--> 4 <aop:pointcut id=" targetPintcut" expression="within(com.chinasofti.test3_1)"/> 5 <!--匹配到包下的类--> 6 <aop:pointcut id="targetPincut" expression="within(com.chinasofti.*)"/> 7 <!--匹配到包及子包下的类--> 8 <aop:pointcut id="targetPincut" expression="within(com..*)"/> 9 </aop:config> 10 <!--按函数匹配--> 11 <aop:pointcut id="target" expression="execution(表达式)"/> 12 <!-- 13 execution(返回类型 类路径.类名.函数名 (参数类型用,分开)) 14 任意返回类型 15 execution(* 类路径.类名.函数名(参数类型)) 16 任意返回类型下指定包任意类 17 execution(* 类路径.*.函数名(参数类型)) 18 任意返回类型下指定包下任意了任意函数 19 execution(* 类路径.*.*(参数类型)) 20 任意返回类型下只当包或子包下任意类任意函数任意参数 21 execution(* com..*.*(..)) 22 --> 23 </aop:pointcut> 24 <!--按bean组件的id匹配--> 25 <aop:pointcut id="target" expression="bean(target)"/> 26 <!--按bean组件名称(含通配符)匹配--> 27 <aop:pointcut id="target" expression="bean(target*)"/>