zoukankan      html  css  js  c++  java
  • spring aop 环绕通知around和其他通知的区别

    前言:

         spring 的环绕通知和前置通知,后置通知有着很大的区别,主要有两个重要的区别:

    1) 目标方法的调用由环绕通知决定,即你可以决定是否调用目标方法,而前置和后置通知   是不能决定的,他们只是在方法的调用前后执行通知而已,即目标方法肯定是要执行的。

    2)  环绕通知可以控制返回对象,即你可以返回一个与目标对象完全不同的返回值,虽然这很危险,但是你却可以办到。而后置方法是无法办到的,因为他是在目标方法返回值后调用

       这里是经过我自己测试的过的例子,使用面向切面来处理一些问公共的问题,比如,权限管理,事务的委托

    下面的例子就是使用环绕通知,当程序发生异常时,重复提交请求,重复的次数是可以设定的

        当我们开发企业级应用时,通常会想要从几个切面来引用模块化的应用和特定操作的集合,下面是一个典型的通用切面,看起来可能像下面这样(这也是Spring文档里的)

    package test.prefer.aspect;

    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;

    @Aspect
    public class SystemArchitecture {

      /**
       * A join point is in the web layer if the method is defined
       * in a type in the com.xyz.someapp.web package or any sub-package
       * under that.
       */
      @Pointcut("within(com.xyz.someapp.web..*)")
      public void inWebLayer() {}

      /**
       * A join point is in the service layer if the method is defined
       * in a type in the com.xyz.someapp.service package or any sub-package
       * under that.
       */
      @Pointcut("within(com.xyz.someapp.service..*)")
      public void inServiceLayer(){}

      /**
       * A join point is in the data access layer if the method is defined
       * in a type in the com.xyz.someapp.dao package or any sub-package
       * under that.
       */
      @Pointcut("within(com.xyz.someapp.dao..*)")
      public void inDataAccessLayer(){}

      /**
       * A business service is the execution of any method defined on a service
       * interface. This definition assumes that interfaces are placed in the
       * "service" package, and that implementation types are in sub-packages.
       * 
       * If you group service interfaces by functional area (for example, 
       * in packages com.xyz.someapp.abc.service and com.xyz.def.service) then
       * the pointcut expression "execution(* com.xyz.someapp..service.*.*(..))"
       * could be used instead.
       *
       * Alternatively, you can write the expression using the 'bean'
       * PCD, like so "bean(*Service)". (This assumes that you have
       * named your Spring service beans in a consistent fashion.)
       */
      @Pointcut("execution(* test.prefer.aspect.*.*(..))")
      public void businessService(){}
      
      /**
       * A data access operation is the execution of any method defined on a 
       * dao interface. This definition assumes that interfaces are placed in the
       * "dao" package, and that implementation types are in sub-packages.
       */
      @Pointcut("execution(* com.xyz.someapp.dao.*.*(..))")
      public void dataAccessOperation(){}

    }

     一、定义自己的一个切面

    /*
    *文件名:ConcurrentOperationExecutor.Java
    *描述:<描述>
    *修改人:Administrator
    */

    package test.prefer.aspect;

    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.springframework.core.Ordered;

    /**
     * @author 
     *@date 2010-6-1
     */
    @Aspect
    public class ConcurrentOperationExecutor implements Ordered {
       
       private static final int DEFAULT_MAX_RETRIES = 2;

       private int maxRetries = DEFAULT_MAX_RETRIES;
       private int order = 1;

       public void setMaxRetries(int maxRetries) {
          this.maxRetries = maxRetries;
       }
       
       public int getOrder(){
          return this.order;
       }
       public void setOrder(int order){
          this.order = order;
       }
       
       @Around("test.prefer.aspect.SystemArchitecture.businessService()")
       public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable { 
        //环绕通知处理方法
          int numAttempts = 0;
          Exception lockFailureException;
          do {
             numAttempts++;
             try { 
              System.out.println("环绕通知方法[ doConcurrentOperation(ProceedingJoinPoint pjp) ].............");
                return pjp.proceed();
             }
             catch(Exception ex) {
                lockFailureException = ex;
             }
          }
          while(numAttempts <= this.maxRetries);
          throw lockFailureException;
       }

    }

    说明:

        请注意切面实现了 Ordered 接口,这样我们就可以把切面的优先级设定为高于事务通知 (我们每次重试的时候都想要在一个全新的事务中进行)。maxRetriesorder 属性都可以在Spring中配置。主要的动作在doConcurrentOperation这个环绕通知方法中发生。 请注意这个时候我们所有的businessService()方法都会使用这个重试策略。 我们首先会尝试处理,如果得到一个Exception异常, 我们仅仅重试直到耗尽所有预设的重试次数(spring开发文档)

    二、在配置文件里配置这个切面

    <aop:aspectj-autoproxy/>

    <bean id="concurrentOperationExecutor"
      class="test.prefer.aspect.ConcurrentOperationExecutor">
         <property name="maxRetries" value="3"/>
         <property name="order" value="100"/>  
    </bean>

    好了,下面我们就试一下效果吧

    三、测试效果

        1)新建一个测试的bean: MyTestAspect,代码如下:

    package test.prefer.aspect;
    /**
     * 这是一个切面类
     */
    import org.aspectj.lang.annotation.Aspect;

    public class MyTestAspect {
     int k=0;
     public void test(String args) throws Exception{
      System.out.println("这里是[ 目标 ]方法test()"+ ++k);
      if(k<2){
       throw new Exception();
      }
      
     }

    }

    这个类必须在连接点的包或者子包下面,

    在SystemArchitecture里有定义

     @Pointcut("execution(* test.prefer.aspect.*.*(..))")
      public void businessService(){}

    2)applicationContext.xml里配置 MyTestAspect

    <bean id="test" class="test.prefer.aspect.MyTestAspect"/>

    3)好了,看看效果吧

    package test;

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import test.prefer.aspect.MyTestAspect;

    public class example {
     
     public static void main(String args[]){
      
      ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
      MyTestAspect t =(MyTestAspect)ctx.getBean("test");
      try{
      t.test("");
      }catch(Exception e){
       System.out.println("main()中处理异常"+e);
      }
     }

    }

    输出结果是:

    环绕通知方法[ doConcurrentOperation(ProceedingJoinPoint pjp) ].............
    这里是[ 目标 ]方法test()1
    环绕通知方法[ doConcurrentOperation(ProceedingJoinPoint pjp) ].............
    这里是[ 目标 ]方法test()

  • 相关阅读:
    Go语言
    Go语言
    electron-builder vue3 用户自定义安装目录
    提取页面中的style标签内容
    px2rpx | px转rpx
    js EventBus
    select 下拉选择多个值
    keep-alive页面缓存
    js适配移动端页面
    vue日常问题记录
  • 原文地址:https://www.cnblogs.com/gmq-sh/p/6018587.html
Copyright © 2011-2022 走看看