spring源码分析(二)Aop

创建日期:2016.08.19

修改日期:2016.08.20-2016.08.21

交流QQ:992591601

参考资料:《spring源码深度解析》、《spring技术内幕》、传值播客spring教学视频

                http://www.cnblogs.com/xing901022/p/4264334.html

                http://www.cnblogs.com/digdeep/p/4528353.html

 

 

一,动态代理、java InvocationHandler实现、Cglib实现

 

        要了解动态代理,可阅读设计模式相关书籍,不难理解。这篇博文我简单解释,动态代理就是与静态代理相对应的,在程序运行过程中动态创建的代理类。代理类可以简单理解为一种对目标类的增强,之后我们要使用目标类的话,只需用代理类就可以了。

        实现动态代理有两种方式,其一是java自带的动态代理功能,另外就是使用Cglib库实现。前者的使用有一个必须的条件,那就是目标类必须实现接口。而后者的使用则是对前者的一种补充。

        假设我们有这样两个bean,其一为AlienFishServiceBean,不实现任何接口。

                                                    其一为FishServiceImpl,实现FishService接口。

        对于前者,我们需要使用Cglib来实现动态代理功能(示例代码,实现功能:当bean的fishName字段不为空时,才能调用该bean的方法):

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. package cn;  
  2.   
  3. import java.lang.reflect.Method;  
  4.   
  5. import cn.aop.service.AlienFishServiceBean;  
  6.   
  7. import net.sf.cglib.proxy.Enhancer;  
  8. import net.sf.cglib.proxy.MethodInterceptor;  
  9. import net.sf.cglib.proxy.MethodProxy;  
  10.   
  11. /** 
  12. * @ClassName: CGlibProxyFactory  
  13. * @Description: CGlibProxyFactory 
  14. * @author 无名 
  15. * @date 2016-8-14 17:31:48  
  16. * @version 1.0 
  17.  */  
  18. public class CGlibProxyFactory implements MethodInterceptor {  
  19.     private Object targetObject;  
  20.       
  21.     public Object createProxyIntance(Object targetObject) {  
  22.         this.targetObject = targetObject;  
  23.         Enhancer enhancer = new Enhancer();  
  24.         enhancer.setSuperclass(this.targetObject.getClass());  
  25.         enhancer.setCallback(this);  
  26.         return enhancer.create();  
  27.     }  
  28.   
  29.     public Object intercept(Object proxy, Method method, Object[] args,  
  30.             MethodProxy  methodProxy) throws Throwable {  
  31.         AlienFishServiceBean bean = (AlienFishServiceBean) this.targetObject;  
  32.         Object result = null;  
  33.         if(bean.getFishName()!=null) {  
  34.             result = methodProxy.invoke(targetObject, args);  
  35.         }  
  36.         return result;  
  37.     }  
  38. }  

          对于后者,我们可以使用java自带的动态代理功能(示例代码,实现功能:当bean的fishName字段不为空时,才能调用该bean的方法):

 

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. package cn;  
  2.   
  3. import java.lang.reflect.InvocationHandler;  
  4. import java.lang.reflect.Method;  
  5. import java.lang.reflect.Proxy;  
  6.   
  7. import cn.aop.service.FishService;  
  8. import cn.aop.service.FishServiceImpl;  
  9.   
  10. /** 
  11. * @ClassName: JDKProxyFactory  
  12. * @Description: JDKProxyFactory 
  13. * @author 无名 
  14. * @date 2016-8-13 下午11:55:31  
  15. * @version 1.0 
  16.  */  
  17. public class JDKProxyFactory implements InvocationHandler {  
  18.       
  19.     private Object targetObject;  
  20.       
  21.     public Object createInstance(Object targetObject) {  
  22.         this.targetObject = targetObject;  
  23.         return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),   
  24.                 this.targetObject.getClass().getInterfaces(), this);  
  25.     }  
  26.   
  27.     @Override  
  28.     public Object invoke(Object proxy, Method method, Object[] args)  
  29.             throws Throwable {  
  30.         FishService fs = (FishServiceImpl)targetObject;  
  31.         Object result = null;  
  32.         if(fs.getFishName() != null) {  
  33.             result = method.invoke(targetObject, args);  
  34.         }  
  35.         return result;  
  36.     }  
  37. }  


       上述两个代理方式使用时,先用代理类工厂创建对应代理类,然后使用代理类即可(此代理类是对目标类的代理,‘增强了’目标类的一些方面)。

 

 

二,Spring Aop



       spring aop需要的jar包:

  org.springframework.aop-xxx.jar(spring),aopalliance-1.0.jar,aspectjrt.jar, aspectjweaver.jar,cglib.jar

 

       spring aop是在动态代理这种设计模式的基础之上的。  

       这里我说下aop的几个基本概念,都是基于我自己的理解,简单粗暴:

              aspect:面,可以理解为一个事务,对该事务做对应处理。

              pointcut:切入点,对应于面具体的切入的地方。

              advice:spring定义了四个advice, BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice。在触及面和点的时候,根据配置,执行对应通知。

        

        spring aop的实现由两种,其一是配置文件方式,另外是注解方式。

        我们首先,用配置文件方式:

        目标类,接口和实现类:

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. package cn.service;  
  2.   
  3. public interface FishService {  
  4.     public void say00();  
  5.     public void say01();  
  6. }  

 

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. package cn.service.impl;  
  2.   
  3. import cn.service.FishService;  
  4.   
  5. public class FishServiceBean implements FishService {  
  6.   
  7.     public void say00() {  
  8.         System.out.println("I am fish 00");  
  9.     }  
  10.   
  11.     public void say01() {  
  12.         System.out.println("I am fish 01");  
  13.     }  
  14.   
  15. }  

         下面的类提供对应的advice

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. package cn.service;  
  2.   
  3. import org.aspectj.lang.ProceedingJoinPoint;  
  4. /** 
  5.  * 切面 
  6.  * 
  7.  */  
  8. public class MyInterceptor {      
  9.     public void doBefore() {  
  10.         System.out.println("before");  
  11.     }  
  12.   
  13.     public void doAfter() {  
  14.         System.out.println("after");  
  15.     }  
  16.       
  17.     public void doFinal() {  
  18.         System.out.println("final");  
  19.     }  
  20.       
  21.     public void doThrowing() {  
  22.         System.out.println("throwing");  
  23.     }  
  24.       
  25.     public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {  
  26.         System.out.println("进入方法");  
  27.         Object result = pjp.proceed();  
  28.         System.out.println("退出方法");  
  29.         return result;  
  30.     }  
  31.       
  32. }  


            配置文件(aop:config内设置aop,切面里设置切入点,及几种advice):

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  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.        xmlns:context="http://www.springframework.org/schema/context"   
  5.        xmlns:aop="http://www.springframework.org/schema/aop"        
  6.        xsi:schemaLocation="http://www.springframework.org/schema/beans  
  7.            http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
  8.            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd  
  9.            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">  
  10.         <aop:aspectj-autoproxy/>   
  11.         <bean id="fishService" class="cn.service.impl.FishServiceBean"></bean>  
  12.         <bean id="aspetbean" class="cn.service.MyInterceptor"/>  
  13.         <aop:config>  
  14.             <aop:aspect id="asp" ref="aspetbean">  
  15.                 <aop:pointcut id="mycut" expression="execution(* cn.service..*.*(..))"/>  
  16.                 <aop:before pointcut-ref="mycut" method="doBefore"/>  
  17.                 <aop:after-returning pointcut-ref="mycut" method="doFinal"/>  
  18.             <aop:after-throwing pointcut-ref="mycut" method="doThrowing"/>  
  19.             <aop:after pointcut-ref="mycut" method="doAfter"/>  
  20.             <aop:around pointcut-ref="mycut" method="doBasicProfiling"/>  
  21.             </aop:aspect>  
  22.         </aop:config>  
  23. </beans>  


          测试类:

 

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. package junit.test;  
  2.   
  3. import org.junit.BeforeClass;  
  4. import org.junit.Test;  
  5. import org.springframework.context.ApplicationContext;  
  6. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  7.   
  8. import cn.service.FishService;  
  9.   
  10. public class SpringAOPTest {  
  11.   
  12.     @BeforeClass  
  13.     public static void setUpBeforeClass() throws Exception {  
  14.     }  
  15.   
  16.     @Test public void interceptorTest(){  
  17.         ApplicationContext cxt = new ClassPathXmlApplicationContext("beans.xml");  
  18.         FishService fishService = (FishService)cxt.getBean("fishService");  
  19.         fishService.say00();  
  20.         fishService.say01();  
  21.     }  
  22. }  


           运行结果:

 



  

 

          与配置文件方式相对应的便是注解的方式

           注解方式只需在spring的xml文件里保留  <aop:aspectj-autoproxy/> 就可以了。

          而上面对应的MyInterceptor类需要多写相对的注解(著名切入点、advice等)。

 

 

三,Spring源码分析

 

       首先自己设想一下spring实现aop的思路,大概是:1,获取、寻找所有bean,如果为AspectJ注解的类,则进行对应处理。

                                                                                        2,对标记为AspectJ注解的类进行增强器的提取(前文提到动态代理是对目标类的一种增强)

                                                                                        3,创建动态代理。

       首先看一个类AopNamespaceHandler,这里是aop注解对应的解析器:

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public class AopNamespaceHandler extends NamespaceHandlerSupport {  
  2.   
  3.     public void init() {  
  4.         // In 2.0 XSD as well as in 2.1 XSD.  
  5.         registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());  
  6.         registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());  
  7.         registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());  
  8.   
  9.         // Only in 2.0 XSD: moved to context namespace as of 2.1  
  10.         registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());  
  11.     }  
  12.   
  13. }  


        我们看registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());这一句,可知其内涵是向NamespaceHandlerSupport这一类的

 

         private final Map<String, BeanDefinitionParser> parsers =
new HashMap<String, BeanDefinitionParser>();    这一map数据结构中注册对应解析器。

      此后 一旦遇到aspectj-autoproxy注解,便自然会从map中取到对应解析器,并使用解析器AspectJAutoProxyBeanDefinitionParser解析。AspectJAutoProxyBeanDefinitionParser是继承了BeanDefinitionParser接口的。

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public BeanDefinition parse(Element element, ParserContext parserContext) {  
  2.     AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);  
  3.     extendBeanDefinition(element, parserContext);  
  4.     return null;  
  5. }  

 

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(  
  2.         ParserContext parserContext, Element sourceElement) {  
  3.   
  4.     BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(  
  5.             parserContext.getRegistry(), parserContext.extractSource(sourceElement));  
  6.     useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);  
  7.     registerComponentIfNecessary(beanDefinition, parserContext);  
  8. }  

       首先看下进入该函数时参数的状态:

        sourceElement的name为aop:aspectj-autoproxy。这个函数就是专门用于注册aop:aspectj注解的。

        这个方法三句代码,各有其作用,分别是:1注册AnnotationAwareAspectJAutoProxyCreator(AOP的实现都靠这个),返回的BeanDefinition设置对应的AnnotationAwareAspectJAutoProxyCreator

                                                                             2处理proxy-target-class(CgLib或jdk)以及expose-proxy属性

                                                                             3注册组件并通知



       从spring的 DefaultAopProxyFactory类入手,该类继承自AopProxyFactory接口。

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. package org.springframework.aop.framework;  
  2.   
  3. /** 
  4.  * Interface to be implemented by factories that are able to create 
  5.  * AOP proxies based on {@link AdvisedSupport} configuration objects. 
  6.  * 
  7.  * <p>Proxies should observe the following contract: 
  8.  * <ul> 
  9.  * <li>They should implement all interfaces that the configuration 
  10.  * indicates should be proxied. 
  11.  * <li>They should implement the {@link Advised} interface. 
  12.  * <li>They should implement the equals method to compare proxied 
  13.  * interfaces, advice, and target. 
  14.  * <li>They should be serializable if all advisors and target 
  15.  * are serializable. 
  16.  * <li>They should be thread-safe if advisors and target 
  17.  * are thread-safe. 
  18.  * </ul> 
  19.  * 
  20.  * <p>Proxies may or may not allow advice changes to be made. 
  21.  * If they do not permit advice changes (for example, because 
  22.  * the configuration was frozen) a proxy should throw an  
  23.  * {@link AopConfigException} on an attempted advice change. 
  24.  * 
  25.  * @author Rod Johnson 
  26.  * @author Juergen Hoeller 
  27.  */  
  28. public interface AopProxyFactory {  
  29.   
  30.     /** 
  31.      * Create an {@link AopProxy} for the given AOP configuration. 
  32.      * @param config the AOP configuration in the form of an 
  33.      * AdvisedSupport object 
  34.      * @return the corresponding AOP proxy 
  35.      * @throws AopConfigException if the configuration is invalid 
  36.      */  
  37.     AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException;  
  38.   
  39. }  


          我们看最核心的createAopProxy方法:

 

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {  
  2.     if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {  
  3.         Class targetClass = config.getTargetClass();  
  4.         if (targetClass == null) {  
  5.             throw new AopConfigException("TargetSource cannot determine target class: " +  
  6.                     "Either an interface or a target is required for proxy creation.");  
  7.         }  
  8.         if (targetClass.isInterface()) {  
  9.             return new JdkDynamicAopProxy(config);  
  10.         }  
  11.         if (!cglibAvailable) {  
  12.             throw new AopConfigException(  
  13.                     "Cannot proxy target class because CGLIB2 is not available. " +  
  14.                     "Add CGLIB to the class path or specify proxy interfaces.");  
  15.         }  
  16.         return CglibProxyFactory.createCglibProxy(config);  
  17.     }  
  18.     else {  
  19.         return new JdkDynamicAopProxy(config);  
  20.     }  
  21. }  

           这段代码逻辑很清晰,判断如果目标类实现接口则使用jdk创建代理,否则使用cglib。

 

           再仔细看这段代码,发现无论是jdk还是cglib,都要使用AdvisedSupport config这个参数。

           设置断点,查看该变量,发现果然信息量很大,对应的AdvisedSupport类代码我就不贴了,看下面截图也可以想象个大概了。

 

具体看下advice的内容,我们配置文件里配置的内容都在里面了:

             至于这个config是怎样生成的,其实很好想象,就是前面讲的,对应注解的解析,注册。

             new JdkDynamicAopProxy(config)

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * Construct a new JdkDynamicAopProxy for the given AOP configuration. 
  3.  * @param config the AOP configuration as AdvisedSupport object 
  4.  * @throws AopConfigException if the config is invalid. We try to throw an informative 
  5.  * exception in this case, rather than let a mysterious failure happen later. 
  6.  */  
  7. public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {  
  8.     Assert.notNull(config, "AdvisedSupport must not be null");  
  9.     if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {  
  10.         throw new AopConfigException("No advisors and no TargetSource specified");  
  11.     }  
  12.     this.advised = config;  
  13. }  

             CglibProxyFactory.createCglibProxy(config)

 

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * Create a new Cglib2AopProxy for the given AOP configuration. 
  3.  * @param config the AOP configuration as AdvisedSupport object 
  4.  * @throws AopConfigException if the config is invalid. We try to throw an informative 
  5.  * exception in this case, rather than let a mysterious failure happen later. 
  6.  */  
  7. public Cglib2AopProxy(AdvisedSupport config) throws AopConfigException {  
  8.     Assert.notNull(config, "AdvisedSupport must not be null");  
  9.     if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {  
  10.         throw new AopConfigException("No advisors and no TargetSource specified");  
  11.     }  
  12.     this.advised = config;  
  13.     this.advisedDispatcher = new AdvisedDispatcher(this.advised);  
  14. }  

        都是用config设置对应的advise属性。

 

        小小总结:最后我又单步调试一通,结合上一章节内容《spring源码分析(一)IoC、DI》,发现AOP代理类的创建时CreateBean这一阶段开始的,其实这时候就是对应目标类用代理类取代了。

        这是断点运行时,观察createBean返回的结果,可见创建的fishService bean实质上是JdkDynamicAopProxy代理类。在那之后我们使用fishService实际上就是使用对应的JdkDynamicAopProxy代理类了,之前注册的那些advice也就生效了:

 

        (在这一过程中值得一提的还有AbstractAutoProxyCreator的postProcessAfterInitialization方法,正如注释所说,作用是Create a proxy with the configured interceptors if   the bean is  identified as one to proxy by the subclass.

 

                                                           AnnotationAwareAspectJAutoProxyCreator的findCandidateAdvisors方法,作用是获取增强器)

 

posted on 2016-08-21 11:00  J·Marcus  阅读(477)  评论(0编辑  收藏  举报

导航