自定义注解+AOP实现记录日志功能

简介: 自定义注解+AOP实现记录日志功能

使用注解可以大大减少开发的代码量,所以在实际项目的开发中会使用到很多的注解。特别是做一些公共基础的功能,比如日志记录,事务管理,权限控制这些功能,使用注解会非常高效且优雅。


对于自定义注解,主要有三个步骤,定义注解,标记注解,解析注解,其实并不是很难。所以关于自定义注解的更多内容就不在这里展开说了,大家也可以看我之前的文章Java注解Annotation小结


正如文章标题所言,通过自定义注解+AOP切面编程实现日志记录功能,开发实现流程主要分为下面四步:


  • 1 定义注解
  • 2 标记注解:在对应需要记录日志的方法上标记注解
  • 3 解析注解:编写切面类,进行相应处理逻辑
  • 4 开启切面能力


下面实战一下,自定义一个注解@DoLog,用于方法上,当方法被调用时即打印日志,在控制台显示调用方传入的参数和调用返回的结果。


1 定义注解


首先定义注解@DoLog,在方法上使用,为了能在反射中读取注解信息,当然是设置为RUNTIME

@Target(value = ElementType.METHOD)
@Documented
@Retention(value = RetentionPolicy.RUNTIME)
public @interface DoLog {
    String dataStatus() default "";
    String[] labelNames() default {};
}


说明:这里注解里面定义的属性,对于记录日志没有实际意义,这里只是顺便演示一下如何在切面类中获取注解中的属性值。


2 标记注解:在对应需要记录日志的方法上标记注解


注意:使用切面的注解不能加在私有方法上,否则切入不了。这个要注意一下,你也可以自己试一下。

@RestController
@Slf4j
public class DemoController {
    @GetMapping("/demo2/{orderno}")
    @DoLog(dataStatus = "1",labelNames = {"name","age"})
    public BaseResult<String> test1(@PathVariable(value = "orderno") String orderno) {
        return BaseResult.success("aaa");
    }
}


3 解析注解:编写切面类,进行相应处理逻辑


最关键的一步来了,解析注解,一般在项目中会使用Spring的AOP技术解析注解,当然如果只需要解析一次的话,也可以使用Spring处理器BeanPostProcessor来读取注解,相关内容可以参考我的文章使用Spring的BeanPostProcessor优雅的实现工厂模式

咱这里的场景是打印每次方法被调用的日志,所以使用AOP比较合适。

创建一个切面类DoLogAspect进行解析。

@Aspect
@Slf4j
@Component
public class DoLogAspect {
    //切面点为标记了@DoLog注解的方法
    @Pointcut("@annotation(cn.test.util.DoLog)")
    public void doLog() {
    }
    //环绕通知
    @Around("doLog()")
    @SuppressWarnings("unchecked")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        long starTime = System.currentTimeMillis();
        //通过反射获取被调用方法的Class
        Class type = joinPoint.getSignature().getDeclaringType();
        //获取类名
        String typeName = type.getSimpleName();
        //方法名
        String methodName = joinPoint.getSignature().getName();
        //获取参数列表
        Object[] args = joinPoint.getArgs();
        //参数Class的数组
        Class[] clazz = new Class[args.length];
        for (int i = 0; i < args.length; i++) {
            clazz[i] = args[i].getClass();
        }
        //通过反射获取调用的方法method
        Method method = type.getMethod(methodName, clazz);
        DoLog doLog = method.getAnnotation(DoLog.class);
        log.info("注解参数:{},{}",logApi.dataStatus(),JSONObject.toJSONString(logApi.labelNames()));
        //获取方法的参数
        Parameter[] parameters = method.getParameters();
        //拼接字符串,格式为{参数1:值1,参数2::值2}
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            String name = parameter.getName();
            sb.append(name).append(":").append(args[i]).append(",");
        }
        if (sb.length() > 0) {
            sb.deleteCharAt(sb.lastIndexOf(","));
        }
        //执行结果
        Object res;
        try {
            //执行目标方法,获取执行结果
            res = joinPoint.proceed();
            log.info("调用{}.{}方法成功,参数为[{}],返回结果[{}]", typeName, methodName, sb.toString(), JSONObject.toJSONString(res));
        } catch (Exception e) {
            log.error("调用{}.{}方法发生异常", typeName, methodName);
            //如果发生异常,则抛出异常
            throw e;
        } finally {
            log.info("调用{}.{}方法,耗时{}ms", typeName, methodName, (System.currentTimeMillis() - starTime));
        }
        //返回执行结果
        return res;
    }
}


注意:自己在主逻辑之外使用切面加入的处理逻辑,一定要try处以免影响到正常业务逻辑。


扩展(Pointcut定义):

public class DoLogAspect {
    //切面点为标记了@DoLog注解的方法
    @Pointcut("@annotation(cn.test.util.DoLog)")
    public void doLog() {
    }
    //环绕通知
    @Around("doLog()")
    @SuppressWarnings("unchecked")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        //处理代码
    }


以上代码同样可以替换为下面的代码:

public class DoLogAspect {
    //环绕通知
    @Around("@annotation(cn.test.util.DoLog)")
    @SuppressWarnings("unchecked")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        //处理代码
    }


4 开启切面能力


定义完切面类后,就需要在启动类添加启动AOP的注解。

@SpringBootApplication
//添加此注解,开启AOP
@EnableAspectJAutoProxy
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}


5 测试


启动项目,请求接口,我们可以看到控制台出现被调用方法的日志信息,如下:

2021-11-13 06:16:26.617|||INFO|||-|||-|||RMI TCP Connection(2)-192.168.0.100|||DispatcherServlet--->Completed initialization in 22 ms
2021-11-13 06:30:08.807|||INFO|||-|||-|||http-nio-8084-exec-1|||DoLogAspect--->注解参数:1,["name","age"]
2021-11-13 06:30:08.823|||INFO|||-|||-|||http-nio-8084-exec-1|||DoLogAspect--->调用DemoController.test1方法成功,参数为[arg0:1001],返回结果[{"data":"aaa","msg":"success","ret":"0"}]
2021-11-13 06:30:08.823|||INFO|||-|||-|||http-nio-8084-exec-1|||DoLogAspect--->调用DemoController.test1方法,耗时20ms


总结


这种使用注解和切面编程,记录接口请求参数和返回值的功能,因为使用起来特别方便,而且代码侵入性又特别小,所以在实际项目中基本上都会使用,而最常见的场景就是日志、监控打点,异常收口。


关于自定义注解+AOP实现记录日志功能就分享到这里,期待能够给有缘的同学带来一些启发和帮助。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
3月前
|
存储 Go
Go 浅析主流日志库:从设计层学习如何集成日志轮转与切割功能
本文将探讨几个热门的 go 日志库如 logrus、zap 和官网的 slog,我将分析这些库的的关键设计元素,探讨它们是如何支持日志轮转与切割功能的配置。
93 0
Go 浅析主流日志库:从设计层学习如何集成日志轮转与切割功能
|
10天前
|
存储 关系型数据库 MySQL
【mybatis-plus】Springboot+AOP+自定义注解实现多数据源操作(数据源信息存在数据库)
【mybatis-plus】Springboot+AOP+自定义注解实现多数据源操作(数据源信息存在数据库)
|
16天前
|
XML Java Maven
Spring之Aop的注解使用
Spring之Aop的注解使用
|
30天前
|
存储 数据可视化 Java
自定义注解实现aop切面
自定义注解实现aop切面
23 1
|
1月前
|
SQL NoSQL Java
【七】springboot整合AOP实现日志操作
【七】springboot整合AOP实现日志操作
41 0
|
3月前
|
数据可视化 API 开发工具
Baumer工业相机堡盟工业相机如何通过NEOAPI SDK使用相机日志跟踪功能(C++)
Baumer工业相机堡盟工业相机如何通过NEOAPI SDK使用相机日志跟踪功能(C++)
33 0
|
3月前
|
Dubbo Java 应用服务中间件
微服务框架(八)Spring Boot AOP 日志切面实现
  此系列文章将会描述Java框架Spring Boot、服务治理框架Dubbo、应用容器引擎Docker,及使用Spring Boot集成Dubbo、Mybatis等开源框架,其中穿插着Spring Boot中日志切面等技术的实现,然后通过gitlab-CI以持续集成为Docker镜像。   本文为使用Spring Boot AOP 实现日志切面、分离INFO和ERROR级别日志
|
3月前
|
XML Java 数据格式
Spring AOP(注解方式和XML方式)
Spring AOP(注解方式和XML方式)
126 0
|
3月前
|
Java 程序员 数据库
业务需求-用AOP记录系统操作日志
全栈老司机 程序员林中酒 更新了本文详细介绍了如何使用AOP(面向切面编程)记录系统操作日志的业务需求,包括需求分析、技术实现分析、数据库设计和代码实现等各个环节。您将了解如何高效、规范地实现这一功能
|
4月前
|
Java Spring
spring boot aop 实践---记录日志
spring boot aop 实践---记录日志
28 0