基于AOP实现的方法执行时间日志打印
1.Aop简单介绍
AOP是Spring框架面向切面的编程思想,AOP采用一种称为“横切”的技术,将涉及多业务流程的通用功能抽取并单独封装,形成独立的切面,在合适的时机将这些切面横向切入到业务流程指定的位置中(百度)
2.注解简单介绍
相信大家在日常开发中使用过很多注解,比如常见的@Controller @Service @Autowired等等,那么如何自己去定义一个注解呢?下面简单举例介绍一下:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Performance {
String functionName() default "";
String jobKey() default "";
}
@Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标
@Retention按生命周期来划分可分为3类:
1、RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
2、RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
3、RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
@Documented 注解表明这个注解应该被 javadoc工具记录. 默认情况下,javadoc是不包括注解的. 但如果声明注解时指定了 @Documented,则它会被 javadoc 之类的工具处理, 所以注解类型信息也会被包括在生成的文档中,是一个标记注解,没有成员
(以上概念作者本人百度所得,如有错误大家请指出)
并且声明@interface类型,其中可以定义一些参数,例子中已展示。
注解本身还支持一些其他的元注解,各位看官有兴趣可以自己摸索一番,在这我只简单应用。
3.AOP具体代码
@Aspect
@Component
public class PerformanceAspect {
@Pointcut("@annotation(com.xx.xx.xx.Performance)")
public void pointCut(){ }
@Around("pointCut()")
public void doBefore(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
Performance annotation = signature.getMethod().getAnnotation(Performance.class);
String functionName = annotation.functionName();
if(functionName.isEmpty()){
functionName = signature.getMethod().getName();
}
String jobKey = (String) AspectSupportUtils.getKeyValue(proceedingJoinPoint,annotation.jobKey());
long start = System.currentTimeMillis();
try {
proceedingJoinPoint.proceed();
}catch (Exception e){
LogUtil.exception(e);
}
long end = System.currentTimeMillis();
long timeUse = end - start;
LogUtil.debug(" service : " + functionName + jobKey+" use time: "+timeUse+" ms");
}
}
@Aspect声明为切面类
@Pointcut("@annotation(com.znv.datacenter.annoations.Performance)")
public void pointCut(){ }
@Pointcut注解的表达式有很多种支持和实现,有兴趣的可以百度,我这里代表拦截我的注解所在
下面的主体方法
@Before: 标识一个前置增强方法,相当于BeforeAdvice的功能.
@After: final增强,不管是抛出异常或者正常退出都会执行.
@AfterReturning: 后置增强,似于AfterReturningAdvice, 方法正常退出时执行.
@AfterThrowing: 异常抛出增强,相当于ThrowsAdvice.
@Around: 环绕增强,相当于MethodInterceptor.
逻辑很简单,获得方法名,然后在执行方法前后去截取时间戳,最后调用日志打印。
其中以下方法就是调用注解所在的方法本身。
proceedingJoinPoint.proceed();
使用的时候在方法上加上注解即可
@UseTemporaryDatabases
@Performance(functionName = "database import operation job with jobkey : ", jobKey = "#jobKey")
public void importData(List<Map> tableStructureList, String jobKey) throws Exception {
DataSourceContextHolder.setDBType("temporarySource");
//拼接数据库名的转义
String tableName = "`" + jobKey + "`";
dataCollectionMapper.importData(tableStructureList, tableName);
}
其中需要注意的是jobKey这个参数使用了springEL表达式。
springEL表达式也是很强大,常见的@Value("${xxx}")就是一个使用的表现,但是我们自己实现的注解中想要使用SpringEL表达式,需要额外添加类。具体可以参考下链接: 自定义注解支持spring EL表达式.