zoukankan      html  css  js  c++  java
  • 基于Spring aop写的一个简单的耗时监控

    前言:毕业后应该有一两年没有好好的更新博客了,回头看看自己这一年,似乎少了太多的沉淀了。让自己做一个爱分享的人,好的知识点拿出来和大家一起分享,一起学习。

    背景: 在做项目的时候,大家肯定都遇到对一些对方法,模块耗时的监控,为了方便性能的监控,问题的定位等。如果每一个方法里都加上

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

     类似的代码肯定没问题,但是就会显得代码特别的冗余。正好近期看了点spring-aop的相关数据,就想到做一个对方法的切面监控方法耗时,同时利用一个简单的注解,可以达到代码简洁化。

    1、定义注解类

    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 "";
    }

    2、定义切面

    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; } }

    3、在方法上打上该注解

    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); } }
     

    4、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>

    5、测试入口

    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:说明一下,该工具使用时,第1、2两步需要写入到底层common层代码中。如果是基于osgi的应用,不同的bundle之间spring上下文可能是不通的,那么需要在使用这个注解的bundle中配置步骤4中的spring上下文(切记)。

  • 相关阅读:
    读《成交》有感
    【VC++学习笔记二】绘制图片的双缓冲技术
    【VC++学习笔记一】MFC操作Excel
    敏捷软件开发
    VIM常用命令
    Ubuntu下配置samba实现文件夹共享
    linux系统编程之管道(三):命令管道(FIFO)
    linux系统编程之管道(二):管道读写规则
    linux系统编程之管道(一):匿名管道(pipe)
    《Linux环境进程间通信》系列文章链接
  • 原文地址:https://www.cnblogs.com/newpanderking/p/9108082.html
Copyright © 2011-2022 走看看