日常开发过程中,以及性能调优过程中,都想记录一个业务请求路径中各个方法的处理时间,以便进行针对性处理。在每个方法的开头和结尾加代码处理记录打印时间,代码就会显得过于冗余,那怎么在不改变原有代码结构的方式的条件下来实现打印方法执行时间了,我们一步一步来分解说明
一、定义的一个java注解
import java.lang.annotation.Documented;
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)
@Documented
public @interface LogMethodExecuteTime {
}
这里声明了一个名为 LogMethodExecuteTime 的注解,
Retention 表示该注解在运行时保留
Target 表示注解适用的目标类型,此处目标为java中的方法。
二、定义一个切面
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import lombok.extern.slf4j.Slf4j;
/**
* @author zhangyi
*/
@Aspect
@Slf4j
public class LogMethodExecuteTimeAspect {
//@Around("@annotation(注解的package.注解名称)")
//@Around("@within(com.study.controller.*)")
//@Around("execution(* com.study.controller.*.*(..))")
@Around("@annotation(com.study.LogMethodExecuteTime)")
private Object logAround(ProceedingJoinPoint pjp) throws Throwable {
long startTime = System.currentTimeMillis(); // 开始时间
Signature signature = pjp.getSignature();
// 方法名
String methodName = signature.getName();
// 类名
String className = signature.getDeclaringTypeName();
Object proceed = pjp.proceed();
log.info("{}.{}-execute time : {} ms", className, methodName, (System.currentTimeMillis() - startTime));
return proceed;
}
}
logAround方法定义了一个切点,表示所有被 LogMethodExecutionTime 注解的方法都会匹配该切点
三、使用
@LogMethodExecuteTime
@RequestMapping(value = "rules", method = RequestMethod.POST)
public String rules() throws Exception {
//.....;
return "";
}
四、日志打印
com.study.TestService.rules-execute time : 18 ms
五、execution、within和annotation的说明
execution: 用于匹配方法执行连接点。 这是使用Spring AOP时使用的主要切点标识符。 可以匹配到方法级别,细粒度
within: 只能匹配类这级,只能指定类, 类下面的某个具体的方法无法指定, 粗粒度
annotation:限制匹配连接点(加注解才能被拦截执行)
以上面的例子说说三个具体的差别:
@Around("@within(com.study.controller.*)"):
--有效范围对应com.study.controller包下面的所有类,不包括子包,可以无需定义注解,方法无需加注解,直接加切点就可以
@Around("execution(* com.study.controller.*.*(..))")
--有效范围对应com.study.controller包以及子包下面的所有类(当然还可以更细致的控制哪些方法执行,哪些方法不执行),可以无需定义注解,方法无需加注解,直接加切点就可以
@Around("@annotation(com.study.LogMethodExecuteTime)")
--可以匹配任何方法,但是必须加注解@LogMethodExecuteTime