Spring中的AOP


何为AOP

AOP,面向切面编程。

在不改动代码的前提下,灵活的在现有代码的执行顺序前后,添加进新规机能。

来一个简单的Sample:

目标类:

[java]
  1. package com.hyron.tony;  
  2.   
  3. public class CustomerService {  
  4.     private String name;  
  5.     private String url;  
  6.   
  7.     public void setName(String name) {  
  8.         this.name = name;  
  9.     }  
  10.   
  11.     public void setUrl(String url) {  
  12.         this.url = url;  
  13.     }  
  14.   
  15.     public void printName() {  
  16.         System.out.println("Customer name : " + this.name);  
  17.     }  
  18.   
  19.     public void printURL() {  
  20.         System.out.println("Customer website : " + this.url);  
  21.     }  
  22.   
  23.     public void printThrowException() {  
  24.         throw new IllegalArgumentException();  
  25.     }  
  26.   
  27. }  

advice:只以Around advice为例

[java]
  1. import java.util.Arrays;  
  2.   
  3. import org.aopalliance.intercept.MethodInterceptor;  
  4. import org.aopalliance.intercept.MethodInvocation;  
  5.   
  6. public class HijackAroundMethod implements MethodInterceptor {  
  7.     @Override  
  8.     public Object invoke(MethodInvocation methodInvocation) throws Throwable {  
  9.   
  10.         System.out.println("Method name : "  
  11.                 + methodInvocation.getMethod().getName());  
  12.         System.out.println("Method arguments : "  
  13.                 + Arrays.toString(methodInvocation.getArguments()));  
  14.   
  15.         // same with MethodBeforeAdvice   
  16.         System.out.println("HijackAroundMethod : Before method hijacked!");  
  17.   
  18.         try {  
  19.             // proceed to original method call   
  20.             Object result = methodInvocation.proceed();  
  21.   
  22.             // same with AfterReturningAdvice   
  23.             System.out.println("HijackAroundMethod : Before after hijacked!");  
  24.   
  25.             return result;  
  26.   
  27.         } catch (IllegalArgumentException e) {  
  28.             // same with ThrowsAdvice   
  29.             System.out  
  30.                     .println("HijackAroundMethod : Throw exception hijacked!");  
  31.             throw e;  
  32.         }  
  33.     }  
  34. }  

编织切入关系的配置文件:

[html]
  1. <bean id="customerService" class="com.mkyong.customer.services.CustomerService">  
  2.         <property name="name" value="Yong Mook Kim" />  
  3.         <property name="url" value="http://www.mkyong.com" />  
  4.     </bean>  
  5.   
  6.     <bean id="hijackAroundMethodBean" class="com.mkyong.aop.HijackAroundMethod" />  
  7.   
  8.     <bean id="customerServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">  
  9.   
  10.         <property name="target" ref="customerService" />  
  11.   
  12.         <property name="interceptorNames">  
  13.             <list>  
  14.                 <value>hijackAroundMethodBean</value>  
  15.             </list>  
  16.         </property>  
  17.     </bean>  

Sample的启动:

[java]
  1. import org.springframework.context.ApplicationContext;  
  2. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  3.   
  4. import com.mkyong.customer.services.CustomerService;  
  5.   
  6. public class App {  
  7.     public static void main(String[] args) {  
  8.         ApplicationContext appContext = new ClassPathXmlApplicationContext(  
  9.                 new String[] { "Spring-Customer.xml" });  
  10.   
  11.         CustomerService cust = (CustomerService) appContext  
  12.                 .getBean("customerServiceProxy");  
  13.   
  14.         System.out.println("*************************");  
  15.         cust.printName();  
  16.         System.out.println("*************************");  
  17.         cust.printURL();  
  18.         System.out.println("*************************");  
  19.         try {  
  20.             cust.printThrowException();  
  21.         } catch (Exception e) {  
  22.   
  23.         }  
  24.   
  25.     }  
  26. }  

以上代码,用customerServiceProxy代理CustomerService的执行

在customerServiceProxy的配置中,定义了用hijackAroundMethodBean作为方法拦截器,在hijackAroundMethodBean中利用invoke方法,拦截住所有的方法调用,塞入自己的逻辑业务。

AOP的两种实现

                上面看到的是Spring的Sample。

                其实,Spring的AOP也是调用了其他开源技术实现。

                比较常用的是JDK自己的Proxy,和开源的CGLIB

                两者的区别,Proxy需要Advice必须从接口继承过来。如果切入的目标物是实体类,则无法使用。

                CGLIB则可以用于直接覆盖实体类的方法。

                Spring对以上两种都有支持。

Spring的底层实现

                Spring在配置文件中,通过ProxyFactoryBean编织和实现了切面的构成。

                我们在执行以下这行话的时候

CustomerService cust = (CustomerService) appContext

                                                                .getBean("customerServiceProxy");

 

                其实是将动态对象的生成委托给了ProxyFactoryBean

当配置文件中 <bean>的class属性配置的实现类是FactoryBean时,通过getBean方法返回的不是FactoryBean本身,而是 FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方 法。

 

执行顺序如下:

1.  ProxyFactoryBean中的getObject

[java]
  1. /** 
  2.      * Return a proxy. Invoked when clients obtain beans from this factory bean. 
  3.      * Create an instance of the AOP proxy to be returned by this factory. 
  4.      * The instance will be cached for a singleton, and create on each call to 
  5.      * <code>getObject()</code> for a proxy. 
  6.      * @return a fresh AOP proxy reflecting the current state of this factory 
  7.      */  
  8.     public Object getObject() throws BeansException {  
  9.         initializeAdvisorChain();  
  10.         if (isSingleton()) {  
  11.             return getSingletonInstance();  
  12.         }  
  13.         else {  
  14.             if (this.targetName == null) {  
  15.                 logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +  
  16.                         "Enable prototype proxies by setting the 'targetName' property.");  
  17.             }  
  18.             return newPrototypeInstance();  
  19.         }  
  20.     }  

2.  ProxyFactoryBean中的initializeAdvisorChain

从配置文件中的advice list中取得interceptorNames,并将其加入advisorChain

[java] view plaincopyprint?
  1. for (String name : this.interceptorNames) {  
  2.                 if (logger.isTraceEnabled()) {  
  3.                     logger.trace("Configuring advisor or advice '" + name + "'");  
  4.                 }  
  5.   
  6.                 if (name.endsWith(GLOBAL_SUFFIX)) {  
  7.                     if (!(this.beanFactory instanceof ListableBeanFactory)) {  
  8.                         throw new AopConfigException(  
  9.                                 "Can only use global advisors or interceptors with a ListableBeanFactory");  
  10.                     }  
  11.                     addGlobalAdvisor((ListableBeanFactory) this.beanFactory,  
  12.                             name.substring(0, name.length() - GLOBAL_SUFFIX.length()));  
  13.                 }  
  • 1
  • 2
  • 下一页

相关内容