zoukankan      html  css  js  c++  java
  • 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方法,作用是获取增强器)

  • 相关阅读:
    Security headers quick reference Learn more about headers that can keep your site safe and quickly look up the most important details.
    Missing dollar riddle
    Where Did the Other Dollar Go, Jeff?
    proteus 与 keil 联调
    cisco router nat
    router dhcp and dns listen
    配置802.1x在交换机的端口验证设置
    ASAv931安装&初始化及ASDM管理
    S5700与Cisco ACS做802.1x认证
    playwright
  • 原文地址:https://www.cnblogs.com/rixiang/p/5792317.html
Copyright © 2011-2022 走看看