在Spring1.2或之前的版本中,实现AOP的传统方式就是通过实现Spring的AOP API来定义Advice,并设置代理对象。Spring根据Adivce加入到业务流程的时机的不同,提供了四种不同的Advice:Before Advice、After Advice、Around Advice、Throw Advice。
1、Before Advice
顾名思义,Before Advice会在目标对象的方法执行之前被调用,您可以通过实现org.springframework.aop.MethodBeforeAdvice接口来实现Before Advice的逻辑,接口定义如下:
- package org.springframework.aop;
- public interface MethodBeforeAdvice extends BeforeAdvice {
- void before(Method method, Object[] args, Object target) throws Throwable;
- }
其中BeforeAdvice继承自Adivce接口,这两者都是标签接口,并没有定义任何具体的方法。before方法会在目标对象的指定方法执行之前被执行,在before方法种,你可以取得指定方法的Method实例、参数列表和目标对象,在before方法执行完后,目标对象上的方法将会执行,除非在before方法种抛出异常。
下面通过例子来说明Before Advice的使用方法。首先定义目标对象所要实现的接口:
- package com.savage.aop
- public interface MessageSender {
- void send(String message);
- }
接着实现MessageSender接口:
- package com.savage.aop;
- public class HttpMessageSender implements MessageSender {
- public void send(String message) {
- System.out.println("Send Message[" + message + "] by http.");
- }
- }
OK,我们的业务代码实现完了,现在如果要在不改变我们的业务代码的前提下,在执行业务代码前要记录一些日志,这时就可以通过实现MethodBeforeAdvice接口来实现,如:
- package com.savage.aop;
- import java.lang.reflect.Method;
- import org.springframework.aop.framework.MethodBeforeAdvice;
- public class LogBeforeAdvice implements MethodAdvice {
- public void before(Method method, Object[] args, Object target) throws Throwable {
- System.out.println("Log before " + method + " by LogBeforeAdvice.");
- }
- }
然后再在XML进行如下定义:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
- <bean id="messageSenderImpl" class="com.savage.aop.HttpMessageSender"></bean>
- <bean id="logBeforeAdvice" class="com.savage.aop.LogBeforeAdvice"></bean>
- <bean id="messageSender" class="org.springframework.aop.framework.ProxyFactoryBean">
- <property name="proxyInterfaces" value="com.savage.aop.MessageSender"/>
- <property name="target" ref="messageSenderImpl"/>
- <property name="interceptorNames">
- <list>
- <value>logBeforeAdvice</value>
- </list>
- </property>
- </bean>
- </beans>
这样我们就为MessageSender对象指定了Before Advice对象。在这里,我们分别定义了一个MessageSender对象(messageSenderImpl)和一个Before Advice对象(logBeforeAdvice),并定义了一个org.springframework.aop.framework.ProxyFactoryBean对象(messageSender),FactoryBean或ApplicationContext将使用ProxyFactoryBean来建立代理对象,在这里就是messageSenderImpl建立代理对象。在ProxyFactoryBean的定义中,proxyInterfaces属性指定了要代理的接口;target指定了要建立代理的目标对象;interceptorNames则指定了应用与指定接口上的Advices对象列表,spring将根据列表中定义的顺序在执行目标对象的方法前、后执行Advice中定义的方法。
现在我们写一个程序来验证下:
- package com.savage.aop;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplication;
- public class AdviceDemo {
- public void main(String[] args) {
- ApplicationContext context = new ClassPathXmlApplicationContext("beans-config.xml");
- MessageSender sender = (MessageSender)context.getBean("messageSender");
- sender.send("message");
- }
- }
执行结果:
Log before public abstract void com.savage.simplespring.bean.MessageSender.send(java.lang.String) by LogBeforeAdvice.
Send Message[message] by http.
正如你所看到的,在执行MessageSender的send方法前先执行了LogBeforeAdvice的方法!在这个例子中,记录日志的代码并没有横切到我们的业务代码中,LogBeforeAdvice和HttpMessageSender彼此不知道对方的存在,而且我们的应用程序AdviceDemo对LogBeforeAdvice的存在也是一无所知。假如有一天我们的应用程序不需要再业务代码执行前记录日志了,只需要修改XML文件中的定义,而不用更改AdviceDemo的代码:
- <bean id="messageSender" class="com.savage.aop.HttpMessageSender">bean>
2、After Advice
After Advice会在目标对象的方法执行完后执行,你可以通过实现org.springframework.aop.AfterReturingAdvice接口来实现After Advice的逻辑,AfterReturingAdvice接口定义如下:
- package org.springframework.aop;
- public interface AfterReturningAdvice {
- void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;
- }
在afterReturning方法中,你可以获得目标方法执行后的返回值、目标方法对象、目标方法的参数以及目标对象。
继续以上面的例子为例,如果要在MessageSender的send方法执行完后,要再记录日志,那么我们可以先实现AfterReturningAdvice接口:
- package com.savage.aop;
- import org.springframework.aop;
- public LogAfterAdvice implements AfterReturningAdvice {
- public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
- System.out.println("Log after " + method + " by LogAfterAdvice.");
- }
- }
然后在XML文件中指定LogAfterAdvice的实例:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
- <bean id="messageSenderImpl" class="com.savage.aop.HttpMessageSender"></bean>
- <bean id="logBeforeAdvice" class="com.savage.aop.LogBeforeAdvice"></bean>
- <bean id="messageSender" class="org.springframework.aop.framework.ProxyFactoryBean">
- <property name="proxyInterfaces" value="com.savage.aop.MessageSender"/>
- <property name="target" ref="messageSenderImpl"/>
- <property name="interceptorNames">
- <list>
- <value>logAfterAdvice</value>
- </list>
- </property>
- </bean>
- </beans>
在前面Before Advice的基础上,我们为MessageSender再指定了一个LogAfterAdvice的服务。运行前面的AdviceDemo,结果如下:
Send Message[message] by http.
Log after public abstract void com.savage.simplespring.bean.MessageSender.send(java.lang.String) by LogAfterAdvice.
3、Around Advice
在上面的LogAfterAdvice例子中,我们通过指定BeforeAdvice和AfterReturingAdvice,在MessageSender的send方法前后执行额外的业务。实际上,如果需要在业务代码执行前后增加额外的服务,你可以直接通过实现org.aopalliance.intercept.MethodInterceptor接口来达到这一目的,MethodInterceptor定义如下:
- package org.aopalliance.intercept;
- public interface MethodInterceptor {
- public Object invoke(MethodInvocation methodInvocation) throws Throwable;
- }
例如:
- package com.savage.aop;
- import org.aopalliance.intercept.MethodInterceptor;
- import org.aopalliance.intercept.MethodInvocation;
- public class LogAdvice implements MethodInterceptor {
- public Object invoke(MethodInvocation methodInvocation) throws Throwable {
- System.out.println("Log before " + methodInvocation.getMethod() + " by LogAdvice.");
- Object retValue = methodInvocation.proceed();
- System.out.println("Log after " + methodInvocation.getMethod() + " by LogAdvice.");
- return retValue;
- }
- }
正如上面所示,在MethodInterceptor中你得自行决定是否调用MethodInvocation的proceed()方法来执行目标对象上的方法,proceed()方法在执行完后会返回目标对象上方法的执行结果。
MethodInterceptor在XML文件中的定义如下:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
- <bean id="messageSenderImpl" class="com.savage.aop.HttpMessageSender"></bean>
- <bean id="logAdvice" class="com.savage.aop.LogAdvice"></bean>
- <bean id="messageSender" class="org.springframework.aop.framework.ProxyFactoryBean">
- <property id="proxyInterfaces" value="com.savage.aop.MessageSender"/>
- <property id="target" ref="messageSenderImpl"/>
- <property id="interceptorNames">
- <list>
- <value>logAdvice</value>
- </list>
- </property>
- </bean>
- </beans>
Spring在真正执行目标对象的方法前,会执行interceptorNames中执行的Advice,每个Advice在执行完自己的业务后,会调用MethodInvocation的proceed()方法,将执行的主动权移交给下一个Advice,直到没有下一个Advice为止,在执行完目标对象的方法后,Spring会再以相反的顺序一层层的返回。例如:
- <bean id="messageSender" class="org.springframework.aop.framework.ProxyFactoryBean">
- <property id="proxyInterfaces" value="com.savage.aop.MessageSender"/>
- <property id="target" ref="messageSenderImpl"/>
- <property id="interceptorNames">
- <list>
- <value>logBeforeAdvice</value>
- <value>logAdvice</value>
- <value>logAfterAdvice</value>
- </list>
- </property>
- </bean>
象上面这个例子,logBeforeAdvice先会被执行,然后执行logAdvice,接着执行logAfterAdvice,最后又返回到了logAdvice。
现在我们把LogAdvice作一下简单的修改,增加一个id属性,用以在后面查看Advice的调用顺序:
- package com.savage.aop;
- import org.aopalliance.intercept.MethodInterceptor;
- import org.aopalliance.intercept.MethodInvocation;
- public class LogAdvice implements MethodInterceptor {
- private static int INSTANCE_NUM = 0;
- private int id;
- public LogAdvice() {
- id = ++INSTANCE_NUM;
- }
- public Object invoke(MethodInvocation methodInvocation) throws Throwable {
- System.out.println("Log before " + methodInvocation.getMethod() + " by LogAdvice[" + id + "].");
- Object retValue = methodInvocation.proceed();
- System.out.println("Log after " + methodInvocation.getMethod() + " by LogAdvice[" + id + "].");
- return retValue;
- }
- }
同时把XML中的定义改为:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
- <bean id="messageSenderImpl" class="com.savage.aop.HttpMessageSender"></bean>
- <bean id="logBeforeAdvice" class="com.savage.aop.LogBeforeAdvice"></bean>
- <bean id="logAfterAdvice" class="com.savage.aop.LogAfterAdvice"></bean>
- <bean id="logAdvice1" class="com.savage.aop.LogAdvice"></bean>
- <bean id="logAdvice2" class="com.savage.aop.LogAdvice"></bean>
- <bean id="messageSender" class="org.springframework.aop.framework.ProxyFactoryBean">
- <property id="proxyInterfaces" value="com.savage.aop.MessageSender"/>
- <property id="target" ref="messageSenderImpl"/>
- <property id="interceptorNames">
- <list>
- <value>logBeforeAdvice</value>
- <value>logAdvice1</value>
- <value>logAfterAdvice</value>
- <value>logAdvice2</value>
- </list>
- </property>
- </bean>
- </beans>
现在再执行AdviceDemo,得到如下结果:
Log before public abstract void com.savage.simplespring.bean.MessageSender.send(java.lang.String) by LogBeforeAdvice.
Log before public abstract void com.savage.simplespring.bean.MessageSender.send(java.lang.String) by LogAdvice[1].
Log before public abstract void com.savage.simplespring.bean.MessageSender.send(java.lang.String) by LogAdvice[2].
Send Message[message] by http.
Log after public abstract void com.savage.simplespring.bean.MessageSender.send(java.lang.String) by LogAdvice[2].
Log after public abstract void com.savage.simplespring.bean.MessageSender.send(java.lang.String) by LogAfterAdvice.
Log after public abstract void com.savage.simplespring.bean.MessageSender.send(java.lang.String) by LogAdvice[1].
4、Throw Advice
如果想要在异常发生时执行某些业务,你可以通过实现org.springframework.aop.ThrowsAdvice接口,这是一个标签接口,没有定义任何方法,你可以在当中为每个你需要处理的异常类定义afterThrowing方法,当程序出现异常时,spring会根据异常的类型调用对应的afterThrowing方法。AfterThrowing的格式如下:
- afterThrowing([Method],[args],[target],subClassOfThrowable);
方括号[]中的参数为可选项,但方法中必须有subClassOfThrowable,且必须是Throwable的子类。
Spring在调用完afterThrowing方法后,原先的异常会继续在程序中传播,如果象要终止程序对异常的处理,只能在afterThrowing方法中抛出其他异常。
在Spring 2.0中,除了传统的通过实现AOP AIP的方式来实现Advice之外,还提供了两种更加简便的方式来实现Advice:1)基于XML Schema的设置;2)基于Annotation的支持,采用这两种方式,Advice将不用实现特定的接口。现在让我们来看看如何使用这两种方式来分别实现Before Advice、After Advice、Around Advice、Throwing Advice。
一、Before Advice:基于XML Schema
当基于XML Schema实现Before Advice时,你的Advice类不用实现org.springframework.aop.MethodBeforeAdvice接口,例如:
- package com.savage.aop;
- import org.aspectj.lang.JoinPoint;
- public class LogBeforeAdvice {
- public void before(JoinPoint joinPoint) {
- System.out.println("Logging before " + joinPoint.getSignature().getName());
- }
- }
before方法是在目标对象上的方法被执行前要执行的方法,before方法中的JoinPoint参数是可选项,你可以根据需要决定是否需要JoinPoint参数,通过JoinPoint对象,你可以获得目标对象(getTarget())、目标方法上的参数(getArgs())等信息。
然后在XML中为目标对象指定LogBeforeAdvice代理:
- <?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:aop="http://www.springframework.org/schema/aop"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
- <bean id="messageSender" class="com.savage.aop.HttpMessageSender"></bean>
- <bean id="logBeforeAdvice" class="com.savage.aop.LogBeforeAdvice"></bean>
- <aop:config>
- <aop:aspect id="logBefore" ref="logBeforeAdvice">
- <aop:before pointcut="execution(* com.savage.aop.MessageSender.*(..))"
- method="before"/>
- </aop:aspect>
- </aop:config>
- </beans>
如上所示,在Spring 2.0中要使用基于XML Sechma声明AOP的方式,需要在XML中加入aop的名称空间。当基于XML Sechma实现AOP时,所有的AOP都是在<aop:config></aop:config>标签中声明的,<aop:aspect></aop:aspect>用于定义Advice实例。<aop:before></aop:before>表示当前实例用于实现Before Advice;pointcut属性用于指定pointcut表示式,上面的例子表示此Advice将应用于com.savage.aop.MessageSender接口中的任何方法;method属性表示Advice上要调用的方法。
现在调用任何MessageSender接口上的方法之前都会执行LogBeforeAdvice的before方法,例如:
- package com.savage.aop;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- public class AdviceDemo {
- public static void main(String[] args) {
- ApplicationContext context =
- new ClassPathXmlApplicationContext("beans-config.xml");
- MessageSender sender = (MessageSender)context.getBean("messageSender");
- sender.sendMessage("message");
- }
- }
二、Before Advice:基于Annotation
使用Annotation来实现Advice,在XML文件上的定义要比基于XML Sechema的方法要简便的多,但在实现Before Advice类时,则需要使用到@Aspect、@Before标识,并需要引入org.aspectj.lang.annotation包中的类。还以LogBeforeAdvice为例,LogBeforeAdvice类需要改为:
- package com.savage.aop;
- import org.aspectj.lang.JoinPoint;
- import org.aspectj.lang.annotation.Aspect;
- import org.aspectj.lang.annotation.Before;
- @Aspect
- public class LogBeforeAdvice {
- @Before("execution(* com.savage.aop.MessageSender.*(..))")
- public void before(JoinPoint joinPoint) {
- System.out.println("Logging before " + joinPoint.getSignature().getName());
- }
- }
如上所示,通过@Aspect将一个类声明为Aspect类,通过@Before将方法声明Before Advice,方法中的JoinPoint同样是可选的。然后在XML文件中做如下定义:
- <?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:aop="http://www.springframework.org/schema/aop"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
- <bean id="messageSender" class="com.savage.aop.HttpMessageSender"></bean>
- <bean id="logBeforeAdvice" class="com.savage.aop.LogBeforeAdvice"></bean>
- <aop:aspectj-autoproxy/>
- </beans>
所有基于Annotation实现的Advice,在XML文件中都只要使用<aop:aspectj-autoproxy></aop:aspectj-autoproxy>进行设置就可以了,非常简单。
三、After Advice:基于XML Sechma
和Before Advice一样,基于XML Sechma实现After Returning Advice时,不再需要org.springframework.aop.AfterReturningAdvice接口:
- package com.savage.aop;
- import org.aspectj.lang.JoinPoint;
- public class LogAfterReturningAdvice {
- public void afterReturning(JoinPoint joinPoint) {
- System.out.println("Logging after " + joinPoint.getSignature().getName());
- }
- }
然后在XML中做如下设置:
- <?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:aop="http://www.springframework.org/schema/aop"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
- <bean id="messageSender" class="com.savage.aop.HttpMessageSender"></bean>
- <bean id="logAfterReturningAdvice"
- class="com.savage.aop.LogAfterReturningAdvice"></bean>
- <aop:config>
- <aop:aspect id="logAfterReturning" ref="logAfterReturningAdvice">
- <aop:after-returning
- pointcut="execution(* com.savage.aop.MessageSender.*(..))"
- method="logAfterReturning"/>
- </aop:aspect>
- </aop:config>
- </beans>
四、After Advice:基于Annotation
和Before Advice相似,使用@AfterReturning来表示After Returning Advice:
- package com.savage.aop;
- import org.aspectj.lang.JoinPoint;
- import org.aspectj.lang.annotation.Aspect;
- import org.aspectj.lang.annotation.AfterReturning;
- @Aspect
- public class AfterReturningAdvice {
- @AfterReturning(pointcut="execution(* com.savage.aop.MessageSender.*(..))",
- returning="retVal")
- public void afterReturning(JoinPoint joinPoint, Object retVal) {
- System.out.println("Logging after " + joinPoint.getSignature().getName());
- }
- }
这里和Before Advice有点不同的是,在定义Poincut表示式时,多了一个returning属性,用于指定目标方法执行完后的返回值。
XML文件中的设置与LogBeforeAdvice的相似(将logBeforeAdvice的定义改为logAfterReturning的定义),不再列举。
五、Around Advice:基于XML Sechma
在Spring 2.0中,Around Advice不用实现org.aoplliance.intercept.MethodInterceptor接口,但Advice的方法必须返回对象,并且必须定义一个ProceedingJoinPoint参数,例如:
- package com.savage.aop;
- import org.aspectj.lang.ProceedingJoinPoint;
- public class LogAroundAdvice {
- public void invoke(ProceedingJoinPoint joinPoint) {
- System.out.println("Logging before " + joinPoint.getSignature().getName());
- Object retVal = joinPoint.proceed();
- System.out.println("Logging after " + joinPoint.getSignature().getName());
- return retVal;
- }
- }
XML中的设置如下:
- <?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:aop="http://www.springframework.org/schema/aop"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
- <bean id="messageSender" class="com.savage.aop.HttpMessageSender"></bean>
- <bean id="logAroundAdvice" class="com.savage.aop.LogAroundAdvice"></bean>
- <aop:config>
- <aop:aspect id="logAround" ref="logAroundAdvice">
- <aop:around
- pointcut="execution(* com.savage.aop.MessageSender.*(..))"
- method="invoke"/>
- </aop:aspect>
- </aop:config>
- </beans>
六、Around Advice:基于Annotation
和Before Advice相似,使用@Around来表示Around Advice:
- package com.savage.aop;
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.aspectj.lang.annotation.Aspect;
- import org.aspectj.lang.annotation.Around;
- @Aspect
- public class AfterReturningAdvice {
- @Around("execution(* com.savage.aop.MessageSender.*(..))")
- public void invoke(ProceedingJoinPoint joinPoint) {
- System.out.println("Logging before " + joinPoint.getSignature().getName());
- Object retVal = joinPoint.proceed();
- System.out.println("Logging after " + joinPoint.getSignature().getName());
- return retVal;
- }
- }
XML文件中的设置与LogBeforeAdvice的相似(将logBeforeAdvice的定义改为logAroundAdvice的定义),不再列举。
七、Throw Advice:基于XML Sechma
在Spring 2.0中,Throw Advice不用实现org.springframework.aop.ThrowsAdvice接口,但Advice的方法必须定义Throwable(或其子类)参数,例如:
- package com.savage.aop;
- import org.aspectj.lang.JoinPoint;
- public class LogThrowingAdvice {
- public void afterThrowing (JoinPoint joinPoint, Throwable throwable) {
- System.out.println("Logging when throwing " + joinPoint.getSignature().getName());
- }
- }
在XML的设置如下:
- <?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:aop="http://www.springframework.org/schema/aop"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
- <bean id="messageSender" class="com.savage.aop.HttpMessageSender"></bean>
- <bean id="logThrowingAdvice" class="com.savage.aop.LogThrowingAdvice"></bean>
- <aop:config>
- <aop:aspect id="logThrowing" ref="logThrowingAdvice">
- <aop:after-throwing
- pointcut="execution(* com.savage.aop.MessageSender.*(..))"
- throwing="throwable"
- method="afterThrowing"/>
- </aop:aspect>
- </aop:config>
- </beans>
在<aop:after-throwing></aop:after-throwing>中必须定义throwing属性,指定方法中的throwable参数。Spring将根据异常类型决定是否调用afterThrowing方法。
八、Throw Advice:基于Annotation
- package com.savage.aop;
- import org.aspectj.lang.JoinPoint;
- import org.aspectj.lang.annotation.Aspect;
- import org.aspectj.lang.annotation.AfterThrowing;
- @Aspect
- public class AfterThrowingAdvice {
- @AfterThrowing(pointcut="execution(* com.savage.aop.MessageSender.*(..))",
- throwing="throwable")
- public void afterThrowing(JoinPoint joinPoint, Throwable throwable) {
- System.out.println("Logging when throwing "
- + joinPoint.getSignature().getName());
- }
- }
XML文件中的设置与LogBeforeAdvice的相似(将logBeforeAdvice的定义改为logThrowingAdvice的定义),不再列举。
AOP实现(三)——Spring 2.0中Pointcut的定义
在Spring 2.0中,Pointcut的定义包括两个部分:Pointcut表示式(expression)和Pointcut签名(signature)。让我们先看看execution表示式的格式:
- execution(modifier-pattern?
- ret-type-pattern
- declaring-type-pattern?
- name-pattern(param-pattern)
- throws-pattern?)
括号中各个pattern分别表示修饰符匹配(modifier-pattern?)、返回值匹配(ret-type-pattern)、类路径匹配(declaring-type-pattern?)、方法名匹配(name-pattern)、参数匹配((param-pattern))、异常类型匹配(throws-pattern?),其中后面跟着“?”的是可选项。
在各个pattern中可以使用“*”来表示匹配所有。在(param-pattern)中,可以指定具体的参数类型,多个参数间用“,”隔开,各个也可以用“*”来表示匹配任意类型的参数,如(String)表示匹配一个String参数的方法;(*,String)表示匹配有两个参数的方法,第一个参数可以是任意类型,而第二个参数是String类型;可以用(..)表示零个或多个任意参数。
现在来看看几个例子:
1)execution(* *(..))
表示匹配所有方法
2)execution(public * com. savage.service.UserService.*(..))
表示匹配com.savage.server.UserService中所有的公有方法
3)execution(* com.savage.server..*.*(..))
表示匹配com.savage.server包及其子包下的所有方法
除了execution表示式外,还有within、this、target、args等Pointcut表示式。一个Pointcut定义由Pointcut表示式和Pointcut签名组成,例如:
- //Pointcut表示式
- @Pointcut("execution(* com.savage.aop.MessageSender.*(..))")
- //Point签名
- private void log(){}
然后要使用所定义的Pointcut时,可以指定Pointcut签名,如
- @Before("og()")
上面的定义等同与:
- @Before("execution(* com.savage.aop.MessageSender.*(..))")
Pointcut定义时,还可以使用&&、||、!运算,如:
- @Pointcut("execution(* com.savage.aop.MessageSender.*(..))")
- private void logSender(){}
- @Pointcut("execution(* com.savage.aop.MessageReceiver.*(..))")
- private void logReceiver(){}
- @Pointcut("logSender() || logReceiver()")
- private void logMessage(){}
这个例子中,logMessage()将匹配任何MessageSender和MessageReceiver中的任何方法。
还可以将一些公用的Pointcut放到一个类中,以供整个应用程序使用,如:
- package com.savage.aop;
- import org.aspectj.lang.annotation.*;
- public class Pointcuts {
- @Pointcut("execution(* *Message(..))")
- public void logMessage(){}
- @Pointcut("execution(* *Attachment(..))")
- public void logAttachment(){}
- @Pointcut("execution(* *Service.*(..))")
- public void auth(){}
- }
在使用这些Pointcut时,指定完整的类名加上Pointcut签名就可以了,如:
- package com.savage.aop;
- import org.aspectj.lang.JoinPoint;
- import org.aspectj.lang.annotation.*;
- @Aspect
- public class LogBeforeAdvice {
- @Before("com.sagage.aop.Pointcuts.logMessage()")
- public void before(JoinPoint joinPoint) {
- System.out.println("Logging before " + joinPoint.getSignature().getName());
- }
- }
当基于XML Sechma实现Advice时,如果Pointcut需要被重用,可以使用<aop:pointcut></aop:pointcut>来声明Pointcut,然后在需要使用这个Pointcut的地方,用pointcut-ref引用就行了,如:
- <aop:config>
- <aop:pointcut id="log"
- expression="execution(* com.savage.simplespring.bean.MessageSender.*(..))"/>
- <aop:aspect id="logging" ref="logBeforeAdvice">
- <aop:before pointcut-ref="log" method="before"/>
- <aop:after-returning pointcut-ref="log" method="afterReturning"/>
- </aop:aspect>
- </aop:config>