基于AOP实现的方法执行时间日志打印

基于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表达式.

发布了1 篇原创文章 · 获赞 0 · 访问量 3

猜你喜欢

转载自blog.csdn.net/qq_41696539/article/details/105047412