背景
在我们的开发过程中,日志使用的很普遍,并且使用也非常方便,因为它将很多的细节都进行了封装,我们只需@+使用即可。那么如果我们需要实现一个自定义注解可以怎么做呢;接下来,我将带大家一步一步了解自定义注解的实现过程。
实现过程
这里我将以记录一个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 都会有对应的拦截日志输出,说明注解已经生效
想要了解更多信息,可关注本公众号(一起学开源);或请长按以下二维码添加助手。将拉你加入社区进行更多交流