SpringBoot基于AOP +自定义注解实现打印方法耗时

一、定义注解类TimeLog

@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TimeLog {
    String value() default "";
}

二、定义切面AOP类

@Slf4j
@Aspect
@Component
public class TimeLogAspect {
    private static final ThreadLocal<Long> firstThreadLocal = new ThreadLocal<>();
    private static final ThreadLocal<Long> threadLocal = new ThreadLocal<>();
    // 解决方法调用见清除 threadLocal 空指针
    private static final ThreadLocal<Integer> countThreadLocal = new ThreadLocal<>();

    @Pointcut("execution(* *(..)) && @annotation(timeLog )")
    public void pointcut(TimeLog timeLog ) {
    }
    
	@Around(value = "pointcut(timeLog)", argNames="joinPoint, timeLog")
    public Object doAround(ProceedingJoinPoint point, TimeLog timeLog) throws Throwable {
  		Long startTime = System.currentTimeMillis();
		Object result = point.proceed();
		
        // 获取方法的参数值
        Method method = this.getMethod(point);
        String methodName = method.toString();
        Object[] args = point.getArgs();
        EvaluationContext context = this.bindParam(method, args);
        // 根据spel表达式获取值
        Expression expression = parser.parseExpression(timeLog.value());
        Object methodDesc= expression.getValue(context);
        
        // 打印
        Long endTime = System.currentTimeMillis();
        Long costTime = endTime - startTime;
        log.info("methodName: {}, methodDesc: {}, costTime {}ms",methodName,methodDesc,costTime);
        return result;
    }

    /**
     * 获取当前执行的方法
     *
     * @param pjp
     * @return
     * @throws NoSuchMethodException
     */
    private Method getMethod(ProceedingJoinPoint point) throws NoSuchMethodException {
        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        Method method = methodSignature.getMethod();
        Method targetMethod = point.getTarget().getClass().getMethod(method.getName(), method.getParameterTypes());
        return targetMethod;
    }

    /**
     * 将方法的参数名和参数值绑定
     *
     * @param method 方法,根据方法获取参数名
     * @param args   方法的参数值
     * @return
     */
    private EvaluationContext bindParam(Method method, Object[] args) {
        //获取方法的参数名
        String[] params = discoverer.getParameterNames(method);

        //将参数名与参数值对应起来
        EvaluationContext context = new StandardEvaluationContext();
        for (int len = 0; len < params.length; len++) {
            context.setVariable(params[len], args[len]);
        }
        return context;
    }
}

三、Aspectj LoadTimeWeaving

1、创建aop.xml配置,放到/src/main/resources/META-INF目录下

<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "https://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
    <weaver options="-XnoInline -Xset:weaveJavaxPackages=true -Xlint:ignore -verbose -XmessageHandlerClass:org.springframework.aop.aspectj.AspectJWeaverMessageHandler">
        <!-- 只对指定包下的类进行编织 -->
        <include within="test..*"/>
    </weaver>
    <aspects>
        <!-- 使用指定的切面类进行编织 -->
        <aspect name="xxx.TimeLogAspect"/>
    </aspects>
</aspectj>

2、在程序启动时开启agent

@Slf4j
public class MyAspectjAgentInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>
	@Override
	public void initialize(ConfigurableApplicationContext context) {
				//关键代码,用于在程序中开启agent
		//通过bytebuddy拿到当前jvm的Instrumentation实例
		Instrumentation instrumentation = ByteBuddyAgent.install();
		//激活Aspectj的代理对象
		Agent.agentmain("", instrumentation);
		//激活spring代理对象
		InstrumentationSavingAgent.agentmain("", instrumentation);
	}
}

3、创建spring.factories,放到/src/main/resources/META-INF目录下

org.springframework.context.context.ApplicationContextInitializer=\
xxx.MyAspectjAgentInitializer 

Maven关键依赖

<dependency>
	<groupId>net.bytebuddy</groupId>
	<artifactId>byte-buddy-agent</artifactId>
	<version>1.10.9</version>
</dependency>
<dependency>
	<groupId>net.bytebuddy</groupId>
	<artifactId>byte-buddy</artifactId>
	<version>1.10.9</version>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-web</artifactId>
	<version>5.2.5</version>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-webmvc</artifactId>
	<version>5.2.5</version>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-test</artifactId>
	<version>5.2.5</version>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-core</artifactId>
	<version>5.2.5</version>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context</artifactId>
	<version>5.2.5</version>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-aop</artifactId>
	<version>5.2.5</version>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-aspects</artifactId>
	<version>5.2.5</version>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-instrument</artifactId>
	<version>5.2.5</version>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-tx</artifactId>
	<version>5.2.5</version>
</dependency>

参考


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

书香水墨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值