Spring中AOP的实现 (一)

在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的逻辑,接口定义如下:
java 代码
  1. package org.springframework.aop;
  2. public interface MethodBeforeAdvice extends BeforeAdvice {
  3.    void before(Method method, Object[] args, Object target) throws Throwable;
  4. }

其中BeforeAdvice继承自Adivce接口,这两者都是标签接口,并没有定义任何具体的方法。before方法会在目标对象的指定方法执行之前被执行,在before方法种,你可以取得指定方法的Method实例、参数列表和目标对象,在before方法执行完后,目标对象上的方法将会执行,除非在before方法种抛出异常。
下面通过例子来说明Before Advice的使用方法。首先定义目标对象所要实现的接口:
java 代码
  1. package com.savage.aop
  2. public interface MessageSender {
  3.    void send(String message);
  4. }

接着实现MessageSender接口:
java 代码
  1. package com.savage.aop;
  2. public class HttpMessageSender implements MessageSender {
  3. public void send(String message) {
  4. System.out.println("Send Message[" + message + "] by http.");
  5. }
  6. }

OK,我们的业务代码实现完了,现在如果要在不改变我们的业务代码的前提下,在执行业务代码前要记录一些日志,这时就可以通过实现MethodBeforeAdvice接口来实现,如:
java 代码
  1. package com.savage.aop;
  2. import java.lang.reflect.Method;
  3. import org.springframework.aop.framework.MethodBeforeAdvice;
  4. public class LogBeforeAdvice implements MethodAdvice {
  5.    public void before(Method method, Object[] args, Object target) throws Throwable {
  6.       System.out.println("Log before " + method + " by LogBeforeAdvice.");
  7.    }
  8. }

然后再在XML进行如下定义:
xml 代码
 
  1. <?xml version="1.0" encoding="UTF-8"?>    
  2. <beans xmlns="http://www.springframework.org/schema/beans"    
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans    
  5. http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">    
  6.   
  7.     <bean id="messageSenderImpl" class="com.savage.aop.HttpMessageSender"></bean>    
  8.       
  9.     <bean id="logBeforeAdvice" class="com.savage.aop.LogBeforeAdvice"></bean>    
  10.       
  11.     <bean id="messageSender" class="org.springframework.aop.framework.ProxyFactoryBean">    
  12.         <property name="proxyInterfaces" value="com.savage.aop.MessageSender"/>    
  13.         <property name="target" ref="messageSenderImpl"/>    
  14.         <property name="interceptorNames">    
  15.             <list>    
  16.                 <value>logBeforeAdvice</value>    
  17.             </list>    
  18.         </property>    
  19.     </bean>    
  20. </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中定义的方法。
现在我们写一个程序来验证下:
java 代码
  1. package com.savage.aop;
  2. import org.springframework.context.ApplicationContext;
  3. import org.springframework.context.support.ClassPathXmlApplication;
  4. public class AdviceDemo {
  5.    public void main(String[] args) {
  6.       ApplicationContext context = new ClassPathXmlApplicationContext("beans-config.xml");
  7.       MessageSender sender = (MessageSender)context.getBean("messageSender");
  8.       sender.send("message");
  9.   }
  10. }

执行结果:
Log before public abstract void com.savage.simplespring.bean.MessageSender.send(java.lang.String) by LogBeforeAdvice.
Send Message[message] by http.

正如你所看到的,在执行 MessageSendersend方法前先执行了 LogBeforeAdvice的方法!在这个例子中,记录日志的代码并没有横切到我们的业务代码中, LogBeforeAdviceHttpMessageSender彼此不知道对方的存在,而且我们的应用程序 AdviceDemoLogBeforeAdvice的存在也是一无所知。假如有一天我们的应用程序不需要再业务代码执行前记录日志了,只需要修改 XML文件中的定义,而不用更改 AdviceDemo的代码:
xml 代码
  1. <bean id="messageSender" class="com.savage.aop.HttpMessageSender"><!---->bean>

2After Advice
After Advice会在目标对象的方法执行完后执行,你可以通过实现 org.springframework.aop.AfterReturingAdvice接口来实现 After Advice的逻辑, AfterReturingAdvice接口定义如下:
java 代码
  1. package org.springframework.aop;
  2. public interface AfterReturningAdvice {
  3.    void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;
  4. }

afterReturning方法中,你可以获得目标方法执行后的返回值、目标方法对象、目标方法的参数以及目标对象。
继续以上面的例子为例,如果要在 MessageSendersend方法执行完后,要再记录日志,那么我们可以先实现 AfterReturningAdvice接口:
java 代码
  1. package com.savage.aop;
  2. import org.springframework.aop;
  3. public LogAfterAdvice implements AfterReturningAdvice {
  4.    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
  5.       System.out.println("Log after " + method + " by LogAfterAdvice.");
  6.    }
  7. }

然后在 XML文件中指定 LogAfterAdvice的实例:
xml 代码
 
  1. <?xml version="1.0" encoding="UTF-8"?>    
  2. <beans xmlns="http://www.springframework.org/schema/beans"    
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans    
  5. http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">    
  6.   
  7.     <bean id="messageSenderImpl" class="com.savage.aop.HttpMessageSender"></bean>    
  8.       
  9.     <bean id="logBeforeAdvice" class="com.savage.aop.LogBeforeAdvice"></bean>    
  10.       
  11.     <bean id="messageSender" class="org.springframework.aop.framework.ProxyFactoryBean">    
  12.         <property name="proxyInterfaces" value="com.savage.aop.MessageSender"/>    
  13.         <property name="target" ref="messageSenderImpl"/>    
  14.         <property name="interceptorNames">    
  15.             <list>     
  16.                 <value>logAfterAdvice</value>  
  17.             </list>    
  18.         </property>    
  19.     </bean>    
  20. </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.


3Around Advice
在上面的 LogAfterAdvice例子中,我们通过指定 BeforeAdviceAfterReturingAdvice,在 MessageSendersend方法前后执行额外的业务。实际上,如果需要在业务代码执行前后增加额外的服务,你可以直接通过实现 org.aopalliance.intercept.MethodInterceptor接口来达到这一目的, MethodInterceptor定义如下:
java 代码
  1. package org.aopalliance.intercept;
  2. public interface MethodInterceptor {
  3.    public Object invoke(MethodInvocation methodInvocation) throws Throwable;
  4. }

例如:
java 代码
  1. package com.savage.aop;
  2. import org.aopalliance.intercept.MethodInterceptor;
  3. import org.aopalliance.intercept.MethodInvocation;
  4. public class LogAdvice implements MethodInterceptor {
  5.    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
  6.       System.out.println("Log before " + methodInvocation.getMethod() + " by LogAdvice.");
  7.       Object retValue = methodInvocation.proceed();
  8.       System.out.println("Log after " + methodInvocation.getMethod() + " by LogAdvice.");
  9.       return retValue;
  10.    }
  11. }

正如上面所示,在 MethodInterceptor中你得自行决定是否调用 MethodInvocationproceed()方法来执行目标对象上的方法, proceed()方法在执行完后会返回目标对象上方法的执行结果。
MethodInterceptorXML文件中的定义如下: 
xml 代码
 
  1. <?xml version="1.0" encoding="UTF-8"?>    
  2. <beans xmlns="http://www.springframework.org/schema/beans"    
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans    
  5. http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">    
  6.   
  7.     <bean id="messageSenderImpl" class="com.savage.aop.HttpMessageSender"></bean>    
  8.       
  9.     <bean id="logAdvice" class="com.savage.aop.LogAdvice"></bean>    
  10.       
  11.     <bean id="messageSender" class="org.springframework.aop.framework.ProxyFactoryBean">  
  12.         <property id="proxyInterfaces" value="com.savage.aop.MessageSender"/>    
  13.         <property id="target" ref="messageSenderImpl"/>  
  14.         <property id="interceptorNames">  
  15.             <list>  
  16.                 <value>logAdvice</value>  
  17.             </list>  
  18.         </property>  
  19.     </bean>  
  20. </beans>  


Spring在真正执行目标对象的方法前,会执行 interceptorNames中执行的 Advice,每个 Advice在执行完自己的业务后,会调用 MethodInvocationproceed()方法,将执行的主动权移交给下一个 Advice,直到没有下一个 Advice为止,在执行完目标对象的方法后, Spring会再以相反的顺序一层层的返回。例如:
xml 代码
 
  1. <bean id="messageSender" class="org.springframework.aop.framework.ProxyFactoryBean">  
  2.     <property id="proxyInterfaces" value="com.savage.aop.MessageSender"/>    
  3.     <property id="target" ref="messageSenderImpl"/>  
  4.     <property id="interceptorNames">  
  5.         <list>  
  6.             <value>logBeforeAdvice</value>  
  7.             <value>logAdvice</value>  
  8.             <value>logAfterAdvice</value>  
  9.         </list>  
  10.     </property>  
  11. </bean>  


象上面这个例子, logBeforeAdvice先会被执行,然后执行 logAdvice,接着执行 logAfterAdvice,最后又返回到了 logAdvice
现在我们把 LogAdvice作一下简单的修改,增加一个 id属性,用以在后面查看 Advice的调用顺序:
java 代码
  1. package com.savage.aop;
  2. import org.aopalliance.intercept.MethodInterceptor;
  3. import org.aopalliance.intercept.MethodInvocation;
  4. public class LogAdvice implements MethodInterceptor {
  5.    private static int INSTANCE_NUM = 0;
  6.    private int id;
  7.    public LogAdvice() {
  8.       id = ++INSTANCE_NUM;
  9.    }
  10.    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
  11.       System.out.println("Log before " + methodInvocation.getMethod() + " by LogAdvice[" + id + "].");
  12.       Object retValue = methodInvocation.proceed();
  13.       System.out.println("Log after " + methodInvocation.getMethod() + " by LogAdvice[" + id + "].");
  14.       return retValue;
  15.    }
  16. }

同时把 XML中的定义改为:
xml 代码
 
  1. <?xml version="1.0" encoding="UTF-8"?>    
  2. <beans xmlns="http://www.springframework.org/schema/beans"    
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans    
  5. http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">    
  6.   
  7.     <bean id="messageSenderImpl" class="com.savage.aop.HttpMessageSender"></bean>    
  8.       
  9.     <bean id="logBeforeAdvice" class="com.savage.aop.LogBeforeAdvice"></bean>    
  10.     <bean id="logAfterAdvice" class="com.savage.aop.LogAfterAdvice"></bean>    
  11.     <bean id="logAdvice1" class="com.savage.aop.LogAdvice"></bean>    
  12.     <bean id="logAdvice2" class="com.savage.aop.LogAdvice"></bean>    
  13.       
  14.     <bean id="messageSender" class="org.springframework.aop.framework.ProxyFactoryBean">  
  15.         <property id="proxyInterfaces" value="com.savage.aop.MessageSender"/>    
  16.         <property id="target" ref="messageSenderImpl"/>  
  17.         <property id="interceptorNames">  
  18.             <list>  
  19.                 <value>logBeforeAdvice</value>  
  20.                 <value>logAdvice1</value>  
  21.                 <value>logAfterAdvice</value>  
  22.                 <value>logAdvice2</value>  
  23.             </list>  
  24.         </property>  
  25.     </bean>  
  26. </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的格式如下:
java 代码
  1. afterThrowing([Method],[args],[target],subClassOfThrowable);

方括号 []中的参数为可选项,但方法中必须有 subClassOfThrowable,且必须是 Throwable的子类。
Spring在调用完 afterThrowing方法后,原先的异常会继续在程序中传播,如果象要终止程序对异常的处理,只能在 afterThrowing方法中抛出其他异常。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值