歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

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.                 }  
Copyright © Linux教程網 All Rights Reserved