基於Spring aop寫的一個簡單的耗時監控

前言:畢業後應該有一兩年沒有好好的更新博客了,回頭看看本身這一年,彷佛少了太多的沉澱了。讓本身作一個愛分享的人,好的知識點拿出來和你們一塊兒分享,一塊兒學習。java

背景: 在作項目的時候,你們確定都遇到對一些對方法,模塊耗時的監控,爲了方便性能的監控,問題的定位等。若是每個方法裏都加上spring

...
Stopwatch watch = Stopwatch.createStarted();
...
watch.elapsed(TimeUnit.MILLISECONDS)
...

 相似的代碼確定沒問題,可是就會顯得代碼特別的冗餘。正好近期看了點spring-aop的相關數據,就想到作一個對方法的切面監控方法耗時,同時利用一個簡單的註解,能夠達到代碼簡潔化。express

一、定義註解類app

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PerfMonitor {

    /**
     * 基本描述,可不設置,默認爲空
     * @return
     */
    String desc() default "";
}

二、定義切面工具

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

import com.google.common.base.Stopwatch;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;

public class PerfAspect {

private Logger logger = Logger.getLogger(PerfAspect.class);
/** * 耗時,spring線程問題,防止不一樣線程之間出現數據影響 */ private ThreadLocal<Stopwatch> watch = new ThreadLocal<>(); /** * 進入方法埋點 */ public void before() { // 設置進入時間 watch.set(Stopwatch.createStarted()); } /** * 結束方法埋點 * @param point */ public void after(JoinPoint point) { Class c = point.getTarget().getClass(); Signature signature = point.getSignature(); StringBuilder sb = new StringBuilder(); String desc = getAnnotationDesc(point); if (desc != null && desc != "") { sb.append(desc); } else { sb.append(c.getSimpleName()).append(".").append(signature.getName()); } sb.append(",cost[").append(watch.get().elapsed(TimeUnit.MILLISECONDS)).append("]ms"); logger.info(sb.toString()) } /** * 獲取註解描述信息 * @param joinPoint * @return */ private String getAnnotationDesc(JoinPoint joinPoint) { Method method = getJoinPointMethod(joinPoint); PerfMonitor perfMonitor = method.getAnnotation(PerfMonitor.class); return perfMonitor.desc(); } /** * 計算接入點監控方法 * @param joinPoint * @return */ private Method getJoinPointMethod(JoinPoint joinPoint) { Class c = joinPoint.getTarget().getClass(); Signature signature = joinPoint.getSignature(); if (c != null && c.getMethods() != null) { for (Method method : c.getMethods()) { if (method.getName() == signature.getName()) { return method; } } } return null; } }

三、在方法上打上該註解性能

public class Student implements Person {

    /**
     * logger
     */
    private Logger logger = Logger.getLogger(Student.class);

    @PerfMonitor(desc = "student eat perf")
    public void eat(String food) {
   logger.info(
"student eat " + food); } }
 

四、spring xml配置學習

    <bean id="student" class="com.common.util.test.Student"/>
<!--
性能監控點--> <bean id="perfAspect" class="com.common.util.perfmonitor.PerfAspect"/> <!-- 開啓aop --> <aop:aspectj-autoproxy/> <!-- aop配置 --> <aop:config> <!-- 鏈接點 --> <aop:pointcut id="pointCut" expression="@annotation(com.common.util.perfmonitor.PerfMonitor)"/> <aop:aspect ref="perfAspect"> <aop:after method="after" pointcut-ref="pointCut"/> <aop:before method="before" pointcut-ref="pointCut"/> </aop:aspect> </aop:config>

 

五、測試入口測試

public class Client {

  public static void main(String[] args)  {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("spring.aop.xml");
    final Person person = (Person)ctx.getBean("student");
    ExecutorService executorService = Executors.newFixedThreadPool(10);

        for (int i = 0; i < 100; i++) {
            executorService.execute(new Runnable() {
                public void run() {
              
                    person.eat("fish");
                }
            });

        }
  }
}

 

ps:說明一下,該工具使用時,第一、2兩步須要寫入到底層common層代碼中。若是是基於osgi的應用,不一樣的bundle之間spring上下文多是不通的,那麼須要在使用這個註解的bundle中配置步驟4中的spring上下文(切記)。ui

相關文章
相關標籤/搜索