Spring配置切面(AOP)
AOP,面向接口的编程,实际上是代理模式的实现。
参考:代理模式(Proxy)
一、使用Scheme-based方式配置
需要实现接口重写指定方法,来确定通知所在位置。
(一)、前置通知和后置通知
切点类:
public class Client { public void sayHi(){ System.out.println("Client.sayHi"); } }
通知类:
前置通知实现接口:MethodBeforeAdvice,在实现方法中可获取方法对象,参数数组,调用对象
后置通知实现接口:AfterReturningAdvice,在实现方法中可获取方法对象,参数数组,调用对象,返回值
public class TestBeforeAdvice implements MethodBeforeAdvice { @Override public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println("执行前置通知"); } }
xml配置:
<!--切点类--> <bean class="com.lurenjia.test.Client" id="client"></bean> <!--前置通知对象--> <bean id="mybefore" class="com.lurenjia.advice.TestBeforeAdvice"></bean> <!--后置通知对象--> <bean id="after" class="com.lurenjia.advice.TestAfterAdvice"></bean> <!--配置一个aop--> <aop:config> <!--切点--> <aop:pointcut id="mypoint" expression="execution(* com.lurenjia.test.Client.sayHi())"/> <!--配置一个前置通知--> <aop:advisor advice-ref="mybefore" pointcut-ref="mypoint"></aop:advisor> <!--配置一个后置通知--> <aop:advisor advice-ref="after" pointcut-ref="mypoint"></aop:advisor> </aop:config>
(二)、异常通知
异常通知类的实现方法不为接口提供,是spring文档提供的。(奇奇怪怪)
切点类:
一般在service中都采取抛出异常,从而使得spring进行捕获异常进行事务处理。
public class Client { public void doSomething2() throws Exception{ int a = 5/0; } }
通知类:
自行指定方法名,可在参数中获取方法对象,参数对象,执行对象,异常对象,(或者只获取异常对象)
public class TestThrowAdvice implements ThrowsAdvice { /** * 指定此方法名才能被调用*/ public void afterThrowing(Method method,Object[] args,Object target,Exception e){ System.out.println("获取到发生异常的方法:"+method.getName()); System.out.println("获取到参数:"+args); System.out.println("获取到调用对象:"+target); System.out.println("获取到异常:"+e.getMessage()); } }
xml配置:
<!--切点类--> <bean class="com.lurenjia.test.Client" id="client"></bean> <!--异常通知对象--> <bean class="com.lurenjia.advice.TestThrowAdvice" id="throw"></bean> <!--配置一个aop--> <aop:config> <!--切点--> <aop:pointcut id="myponint2" expression="execution(* com.lurenjia.test.Client.doSomething2())"/> <!--配置一个异常通知--> <aop:advisor advice-ref="throw" pointcut-ref="myponint2"></aop:advisor> </aop:config>
(三)、环绕通知
在方法前后都织入通知。
切点类:
public class Client { public void sayHi(){ System.out.println("Client.sayHi"); } }
通知类:
public class TestArround implements MethodInterceptor { @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { //前置处理 System.out.println("前置处理了"); //执行方法 Object proceed = methodInvocation.proceed(); //后置处理 System.out.println("后置处理了"); //返回结果 return proceed; } }
xml配置:
<!--切点类--> <bean class="com.lurenjia.test.Client" id="client"></bean> <!--环绕通知对象--> <bean id="arround" class="com.lurenjia.advice.TestArround"></bean> <aop:config> <!--切点--> <aop:pointcut id="point1" expression="execution(* com.lurenjia.test.Client.sayHi())"/> <!--配置一个环绕通知--> <aop:advisor advice-ref="arround" pointcut-ref="point1"></aop:advisor> </aop:config>
二、使用AspectJ方式配置
不需要实现接口,完全自定义方法,所以需要在xml中配置方法位置。
(一)、配置通知
切点类:
public class Client { public void sayHi() throws Exception{ //int i = 7/0; System.out.println("执行方法"); } }
通知类:
public class TestAspectjAdvice { public void myBefore(){ System.out.println("before:前置通知!"); } public void myAfter(){ System.out.println("after:finally后置通知!"); } public void myFnalliy(){ System.out.println("after-returning:后置通知!"); } public void myThrow(){ System.out.println("after-throwing:异常通知!"); }
public Object myArround(ProceedingJoinPoint joinPoint) throws Throwable { //前置操作 System.out.println("around:前置操作"); //执行方法 Object result = joinPoint.proceed(); //后置操作 System.out.println("around:后置操作"); //返回值返回 return result; } }
xml配置:
<!--切点类--> <bean class="com.lurenjia.test.Client" id="client"></bean> <!--通知类--> <bean class="com.lurenjia.advice.TestAspectjAdvice" id="advice"></bean> <!--配置一个切面--> <aop:config> <!--使用的通知类--> <aop:aspect ref="advice"> <!--切点方法--> <aop:pointcut id="point1" expression="execution(* com.lurenjia.test.Client.sayHi())"/> <!--前置通知--> <aop:before method="myBefore" pointcut-ref="point1"></aop:before> <!--后置通知--> <aop:after method="myAfter" pointcut-ref="point1"></aop:after> <!--finally通知--> <aop:after-returning method="myFnalliy" pointcut-ref="point1"></aop:after-returning> <!--异常通知--> <aop:after-throwing method="myThrow" pointcut-ref="point1"></aop:after-throwing> <!--环绕通知--> <aop:around method="myArround" pointcut-ref="point1"></aop:around> </aop:aspect> </aop:config>
(二)、获取参数
切点类:
public class Client { public void sayHi(String name,int age) throws Exception{ System.out.println("执行方法"); } }
通知类:
public class TestAspectjAdvice { public void myBefore(String name,int age){ System.out.println("before:前置通知!"); System.out.println("获取到参数:"+name+"-"+age); } }
xml配置:
<!--切点类--> <bean class="com.lurenjia.test.Client" id="client"></bean> <!--通知类--> <bean class="com.lurenjia.advice.TestAspectjAdvice" id="advice"></bean> <!--配置一个切面--> <aop:config> <!--使用的通知类--> <aop:aspect ref="advice"> <!--切点方法,声明参数类型和名称--> <aop:pointcut id="point1" expression="execution(* com.lurenjia.test.Client.sayHi(String,int)) and args(name,age)"/> <!--前置通知,配置参数名必须与上面声明的一样--> <aop:before method="myBefore" pointcut-ref="point1" arg-names="name,age"></aop:before> </aop:aspect> </aop:config>