spring基本使用(12)-springAOP的使用以及原理2 SpringAOP提供的代理技术剖析

1、Advice:增强描述接口,spring提供的增强(Advice)

    Spring只提供方法级别的连接点增强,所以以下说的增强全部是方法级别的增强。

    1.1、方法before增强MethodBeforeAdvice

           顾名思义方法前置增强就是在方法执行之前织入增强的逻辑代码。  

          使用案例:

           public class UserServiceRegistryBeforeAdvice implements MethodBeforeAdvice {
                 @Override
                 public void before(Method method, Object[] objects, Object o) throws Throwable {
                     System.out.println("注册用户前记录一下记录日志。。。。。");
                 }
           }

 

    1.2、方法AfterReturning增强AfterReturningAdvice

            在方法正常返回之后织入增强的逻辑代码。

            使用案例:

              public class UserviceRegistryAfterAdvice implements AfterReturningAdvice {
                  @Override
                  public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
                     System.out.println("注册用户完毕后发送短信通知。。。");
                  }
              }

 

    1.3、方法Around增强MethodInterceptor

            包围方法织入增强的逻辑代码。

            使用案例:

           public class UserServiceRegistryAroundAdvice implements MethodInterceptor(使用aop联盟定义的MethodInterceptor接口) {
                @Override
                public Object invoke(MethodInvocation methodInvocation) throws Throwable {
                    System.out.println("around advice before...");
                    methodInvocation.proceed();
                    System.out.println("around advice after...");
                    return null;
                }
           }

 

     1.4、方法抛出异常后增强ThrowsAdvice

             方法抛出异常后织入增强的逻辑代码。

             使用案例:

            public class UserServiceRegistryThrowsAdvice implements ThrowsAdvice {

             /*
              由于ThrowsAdvice接口没有定义热任何方法,如何使用呢,使用规则如下:
                1:定义的方法名称必须叫afterThrowing。
                2:参数的格式为[Method method, Object[] args, Object target], Throwable throwable。
                  [Method method, Object[] args, Object target] :表示可选参数,但是要么都提供,要么都不提供。
                  Throwable throwable : throwable参数必须需要, 可以定义多个ThrowsAdvice,来处理不同的类型异常,
                  比如ThrowsAdvice1处理SqlException.
                  比如ThrowsAdvice2处理自定义ServiceException, 使用很灵活。

            */
                public void afterThrowing(Method method, Object[] args, Object target, Throwable throwable){
                   System.out.println("方法执行异常。。。。。。");
                   System.out.println(throwable.getMessage());
                   System.out.println("成功回滚事务。。。。。。");
                }
           }

 

     1.5、方法finally增强

            Finally增强其实是AfterReturningAdvice 和ThrowsAdvice 的结合体,用来做比如stream关闭等功能。

     1.6、类级别引介增强DelegatingIntroductionInterceptor

           引介是一个特殊的增强,能为类动态的添加属性和方法,这样可以给一个类动态去实现某接口,也可以为类动态的添加接口的实现逻辑,让业务类成为接口的实现类。

           使用案例: 给目标类是是实现SenderMsgService 接口,实现的逻辑为如下。

        public class SendeMsgIntroductionAdvice extends DelegatingIntroductionInterceptor implements SenderMsgService {

          @Override
          public void sendMsg(String phone) {
             System.out.println("发送短信给手机号:" + phone);
          }


          @Override
          public Object invoke(MethodInvocation mi) throws Throwable {
             return super.invoke(mi);
          }
        }  

 

2、Pointcut:切点描述接口(SpringAop用于描述切点的接口)

      切点的含义我们知道是描述那些连接点的,在SpringAOP中Pointcut接口描述的是方法级别的连接点,也就是说是描述的连接点是:那些类的那些方法,我们看其源码就能知道所以然。

         public interface Pointcut {

	           /**
	            * Return the ClassFilter for this pointcut.
	            * @return the ClassFilter (never {@code null})
	            */
               描述那些类
	           ClassFilter getClassFilter();

	           /**
	            * Return the MethodMatcher for this pointcut.
	            * @return the MethodMatcher (never {@code null})
	            */
               描述那些方法
	           MethodMatcher getMethodMatcher();


	          /**
	           * Canonical Pointcut instance that always matches.
	           */
              提供一个默认全部类的全部方法为切点。
	          Pointcut TRUE = TruePointcut.INSTANCE;

        }

      Pointcut类图结构如下:

                              

 

3、Advisor:切面描述接口(SpringAop 提供用于描述切面的接口)

      我们知道切面Advisor = 切点(Pointcut) + 增强(Advice)。

      Advisor接口源码:

      public interface Advisor {

	     /**
	      * Return the advice part of this aspect. An advice may be an
	      * interceptor, a before advice, a throws advice, etc.
	      * @return the advice that should apply if the pointcut matches
	      * @see org.aopalliance.intercept.MethodInterceptor
	      * @see BeforeAdvice
	      * @see ThrowsAdvice
	      * @see AfterReturningAdvice
	      */
          切面需要提供增强实例
	      Advice getAdvice();

	     /**
	      * Return whether this advice is associated with a particular instance
	      * (for example, creating a mixin) or shared with all instances of
	      * the advised class obtained from the same Spring bean factory.
	      * <p><b>Note that this method is not currently used by the framework.</b>
	      * Typical Advisor implementations always return {@code true}.
	      * Use singleton/prototype bean definitions or appropriate programmatic
	      * proxy creation to ensure that Advisors have the correct lifecycle model.
	      * @return whether this advice is associated with a particular target instance
	      */
	      boolean isPerInstance();

      }

              Advisor接口类图:

                             

              PointcutAdvisor接口:切点切面接口,引介切面我们不做过多描述。

              PointcutAdvisor接口源码:

        public interface PointcutAdvisor extends Advisor {
           获取切点,加上父类的获取增强,刚好可以描述一个切点切面
           Pointcut getPointcut();
        }

              PointcutAdvisor的接口类图:

4、ProxyConfig(Spring提供的SpringAop配置接口):主要配置代理方式:proxyTargetClass 、optimize 等

      Advised(也是Spring配置SpringAop的接口)主要作用是切面的添加、删除等操作addAdvisor、removeAdvisor、                                                                                                                                                              Advisor[] getAdvisors()等

     
 

      我们知道Aop的核心就是生成代理类,那么怎么生成代理类呢?是使用ObjenesisCglibAopProxy还是JdkDynamicAopProxy,是否需要暴露生成好的代理对象呢?需要织入的增强是什么?切点是什么?这些都是需要开发者自己配置的,因此为了实现灵活配置SpringAop提供了ProxyConfig、Advised两个接口以及内置一下实现来完成AOP的配置。其类图如下:

      

 

5、AopProxy(Spring提供的Aop代理接口用于生成代理对象的)

      Aop的核心就是根据AOP的配置生成代理类,AopProxy就是干这个事情的,我们先来看看AopProxy的类图:

         

         根据类图我们可以看出SpringAOP提供了两种生成代理类以及代理对象的方式,JDK动态代理、CGLIB动态代理。

         ObjenesisCglibAopProxy继承于CglibAopProxy 在 CglibAopProxy的基础上优化了生成代理对象的效率。

         JdkDynamicAopProxy生成代理类的效率高于CglibAopProxy , 但是生成的代理对象的执行效率是CglibAopProxy高,因此如果生成的代理类是单例的尽量使用ObjenesisCglibAopProxy来作为生成代理的方式, 如果是多例的就尽量使用JdkDynamicAopProxy来作为生成代理类的方式。

6、 ProxyFactory类:根据AOP配置生成代理类以及对象

      在第4点中我们对SpringAOP的Advised、ProxyConfig整体类结构有一个明确的认识,接下来我们就来一 一剖析。

      ProxyFactory名称叫代理工厂,作用就是根据AOP配置来生成代理类。

       使用案例:

    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        BeforeAdvice beforeAdvice = new UserServiceRegistryBeforeAdvice();
        AfterReturningAdvice afterReturningAdvice = new UserviceRegistryAfetrReturnAdvice();

        ProxyFactory proxyFactory = new ProxyFactory();

        /*
           可添加多个增强,形成增强链,调用顺序跟添加顺序一致,
           也可以使用proxyFactory.addAdvice(1, beforeAdvice);来指定位置。
        */
        proxyFactory.addAdvice(1, beforeAdvice);
        proxyFactory.addAdvice(beforeAdvice);
        proxyFactory.addAdvice(afterReturningAdvice);

        proxyFactory.setTarget(userService);

        /*
           设置接口类型的话,将会默认使用jdk的动态代理JdkDynamicAopProxy来创建代理对象。
           否在会使用CGLIB动态代理ObjenesisCglibAopProxy来创建代理类,虽然是使用ObjenesisCglibAopProxy
           来创建代理类,但是核心还是 其父类CglibAopProxy来控制,只是使用ObjenesisCglibAopProxy提供的
           Objenesis创建对象的技术来创建代理类而已。!!!!!!!!!
        */
        proxyFactory.setInterfaces(userService.getClass().getInterfaces());

        /*
           除此之外可以使用proxyFactory.setOptimize(true)来启动优化代理方式,设置为true的话
           如果代理的Target有接口,也会使用CGLIB来创建代理类。
        */
        proxyFactory.setOptimize(true);
        UserService userServiceProxy = (UserService) proxyFactory.getProxy();

        userServiceProxy.registryUser("张三");
    }

           实现原理分析:

                                 主要的方法是proxyFactory.getProxy();

                                  源码:

	         public Object getProxy() {
		        return createAopProxy().getProxy();
	         }


	         protected final synchronized AopProxy createAopProxy() {
		        if (!this.active) { active默认是false
			       activate();
		        }
                //getAopProxyFactory()不做设置的话会使用默认的AopProxyFactory即DefaultAopProxyFactory
		        return getAopProxyFactory().createAopProxy(this);
	         }

           
                DefaultAopProxyFactory中的createAopProxy方法
                @Override
	           public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		         if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
			         Class<?> targetClass = config.getTargetClass();
			     if (targetClass == null) {
				         throw new AopConfigException("TargetSource cannot determine target class: " +
						"Either an interface or a target is required for proxy creation.");
			     }
			     if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				     return new JdkDynamicAopProxy(config);
			     }
			     return new ObjenesisCglibAopProxy(config);
		         }else {
			             return new JdkDynamicAopProxy(config);
		         }
	         }

                                上面我们使用到了AopProxyFactory 其源码:

                public interface AopProxyFactory {

	              /**
	               * Create an {@link AopProxy} for the given AOP configuration.
	               * @param config the AOP configuration in the form of an
	               * AdvisedSupport object
	               * @return the corresponding AOP proxy
	               * @throws AopConfigException if the configuration is invalid
	               */
                   通过AOP的配置获取一个AopProxy(用于生成代理类以及代理对象)AdvisedSupport 实现了 Advised+ProxyConfig
	               AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException;

                }

                                  AopProxyFactory的实现目前只有一个那就是DefaultAopProxyFactory其源码:

              public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

	             @Override
	             public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		           if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
			          Class<?> targetClass = config.getTargetClass();
			          if (targetClass == null) {
				throw new AopConfigException("TargetSource cannot determine target class: " +
						"Either an interface or a target is required for proxy creation.");
			          }
			          if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				        return new JdkDynamicAopProxy(config);
			          }
			          return new ObjenesisCglibAopProxy(config);
		           }
		           else {
			          return new JdkDynamicAopProxy(config);
		           }
	            }

	          /**
	           * Determine whether the supplied {@link AdvisedSupport} has only the
	           * {@link org.springframework.aop.SpringProxy} interface specified
	           * (or no proxy interfaces specified at all).
	           */
	           private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
		          Class<?>[] ifcs = config.getProxiedInterfaces();
		          return (ifcs.length == 0 || (ifcs.length == 1 && 
                     SpringProxy.class.isAssignableFrom(ifcs[0])));
	           }

         }

 

7、ProxyFactoryBean : 根据AOP配置生成代理类以及代理对象

      ProxyFactoryBean 类实现了Spring的FactoryBean<T> ,如果配置在spring ioc容器中,那么在容器装载的时候会调用其 getObject()方法,ProxyFactoryBean中的getObject()就是根据AOP配置然后生成代理实例返回。

       使用案例:基于XML配置

             <!--ProxyFactoryBean 使用之前置增强 beforeAdvice-->
             <!--1、定义目标bean-->
                <bean id="target" class="com.wzy.springstudy.aop.advice.impl.UserServiceImpl"/>
             <!--2、定义增强bean-->
                <bean id="berforeAdvice" class="com.wzy.springstudy.aop.advice.UserServiceRegistryBeforeAdvice"/>
             <!--3、定义所需生成的代理类 使用实现了FactoryBean接口的ProxyFactoryBean-->
                <bean id="userService" class="org.springframework.aop.framework.ProxyFactoryBean">
                 <!--指定目标类-->
                    <property name="target" ref="target"/>
                 <!--指定生成的代理类需要实现那些接口。-->
                    <property name="proxyInterfaces" value="com.wzy.springstudy.aop.advice.UserService"/>
                 <!--指定需要植入连接点的增强多个增强名称使用“,”号隔开。-->
                    <property name="interceptorNames" value="berforeAdvice"/>
                 <!--是否启用代理优化设置为true将会强制使用CGLIB动态代理,
                     CGLIB创建代理类比较慢,但是创建的代理对象运行的效率
                     较高,跟jdk动态代理刚好相反,如果代理类是代理的,尽
                     量使用CGLIB来生成代理类-->
                    <property name="optimize" value="true"/>
                 <!--是否对类进行代理,如果设置为true将会使用CGLIB生成代理类。-->
                    <property name="proxyTargetClass" value="true"/>
                 <!--生成的代理类是否为单例,默认为true-->
                    <property name="singleton" value="true"/>
                 <!--是否暴露生成的代理对象,默认为false,设置为true 可以
                     在目标类的被增强的连接点(方法)中使用AopContext.currentProxy()
                     来获取当前生成的代理实例。-->
                    <property name="exposeProxy" value="true"/>
                </bean>

                 实现原理分析:我们主要来分析ProxyFactoryBean中的getObject()方法。

                  源码:

	       public Object getObject() throws BeansException {
               初始化切面的链,就是将配置的interceptorNames 按顺序初始化切面bean
		       initializeAdvisorChain();
		       if (isSingleton()) {
                  如果是单例bean就去获取一个单实例,实际是就是去创建代理实例
			      return getSingletonInstance();
		       }
		       else {
			      if (this.targetName == null) {
				  logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
						"Enable prototype proxies by setting the 'targetName' property.");
			      }
                  如果是多例bean就直接创建代理对象
			      return newPrototypeInstance();
		       }
	       }

        
           获取单实例的代理实例
             private synchronized Object getSingletonInstance() {
		       if (this.singletonInstance == null) {
			      this.targetSource = freshTargetSource();
			      if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
				     // Rely on AOP infrastructure to tell us what interfaces to proxy.
				     Class<?> targetClass = getTargetClass();
				     if (targetClass == null) {
					    throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
				     }
				     setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
			      }
			      // Initialize the shared singleton instance.
			      super.setFrozen(this.freezeProxy);
                  创建一个AopProxyFactory然后构建代理实例
			      this.singletonInstance = getProxy(createAopProxy());
		       }
		       return this.singletonInstance;
	       }


           创建一个AopProxy 这里跟ProxyFactory的实现是一样的,都是继承ProxyCreatorSupport
            protected final synchronized AopProxy createAopProxy() {
		      if (!this.active) {
			     activate();
		      }
		      return getAopProxyFactory().createAopProxy(this);
	     }


            protected Object getProxy(AopProxy aopProxy) {
               使用创建的AopProxy来构建代理实例
		       return aopProxy.getProxy(this.proxyClassLoader);
	     }

           

 

8、AspectJProxyFactory:根据Aspectj的配置AOP生成代理实例

      SpringAOP支持使用AspectJ来配置切面,然后使用AspectJProxyFactory来根据AspectJ配置的AOP来生成代理实例。

       AspectJ的基础配置:主要使用的注解:

                                 @Before、@After、@Around、@Pointcut、@Aspect 

                                  AspectJ就是使用上述注解来进行切面的配置。

        使用案例:

           定义目标类
           public class AspectJTarget {
               public void test(){
                  System.out.println("AspectJTarget...");
               }
           }

     

           定义切面类
           @Aspect
           public class AspectJTestBean {

                @Before(value = "execution(* com.wzy.springstudy.aop.AspectJ.c.*.test())")
                public void before(){
                   System.out.println("before");
                }

                测试代码
                public static void main(String[] args) {
                    AspectJProxyFactory aspectJProxyFactory = new AspectJProxyFactory();
                    设置目标类
                    aspectJProxyFactory.setTarget(new AspectJTarget());
                    添加Aspect实例 注意此实例的类一定需要使用@Aspect注解标注
                    aspectJProxyFactory.addAspect(new AspectJTestBean());
                    使用AspectJProxyFactory 来构建代理实例
                    AspectJTarget proxy = aspectJProxyFactory.getProxy();
                    使用代理类调用业务方法
                    proxy.test();
                }
           }

           输出结果
           before
           AspectJTarget...

                         实现原理分析:AspectJProxyFactory的getProxy()方法跟ProxyFactory、ProxyFactoryBean都是一样的实现,此处我们就不过多描述,我们主要关注的是AspectJProxyFactory是如何解析AspectJ的AOP配置的。所以我们主要看

                          aspectJProxyFactory.addAspect(new AspectJTestBean()); 这个的实现

                          源码:

	      public void addAspect(Object aspectInstance) {
		      Class<?> aspectClass = aspectInstance.getClass();
	          String aspectName = aspectClass.getName();
              获取AspectJ的注解数据
		      AspectMetadata am = createAspectMetadata(aspectClass, aspectName);
              校验注解数据是否符合正常的AspectJ的配置
		      if (am.getAjType().getPerClause().getKind() != PerClauseKind.SINGLETON) {
			  throw new IllegalArgumentException(
					"Aspect class [" + aspectClass.getName() + "] does not define a singleton aspect");
		      }
              解析切面并添加到AOP配置中
		      addAdvisorsFromAspectInstanceFactory(
				new SingletonMetadataAwareAspectInstanceFactory(aspectInstance, aspectName));
	      }

          
          解析切面并添加到AOP配置中
          private void addAdvisorsFromAspectInstanceFactory(MetadataAwareAspectInstanceFactory instanceFactory) {
             使用aspectFactory来解析AspectJ配置生成切面Advisor,aspectFactory默认是等于private final AspectJAdvisorFactory aspectFactory = new ReflectiveAspectJAdvisorFactory();的
		     List<Advisor> advisors = this.aspectFactory.getAdvisors(instanceFactory);
		     advisors = AopUtils.findAdvisorsThatCanApply(advisors, getTargetClass());
		     AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(advisors);
		     AnnotationAwareOrderComparator.sort(advisors);
             将通过AspectJ配置生成好的切面添加到AOP配置中,因为AspectJProxyFactory实现了Advised、ProxyConfig接口,所以可以进行此操作。
		     addAdvisors(advisors);
	      }

          使用ReflectiveAspectJAdvisorFactory来将AspectJ的配置实例解析成为Advisor
          ReflectiveAspectJAdvisorFactory 此类的作用就是解析AspectJ的注解来解析成为Advisor切面。此处篇幅太大,所以自行查看其源码。
          

                      结论:AspectJProxyFactory的实现其实就是使用ReflectiveAspectJAdvisorFactory来解析AspectJ的配置生成Advisor切面,然后使用这些切面来构建代理实例。

 

9、整体总结:

       1、本篇博客主要讲了SpringAOP的增强Advice接口、Pointcut(切点接口)、Advisor(切面接口)、Advised\ProxyConfig(代理配置接口)、AopProxy(构建代理类的接口jdk\cglib)、AopProxyFactory(用于创建AopProxy实例的接口)。

       2、其次我们讲解了根据AOP配置来构建代理实例的代理构建器:

             ProxyFactory

             ProxyFactoryBean

             AspectJProxyFactory

             以上三个代理构建器都是费全自动的代理构建器,在后面章节我们会涉及到SpringAOP的自动代理构建器AbstractAutoProxyCreator。

    

 

 

 

 

 

 

 

          

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值