• Spring-AOP-学习笔记(2)-AspectJ


    1.启用@AspectJ,需要下载aspectjweaver.jar   

    <!-- 默认启用动态代理 -->
    <
    aop:aspectj-autoproxy/>

    <!-- 注解启用CGliB -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>

    <!--XML方式启用CGLIB -->
    <aop:config proxy-target-class="true">
        <!-- other beans defined here... -->
    </aop:config>

    2.声明一个切面(Aspect)

    /** 注解的方式声明 **/
    package org.xyz;
    import org.aspectj.lang.annotation.Aspect;
    
    @Aspect
    @Component
    public class MyAspect{
    
    }

    添加@Component注解是为了让Spring自动搜索到并进行管理,当然还需要告诉Spring搜索路径:
    <context:component-scan base-package="org.xyz"/>
    <!-- XML方式声明,不需要@Aspect和@Component注解 -->
    <bean id="masp" class="org.xyz.MyAspect">
        <!-- 配置切面属性 -->
    </bean>
    <aop:config>
        <aop:aspect id="myAspect" ref="masp">
            ... ...
        </aop:aspect>
    </aop:config>

    3.声明一个切点(Pointcut)

      Spring AOP只支持在方法上定义连接点,所以只需考虑如何让切点匹配到目标方法,声明一个切点需要2步:一个包含名称的签名及参数(方法返回值必须为void);一个切点表达式。切点表达式使用@Pointcut注解表示。

    @Pointcut("execution(* com.xyz.myapp.service..(..))")
    public void anyOldTransfer(){
    
    }
    
    /**
     *  anyOldTransfer即为切点签名
     *  execution为切点表达式,这里表示任意返回值,service包下(包括子包)任意形参的接口实现类方法 
     */
    <!-- XML方式配置 -->
    <aop:config>
        <aop:aspect id="myAspect" ref="masp">
           <aop:pointcut id="anyOldTransfer" expression="execution(* com.xyz.myapp.service..(..))"/>    
    
        </aop:aspect>
    </aop:config>

    4.声明一个通知(Advice)

    @Aspect
    public class AspectExample(){
    
       @Before("execution(* com.xyz.myapp.dao..(..)")
        public void beforeTest(){
      
        }
       
        @After("execution(* com.xyz.myapp.dao..(..)")
        public void afterTest(){
      
        }
    
        @AfterReturning("execution(* com.xyz.myapp.dao..(..)")
        public void afterReturnTest(){
      
        }
    
        /** 将返回值传递给切点 */
        @AfterReturning("execution(* com.xyz.myapp.dao..(..)",returning="retVal")
        public void afterReturningTest(Object retVal){
      
        }
    
        @AfterThrowing("execution(* com.xyz.myapp.dao..(..)")
        public void afterThrowingTest(){
      
        }
    
        /** 捕捉指定异常 */
        @AfterThrowing("execution(* com.xyz.myapp.dao..(..)",throwing="ex")
        public void afterThrowingTest(DataAccessException ex){
      
        }
        
        @Around("execution(* com.xyz.myapp.dao..(..)")
        public Object aroundTest(ProceedingJoinPoint pjp) throws Throwable{
            //前处理
            Object retVal = pjp.proceed();
            //后处理
          return retVal;  
        }
    }

     

    <!-- 使用注解的方式声明 -->
    <aop:aspect id="beforeExample" ref="aBean">
        <aop:pointcut id="dataAccessOperation" expression="execution(* com.xyz.myapp.dao..(..))" />
        
        <!-- Before -->
        <aop:before
            pointcut-ref="dataAccessOperation"
            method="doAccessCheck"/>
        
        <!-- After returning -->
        <aop:after-returning
            pointcut-ref="dataAccessOperation"
            returning="retVal"
            method="doAccessCheck"/>
    
        <!-- After throwing-->
        <aop:after-throwing
            pointcut-ref="dataAccessOperation"
            throwing="dataAccessEx"
            method="doRecoveryActions"/>
        
        <!-- After -->
        <aop:after
            pointcut-ref="dataAccessOperation"
            method="doReleaseLock"/>
        
        <!-- Around -->
        <aop:around
            pointcut-ref="dataAccessOperation"
            method="doBasicProfiling"/>
        
    </aop:aspect>

     访问当前JoinPoint

      任何Advice类型方法都可以声明第一个形参为org.aspectj.lang.JoinPoint(Around的为ProceedingJoinPoint,JoinPoint的子类)

      JoinPoint接口提供了:getArgs()获取方法形参,getThis()获取代理对象,getTarget()获取目标对象

      将调用方法参数传递到advice

      后置的上面已经给出实例,下面看看前置的

        @Before("execution(* com.xyz.myapp.dao..(..) && args(account,..)")
        public void beforeTest(Account account){
      
        }

      自定义注解使用

        //定义注解    
        @Retention(RetentionPolicy.RUNTIME)
        @Target(ElementType.METHOD)
        public @interface Auditable {
            AuditCode value();
        } 
    
         //获取注解
        @Before("com.xyz.lib.Pointcuts.anyPublicMethod() &&    @annotation(auditable)")
        public void audit(Auditable auditable) {
            AuditCode code = auditable.value();
        // ...
        }

      Advice参数和泛型

      Spring AOP还可以处理带泛型的类和方法参数

        public interface Sample<T> {
            void sampleGenericMethod(T param);
            void sampleGenericCollectionMethod(Collection<T> param);
        }
    
        @Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)")
        public void beforeSampleMethod(MyType param) {
            // Advice implementation
        }  
     
        /** 对于集合的泛型形参要用?代替,真正类型由调用者自行转换 */ 
        @Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)")
        public void beforeSampleMethod(Collection<?> param) {
            // Advice implementation
        }    

       参数名称确定

     这里的参数名称主要指目标方法的形参名称和Advice方法的形参名称如何确定,Spring AOP通过以下方式来确定参数名称:

    •   如果明确指定了参数名称,就使用指定的参数名称;如何指定呢?advice和pointcut注解有一个可选的"argNames"属性可以用于指定参数名称,如
    @Before(value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)", 
            argNames="bean,auditable")
    public void audit(Object bean, Auditable auditable) {
        AuditCode code = auditable.value();
        // ... use code and bean
    }
    • 如果第一个参数是JoinPointProceedingJoinPoint, or JoinPoint.StaticPart 类型,"argNames"属性中不需要包含他们的命名,如
      @Before(value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)",
              argNames="bean,auditable")
      public void audit(JoinPoint jp, Object bean, Auditable auditable) {
          AuditCode code = auditable.value();
          // ... use code, bean, and jp
      }
    • 如果你不需要再advice方法中获取目标方法的参数,可以省略"argNames"属性
      @Before("com.xyz.lib.Pointcuts.anyPublicMethod()")
      public void audit(JoinPoint jp) {
          // ... use jp
      }
    //或者直接使用aspectJ表达式中的arg
    @Before("execution(* x.y.service.FooService.getFoo(String,int) && arg(name,age))") public void audit(JoinPoint jp,String name,int age) { // ... use jp }

      参数处理

      如果你想在调用目标方法之前处理下传入的参数,可以这样做:

    @Around("execution(List<Account> find*(..)) && com.xyz.myapp.SystemArchitecture.inDataAccessLayer() && args(accountHolderNamePattern)")
    public Object preProcessQueryPattern(ProceedingJoinPoint pjp,
            String accountHolderNamePattern) throws Throwable {
        String newPattern = preProcess(accountHolderNamePattern);
        return pjp.proceed(new Object[] {newPattern});
    }
    
    /**
     * 这里要注意形参的顺序,第一个传入的也要作为第一个传进proceed方法中
     */

      Advice 顺序

      当多个连接点重合时,如何进行有序的执行呢?Spring AOP遵循AspectJ确定的相同的优先级规则作为advice的执行顺序。

        在进入时,优先级越高的越先执行,如两个before advice,优先级高的先执行

        在退出时,优先级越高的越后执行,如两个after advice,优先级高的后执行

      当两个advice定义在不同的切面(Aspect)上且都需要运行在相同的连接点,这种情况下除非你指定顺序,否则执行顺序是不确定的。那如何指定执行顺序呢?

        Aspect 类实现 org.springframework.core.Ordered 接口或添加Order注解,从Ordered的getValue() 返回的值越小优先级越高

      当两个advice定义在相同的切面(Aspect)上且都需要运行在相同的连接点上,这种情况因为Ordered接口也没办法定义顺序了,那建议对advice进行合并或对aspect进行重构。

      

    @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("com.xyz.myapp.SystemArchitecture.businessService()")
        public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
            int numAttempts = 0;
            PessimisticLockingFailureException lockFailureException;
            do {
                numAttempts++;
                try {
                    return pjp.proceed();
                }
                catch(PessimisticLockingFailureException ex) {
                    lockFailureException = ex;
                }
            } while(numAttempts <= this.maxRetries);
            throw lockFailureException;
        }
    
    }
    <aop:aspectj-autoproxy/>
    
    <bean id="concurrentOperationExecutor" class="com.xyz.myapp.service.impl.ConcurrentOperationExecutor">
        <property name="maxRetries" value="3"/>
        <property name="order" value="100"/>
    </bean>

     Spring AOP与AspectJ 如何选择?

      如果你只是在Spring管理的bean上(如controller,service,dao)执行advice,并且没有很复杂的参数传递(如将目标方法的参数传递到Aspect类中),那Spring AOP是最佳的选择

      如果需要在非Spring管理的对象(如domain对象,程序中显示创建的对象)上执行advice,或有复杂的参数传递,建议使用AspectJ

    是否该启用CGLIB?

      如果你需代理目标都有实现的接口,那就无需启用CGLIB了,通常我们service,dao层都有接口,如果只是代理这些实现类,使用Java 动态代理即可

      如果你代理的目标没有实现的接口,那需要启用CGLIB,但这里要注意3点:

        1.final方法是不能被代理的,因为CGLIB无法重写final方法

        2.从Spring 3.2开始CGLIB就集成到Spring core包中,因此无需导入CGLIB包了

        3.使用CGLIB作为代理时,代理对象的构造器会执行2次,但一般代理构造器中并无逻辑处理,所以调用2次也不会有什么影响

  • 相关阅读:
    ros::NodeHandle成员介绍
    ros::NodeHandle成员介绍
    odom_out_and_back.cpp
    odom_out_and_back.cpp
    c++实现ros by example volume1 例子timed_out_and_back功能
    SQL语句进阶
    数据库基础(常用的SQL)
    安装Mysql
    1.初识数据库
    (MYSQL错误解决)本地计算机上的MYSQL服务启动停止后,某些服务在未由其他服务或程序使用时将自动停止
  • 原文地址:https://www.cnblogs.com/manliu/p/5986498.html
Copyright © 2020-2023  润新知