1. 程式人生 > >Spring-Boot+AOP+統計單次請求方法的執行次數和耗時情況

Spring-Boot+AOP+統計單次請求方法的執行次數和耗時情況

package com.yanshu.aspect;


import java.util.*;




import org.aspectj.lang.JoinPoint;  
import org.aspectj.lang.ProceedingJoinPoint;  
import org.aspectj.lang.annotation.AfterReturning;  
import org.aspectj.lang.annotation.Around;  
import org.aspectj.lang.annotation.Aspect;  
import org.aspectj.lang.annotation.Before;  
import org.aspectj.lang.annotation.Pointcut;  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
import org.springframework.stereotype.Component;  
  


/**
 * 攔截器
 * @author Administrator
 *
 */
//@Aspect
//@Component
public class ControllerInterceptor {
//定義一個日誌的全域性的靜態
private static Logger logger =LoggerFactory.getLogger(ControllerInterceptor.class);

//ThreadLocal 維護變數 避免同步   
    //ThreadLocal為每個使用該變數的執行緒提供獨立的變數副本,所以每一個執行緒都可以獨立地改變自己的副本,而不會影響其它執行緒所對應的副本。  
    ThreadLocal<Long> startTime = new ThreadLocal<>();// 開始時間  
   //ThreadLocal 維護變數 避免同步 ThreadLocal是一個關於建立執行緒區域性變數的類、
    //通常情況下,我們建立的變數是可以被任何一個執行緒訪問並且修改的。而使用ThreadLocal建立的變數只能被當前執行緒訪問,其他執行緒無法訪問和修改。
    //ThreadLocal為每個使用該變數的執行緒提供獨立的變數副本,所以每一個執行緒都可以獨以地改變自己的副本,而不會影響其它執行緒所對應的副本
    ThreadLocal<Long> startTimes=new ThreadLocal<>();//開始時間
      
    /** 
     * map1存放方法被呼叫的次數O 
     */  
    ThreadLocal<Map<String, Long >> map1 = new ThreadLocal<>();  
    
     /** 
     * map2存放方法總耗時 
     */  
    ThreadLocal<Map<String, Long >> map2 = new ThreadLocal<>();  
      
      
    /** 
     * 定義一個切入點. 解釋下: 
     * 
     * ~ 第一個 * 代表任意修飾符及任意返回值. ~ 第二個 * 定義在web包或者子包 ~ 第三個 * 任意方法 ~ .. 匹配任意數量的引數. 
     */  
   /// static final String pCutStr = "execution(* com.appleyk.*..*(..))";  
   // static final String pCutStr = "execution(* com.yanshu.controller..*..*(..))";  
    static final String pCutStr = "execution(* com.yanshu.*..*(..))";  
    @Pointcut(value = pCutStr)  
    public void logPointcut() {  
    }  
  
    /**
     * Aop:環繞通知 切整個包下面的所有涉及到呼叫的方法的資訊
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("logPointcut()")  
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {  
  
          
        //初始化 一次  
        if(map1.get() ==null ){  
            map1.set(new HashMap<>());  
              
        }  
          
        if(map2.get() == null){  
            map2.set(new HashMap<>());  
        }  
          
        long start = System.currentTimeMillis();  
        try {  
            Object result = joinPoint.proceed();  
            if(result==null){  
                //如果切到了 沒有返回型別的void方法,這裡直接返回  
                return null;  
            }  
            long end = System.currentTimeMillis();  
  
            logger.info("===================");  
            String tragetClassName = joinPoint.getSignature().getDeclaringTypeName();  
            String MethodName = joinPoint.getSignature().getName();  
                                      
            Object[] args = joinPoint.getArgs();// 引數  
            int argsSize = args.length;  
            String argsTypes = "";  
            String typeStr = joinPoint.getSignature().getDeclaringType().toString().split(" ")[0];  
            String returnType = joinPoint.getSignature().toString().split(" ")[0];  
            logger.info("類/介面:" + tragetClassName + "(" + typeStr + ")");  
            logger.info("方法:" + MethodName);  
            logger.info("引數個數:" + argsSize);  
            logger.info("返回型別:" + returnType);  
            if (argsSize > 0) {  
                // 拿到引數的型別  
                for (Object object : args) {  
                    argsTypes += object.getClass().getTypeName().toString() + " ";  
                }  
                logger.info("引數型別:" + argsTypes);  
            }  
  
            Long total = end - start;  
            logger.info("耗時: " + total + " ms!");  
                      
            if(map1.get().containsKey(MethodName)){  
                Long count = map1.get().get(MethodName);  
                map1.get().remove(MethodName);//先移除,在增加  
                map1.get().put(MethodName, count+1);  
                  
                count = map2.get().get(MethodName);  
                map2.get().remove(MethodName);  
                map2.get().put(MethodName, count+total);  
            }else{  
                  
                map1.get().put(MethodName, 1L);  
                map2.get().put(MethodName, total);  
            }  
              
            return result;  
  
        } catch (Throwable e) {  
            long end = System.currentTimeMillis();  
            logger.info("====around " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : "  
                    + e.getMessage());  
            throw e;  
        }  
  
    }  
  
    //對Controller下面的方法執行前進行切入,初始化開始時間  
    @Before(value = "execution(* com.yanshu.controller.*.*(..))")  
    public void beforMehhod(JoinPoint jp) {  
        startTime.set(System.currentTimeMillis());  
    }  
  
    //對Controller下面的方法執行後進行切入,統計方法執行的次數和耗時情況  
    //注意,這裡的執行方法統計的資料不止包含Controller下面的方法,也包括環繞切入的所有方法的統計資訊  
    @AfterReturning(value = "execution(* com.yanshu.controller.*.*(..))")  
    public void afterMehhod(JoinPoint jp) {  
        long end = System.currentTimeMillis();  
        long total =  end - startTime.get();  
        String methodName = jp.getSignature().getName();  
        logger.info("連線點方法為:" + methodName + ",執行總耗時為:" +total+"ms");  
          
        //重新new一個map  
        Map<String, Long> map = new HashMap<>();  
          
        //從map2中將最後的 連線點方法給移除了,替換成最終的,避免連線點方法多次進行疊加計算     
        //由於map2受ThreadLocal的保護,這裡不支援remove,因此,需要單開一個map進行資料交接  
        for(Map.Entry<String, Long> entry:map2.get().entrySet()){  
            if(entry.getKey().equals(methodName)){                
                map.put(methodName, total);  
                  
            }else{  
                map.put(entry.getKey(), entry.getValue());  
            }             
        }  
      
        for (Map.Entry<String, Long> entry :map1.get().entrySet()) {  
            for(Map.Entry<String, Long> entry2 :map.entrySet()){  
                if(entry.getKey().equals(entry2.getKey())){  
                    System.err.println(entry.getKey()+",被呼叫次數:"+entry.getValue()+",綜合耗時:"+entry2.getValue()+"ms");  
                }  
            }  
              
        }  
    }  





}