Spring中的AOP

 

Spring中的AOP

Written by Tony Jiang @ 2012-1-18

(转)何为AOP

AOP,面向切面编程。

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

来一个简单的Sample:

目标类:

[java]  view plain copy print ?
  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]  view plain copy print ?
  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]  view plain copy print ?
  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]  view plain copy print ?
  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]  view plain copy print ?
  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 plain copy print ?
  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.                 }  

 

3.  被切面改造过的instance的生成

我们以单例举例:

[java]  view plain copy print ?
  1. /** 
  2.      * Return the singleton instance of this class's proxy object, 
  3.      * lazily creating it if it hasn't been created already. 
  4.      * @return the shared singleton proxy 
  5.      */  
  6.     private synchronized Object getSingletonInstance() {  
  7.         if (this.singletonInstance == null) {  
  8.             this.targetSource = freshTargetSource();  
  9.             if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {  
  10.                 // Rely on AOP infrastructure to tell us what interfaces to proxy.  
  11.                 Class targetClass = getTargetClass();  
  12.                 if (targetClass == null) {  
  13.                     throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");  
  14.                 }  
  15.                 setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));  
  16.             }  
  17.             // Initialize the shared singleton instance.  
  18.             super.setFrozen(this.freezeProxy);  
  19.             this.singletonInstance = getProxy(createAopProxy());  
  20.         }  
  21.         return this.singletonInstance;  
  22.     }  

 

AopProxy是最终生成instance的地方,但是它是接口,和框架分离开来了

接口的生成在父类的ProxyCreatorSupport中

[java]  view plain copy print ?
  1. /** 
  2.      * Subclasses should call this to get a new AOP proxy. They should <b>not</b> 
  3.      * create an AOP proxy with <code>this</code> as an argument. 
  4.      */  
  5.     protected final synchronized AopProxy createAopProxy() {  
  6.         if (!this.active) {  
  7.             activate();  
  8.         }  
  9.         return getAopProxyFactory().createAopProxy(this);  
  10.     }  
  11. createAopProxy的实际运行代码:  
  12. public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {  
  13.         if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {  
  14.             Class targetClass = config.getTargetClass();  
  15.             if (targetClass == null) {  
  16.                 throw new AopConfigException("TargetSource cannot determine target class: " +  
  17.                         "Either an interface or a target is required for proxy creation.");  
  18.             }  
  19.             if (targetClass.isInterface()) {  
  20.                 return new JdkDynamicAopProxy(config);  
  21.             }  
  22.             if (!cglibAvailable) {  
  23.                 throw new AopConfigException(  
  24.                         "Cannot proxy target class because CGLIB2 is not available. " +  
  25.                         "Add CGLIB to the class path or specify proxy interfaces.");  
  26.             }  
  27.             return CglibProxyFactory.createCglibProxy(config);  
  28.         }  
  29.         else {  
  30.             return new JdkDynamicAopProxy(config);  
  31.         }  
  32.     }  

 

大家注意一下,这里根据targetClass的类型判断,采用JDK Proxy还是CGLIB模式生成动态对象

 

4.  JDK Proxy的生成

如果大家使用过原生的JDK PROXY,下面的代码是在熟悉不过了

JdkDynamicAopProxy中实例化instance的

[java]  view plain copy print ?
  1. public Object getProxy(ClassLoader classLoader) {  
  2.         if (logger.isDebugEnabled()) {  
  3.             logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());  
  4.         }  
  5.         Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);  
  6.         findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);  
  7.         return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);  
  8.     }  

 

JdkDynamicAopProxy中的invoke方法

[java]  view plain copy print ?
  1. // Get the interception chain for this method.  
  2.             List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);  
  3. // Check whether we have any advice. If we don't, we can fallback on direct  
  4.             // reflective invocation of the target, and avoid creating a MethodInvocation.  
  5.             if (chain.isEmpty()) {  
  6.                 // We can skip creating a MethodInvocation: just invoke the target directly  
  7.                 // Note that the final invoker must be an InvokerInterceptor so we know it does  
  8.                 // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.  
  9.                 retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);  
  10.             }  
  11.             else {  
  12.                 // We need to create a method invocation...  
  13.                 invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);  
  14.                 // Proceed to the joinpoint through the interceptor chain.  
  15.                 retVal = invocation.proceed();  
  16.             }  

 

Proceed的真实代码,把对象本身的invoke和拦截器的invoke交织在一起

[java]  view plain copy print ?
  1. public Object proceed() throws Throwable {  
  2.         //  We start with an index of -1 and increment early.  
  3.         if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {  
  4.             return invokeJoinpoint();  
  5.         }  
  6.   
  7.         Object interceptorOrInterceptionAdvice =  
  8.             this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);  
  9.         if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {  
  10.             // Evaluate dynamic method matcher here: static part will already have  
  11.             // been evaluated and found to match.  
  12.             InterceptorAndDynamicMethodMatcher dm =  
  13.                 (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;  
  14.             if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {  
  15.                 return dm.interceptor.invoke(this);  
  16.             }  
  17.             else {  
  18.                 // Dynamic matching failed.  
  19.                 // Skip this interceptor and invoke the next in the chain.  
  20.                 return proceed();  
  21.             }  
  22.         }  
  23.         else {  
  24.             // It's an interceptor, so we just invoke it: The pointcut will have  
  25.             // been evaluated statically before this object was constructed.  
  26.             return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);  
  27.         }  
  28.     }  

 

拦截器是如何编织的

我们在JdkDynamicAopProxy中的invoke方法中看到如下拦截器链条的生成

[java]  view plain copy print ?
  1. // Get the interception chain for this method.  
  2.             List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);  

 

它对应的代码是DefaultAdvisorChainFactory中的getInterceptorsAndDynamicInterceptionAdvice方法

[java]  view plain copy print ?
  1. AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();  
  2.         for (Advisor advisor : config.getAdvisors()) {  
  3.             if (advisor instanceof PointcutAdvisor) {  
  4.                 // Add it conditionally.  
  5.                 PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;  
  6.                 if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {  
  7.                     MethodInterceptor[] interceptors = registry.getInterceptors(advisor);  

 

最终advisor的登记由DefaultAdvisorAdapterRegistry完成

我们可以看到DefaultAdvisorAdapterRegistry中首先登记了所有的AdviceAdapter

[java]  view plain copy print ?
  1. /** 
  2.      * Create a new DefaultAdvisorAdapterRegistry, registering well-known adapters. 
  3.      */  
  4.     public DefaultAdvisorAdapterRegistry() {  
  5.         registerAdvisorAdapter(new MethodBeforeAdviceAdapter());  
  6.         registerAdvisorAdapter(new AfterReturningAdviceAdapter());  
  7.         registerAdvisorAdapter(new ThrowsAdviceAdapter());  
  8.     }  

 

在如下代码中按照AdviceAdapter的类型塞入责任练中

[java]  view plain copy print ?
  1. public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {  
  2.         List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);  
  3.         Advice advice = advisor.getAdvice();  
  4.         if (advice instanceof MethodInterceptor) {  
  5.             interceptors.add((MethodInterceptor) advice);  
  6.         }  
  7.         for (AdvisorAdapter adapter : this.adapters) {  
  8.             if (adapter.supportsAdvice(advice)) {  
  9.                 interceptors.add(adapter.getInterceptor(advisor));  
  10.             }  
  11.         }  
  12.         if (interceptors.isEmpty()) {  
  13.             throw new UnknownAdviceTypeException(advisor.getAdvice());  
  14.         }  
  15.         return interceptors.toArray(new MethodInterceptor[interceptors.size()]);  
  16.     }  

 

这种做法类似于:

1.  首先为切入Adivsor建立责任链模式

2.       其次将每个Adivsor委托给DefaultAdvisorAdapterRegistry登录

3.       在DefaultAdvisorAdapterRegistry中封装Adivsor到各个专门的Advisor适配器中

比如,AfterReturningAdviceAdapter的代码如下:

[java]  view plain copy print ?
  1. /** 
  2.  * Adapter to enable {@link org.springframework.aop.AfterReturningAdvice} 
  3.  * to be used in the Spring AOP framework. 
  4.  * 
  5.  * @author Rod Johnson 
  6.  * @author Juergen Hoeller 
  7.  */  
  8. class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {  
  9.   
  10.     public boolean supportsAdvice(Advice advice) {  
  11.         return (advice instanceof AfterReturningAdvice);  
  12.     }  
  13.   
  14.     public MethodInterceptor getInterceptor(Advisor advisor) {  
  15.         AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();  
  16.         return new AfterReturningAdviceInterceptor(advice);  
  17.     }  
  18.   
  19. }  

 

4.       在适配器中,将Advisor的方法和目标类方法交织在一起

[java]  view plain copy print ?
  1. **  
  2.  * Interceptor to wrap am {@link org.springframework.aop.AfterReturningAdvice}.  
  3.  * Used internally by the AOP framework; application developers should not need  
  4.  * to use this class directly.  
  5.  *  
  6.  * @author Rod Johnson  
  7.  */  
  8. public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {  
  9.   
  10.     private final AfterReturningAdvice advice;  
  11.   
  12.   
  13.     /** 
  14.      * Create a new AfterReturningAdviceInterceptor for the given advice. 
  15.      * @param advice the AfterReturningAdvice to wrap 
  16.      */  
  17.     public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {  
  18.         Assert.notNull(advice, "Advice must not be null");  
  19.         this.advice = advice;  
  20.     }  
  21.   
  22.     public Object invoke(MethodInvocation mi) throws Throwable {  
  23.         Object retVal = mi.proceed();  
  24.         this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());  
  25.         return retVal;  
  26.     }  
  27.   
  28. }  

 

 

你可能感兴趣的:(spring,AOP)