前言: 毕业后应该有一两年没有好好的更新博客了, 回头看看自己这一年, 似乎少了太多的沉淀了. 让自己做一个爱分享的人, 好的知识点拿出来和大家一起分享, 一起学习.
背景: 在做项目的时候, 大家肯定都遇到对一些对方法, 模块耗时的监控, 为了方便性能的监控, 问题的定位等. 如果每一个方法里都加上
- ...
- 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 上下文 (切记).
来源: http://www.bubuko.com/infodetail-2622971.html