1. 程式人生 > >基於Spring註解@AspectJ的AOP

基於Spring註解@AspectJ的AOP

新增依賴

<!-- aspectj 方式的AOP,需要加入下面三個包 -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.9</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.9</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjtools</artifactId>
            <version>1.8.9</version>
        </dependency>

XML啟用

Spring上下文建立一個AnnotationAwareAspectJAutoProxyCreator類

<aop:aspectj-autoproxy/>

切入點宣告

@AspectJ風格的命名切入點使用org.aspectj.lang.annotation包下的@Pointcut方法(方法必須是返回void型別)實現。

@Pointcut(value="切入點表示式", argNames = "引數名列表")  

使用註解 


  @Pointcut("@annotation(com.apus.dap.hella.common.aspect.annotation.JmxMetrical) || @target(com.apus.dap.hella.common.aspect.annotation.JmxMetrical)")
  public void annotationProcessor() {
  }

 註解類@JmxMetrical 

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface JmxMetrical {

  String name() default "";

  boolean displayArgs() default false;
  /**
   * 增刪改的資料的型別
   */
  Class<?> clazz();
}

通知宣告

  • 前置通知

使用org.aspectj.lang.annotation 包下的@Before註解宣告;
@Before(value = "切入點表示式或命名切入點", argNames = "引數列表引數名")

  • 環繞通知

像Spring基於XML的AOP一樣,@AspectJ註解的使用不僅只限定與定義前置和後置通知型別。我們還可以建立環繞通知,使用環繞通知需要使用@Around。

@Around (  
value="切入點表示式或命名切入點",  
argNames="引數列表引數名")

  • 後置返回通知

使用org.aspectj.lang.annotation 包下的@AfterReturning註解宣告;

@AfterReturning(  
value="切入點表示式或命名切入點",  
pointcut="切入點表示式或命名切入點",  
argNames="引數列表引數名",  
returning="返回值對應引數名")

  • 後置最終通知

使用org.aspectj.lang.annotation 包下的@After註解宣告;
@After (  
value="切入點表示式或命名切入點",  
argNames="引數列表引數名")

  • 後置異常通知

使用org.aspectj.lang.annotation 包下的@AfterThrowing註解宣告

@AfterThrowing (  
value="切入點表示式或命名切入點",  
pointcut="切入點表示式或命名切入點",  
argNames="引數列表引數名",  
throwing="異常對應引數名") 

@Aspect
  @Component
  public class JmxMetricalAOP {

    private static final Logger LOG = LoggerFactory.getLogger(JmxMetricalAOP.class);


    private static MetricalCounterManager metricalCounterManager;

    private static final String POINTCUT_METHOD =
        "(execution(public * " + API_PROTOCOL_CLASS_NAME + ".*(..)))";

    @PostConstruct
    public void init() {
      //初始化一個全域性度量器管理類
      metricalCounterManager = new MetricalCounterManager(Maps.newHashMap());
      LOG.info("JmxMetricalAOP.methodAnnotated init:success");
    }

    @Pointcut("@annotation(com.apus.dap.hella.common.aspect.annotation.JmxMetrical) || @target(com.apus.dap.hella.common.aspect.annotation.JmxMetrical)")
    public void annotationProcessor() {
    }

    @Pointcut(POINTCUT_METHOD)
    public void publicMethod() {
      LOG.info("JmxMetricalAOP.methodJoinPointed");
    }

    @Around("publicMethod() && annotationProcessor()")
    public Object timedJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
      Signature signature = joinPoint.getSignature();
      Method method = ((MethodSignature) signature).getMethod();
      final String methodName = signature.getName();

      /**
       * 1.獲取唯一介面全路徑
       */
      String fullApiName = getFullApiName(joinPoint, signature, methodName);

      if (method.getDeclaringClass().isInterface()) {
        method = joinPoint.getTarget().getClass()
            .getDeclaredMethod(methodName, method.getParameterTypes());
      }

      /**
       * 2. 方法上的註解優先順序比類上的註解高,可以覆蓋類上註解的值,獲取相關值
       */
      //是否註解
      Boolean isJmxMetrical = false;
      //註解物件名
      String jmxMetricalName = null;
      //註解物件引數
      Object[] jmxMetricalArgs = null;
      //註解物件class
      Class<? extends JmxMetrical> aClass = null;

      JmxMetrical jmxMetrical = null;
      //處理方法上的註解
      if (method.isAnnotationPresent(JmxMetrical.class)) {
        isJmxMetrical = true;
        jmxMetrical = method.getAnnotation(JmxMetrical.class);
        jmxMetricalName = jmxMetrical.name();
        aClass = jmxMetrical.getClass();
        if (jmxMetrical.displayArgs()) {
          jmxMetricalArgs = joinPoint.getArgs();
        }
      } else {
        //處理類上面的註解
        Object target = joinPoint.getTarget();
        if (target.getClass().isAnnotationPresent(JmxMetrical.class)) {
          isJmxMetrical = true;
          jmxMetrical = target.getClass().getAnnotation(JmxMetrical.class);
          jmxMetricalName = jmxMetrical.name();
          aClass = jmxMetrical.getClass();
          if (jmxMetrical.displayArgs()) {
            jmxMetricalArgs = joinPoint.getArgs();
          }
        }
      }
      /**
       * 3. 切入點度量
       */
      //切入點方法執行結果
      Object result = null;
      Boolean thrown = false;
      try {
        result = joinPoint.proceed();
      } catch (Throwable throwable) {
        thrown = true;
        //如果需要度量
      }

      //如果不需要度量
      if (!isJmxMetrical) {
        return result;
      }
      metricalCounterManager.metricApi(fullApiName, result, thrown);
      return result;
    }


    private String getFullApiName(ProceedingJoinPoint joinPoint, Signature signature,
        String methodName) {
      //通過包路徑方式獲取
      String[] packageName = signature.getDeclaringTypeName().split("\\.");
      StringBuilder stringBuilder = new StringBuilder();
      for (int i = 0; i < packageName.length; ++i) {
        if (i < packageName.length - 1) {
          stringBuilder.append(packageName[i].substring(0, 1));
        } else {
          stringBuilder.append(packageName[i]);
        }
        if (i == packageName.length - 1) {
          stringBuilder.append(FULL_POINT_SEP);
          continue;
        }
        stringBuilder.append(".");
      }
      String fullApiName = stringBuilder + signature.getName();
      LOG.info("Executing: " + fullApiName);
      //通過介面方式獲取
//    String className = joinPoint.getTarget().getClass().getInterfaces()[0].getName();
//    String fullApiName = className + SEP + methodName;
      return fullApiName;
    }


    /**
     * 後置異常通知
     */
    @AfterThrowing("publicMethod() && annotationProcessor()")
    public void demandRefund() {
      LOG.info("com.apus.hella.protocol.dto.*#{} exception!!!");
    }


    public static List<MetricalCounter> getMonitorList() {
      List<MetricalCounter> monitor_profiles = getMonitorMap().values().stream()
          .collect(Collectors.toList());
      return monitor_profiles;
    }