背景
在我们的开发过程中,日志使用的很普遍,并且使用也非常方便,因为它将很多的细节都进行了封装,我们只需@+使用即可。那么如果我们需要实现一个自定义注解可以怎么做呢;接下来,我将带大家一步一步了解自定义注解的实现过程。
实现过程
这里我将以记录一个web系统的请求日志功能,介绍通过自定义注解实现的全过程,提前我们创建好一个Springboot项目
引入对应依赖包(修改pom文件)
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
然后我们先定义一个注解接口类
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface LogAnnotation { /** 模块 **/ String title() default ""; /** 动作 **/ String action() default ""; }
以上是Jdk中提供的元注解,注解说明:
@Target:
表明该注解可以应用的java元素类型
Target类型 | 描述 |
ElementType.TYPE | 应用于类、接口(包括注解类型)、枚举 |
ElementType.FIELD | 应用于属性(包括枚举中的常量) |
ElementType.METHOD | 应用于方法 |
ElementType.PARAMETER | 应用于方法的形参 |
ElementType.CONSTRUCTOR | 应用于构造函数 |
ElementType.LOCAL_VARIABLE | 应用于局部变量 |
ElementType.ANNOTATION_TYPE | 应用于注解类型 |
ElementType.PACKAGE | 应用于包 |
ElementType.TYPE_PARAMETER | 1.8版本新增,应用于类型变量) |
ElementType.TYPE_USE | 1.8版本新增,应用于任何使用类型的语句中(例如声明语句、泛型和强制转换语句中的类型) |
@Retention:
表明该注解的生命周期
生命周期类型 | 描述 |
RetentionPolicy.SOURCE | 编译时被丢弃,不包含在类文件中 |
RetentionPolicy.CLASS | JVM加载时被丢弃,包含在类文件中,默认值 |
RetentionPolicy.RUNTIME | 由JVM 加载,包含在类文件中,在运行时可以被获取到 |
@Documente:
表明该注解标记的元素可以被Javadoc 或类似的工具文档化
@Inherited
表明使用了@Inherited注解的注解,所标记的类的子类也会拥有这个注解
注解解析
一般注解解析,我们可以通过反射机制实现,但是操作起来都比价繁琐;是Spring里面提供了切面编程的注解,可以非常方便的帮助我们来实现注解解析工作,如下
@Aspect @Component @Slf4j public class SystemLogAspect { /** * 通过注解的方式加入切入点 **/ @Pointcut("@annotation(com.hank.mybatisdemo.aop.annotation.LogAnnotation)") public void logPointCut() { System.out.println("初始化注解切入点..."); } @Around("logPointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { long beginTime = System.currentTimeMillis(); //执行方法 Object method = point.proceed(); long time = System.currentTimeMillis() - beginTime; System.out.println("方法执行耗时:" + time); /** 做点其他事情,比如记录日志 **/ try{ saveSysLog(point, time); }catch (Exception e){ log.error("e={}",e); } return method; } /** 记录日志 **/ private void saveSysLog(ProceedingJoinPoint joinPoint, long time) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class); if(logAnnotation != null){ //注解上的描述 log.info("{}-{}",logAnnotation.title(),logAnnotation.action()); } //请求的方法名 String className = joinPoint.getTarget().getClass().getName(); String methodName = signature.getName(); log.info("请求{}.{}耗时{}毫秒",className,methodName,time); try { //请求的参数 Object[] args = joinPoint.getArgs(); String params=null; if(args.length!=0){ params=JSON.toJSONString(args); } log.info(params); } catch (Exception e) { } } }
如上代码,我们插入了注解的切面插入点,设置了拦截;可以在环绕兰接触获取到拦截到的点,在方法内可以通过反射机制拿到对应的类,方法名,和参数;同时可以进行分析方法执行的耗时
当然,如果我们这里是Http请求,那么可以使用Spring.web里面的RequestContextHolder工具获取到当前的HttpServletRequest对象,从而去获取对应的请求信息,如下
public static HttpServletRequest getHttpServletRequest() { return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); }
应用验证
前面我们写了相关的代码,接下来我们可以将注解进行应用,如下,只需要在对应的方法明上加入注解即可
@LogAnnotation(title = "日志模块",action = "查询日志") @GetMapping(value = "/list") public ResultVO queryLogs(){ return new ResultVO(0,"操作成功....",""); }
晚上以上,即可运行验证,每次请求http://localhost:8080/log/list 都会有对应的拦截日志输出,说明注解已经生效
想要了解更多信息,可关注本公众号(一起学开源);或请长按以下二维码添加助手。将拉你加入社区进行更多交流