自定义注解
Posted blwy-zmh
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义注解相关的知识,希望对你有一定的参考价值。
基础知识:自定义注解
使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。
定义注解类型元素时需要注意如下几点:
- 访问修饰符必须为public,不写默认为public;
- 该元素的类型只能是基本数据类型、String、Class、枚举类型、注解类型(体现了注解的嵌套效果)以及上述类型的一位数组;
- 该元素的名称一般定义为名词,如果注解中只有一个元素,请把名字起为value(后面使用会带来便利操作);
- ()不是定义方法参数的地方,也不能在括号中定义任何参数,仅仅只是一个特殊的语法;
- default代表默认值,值必须和第2点定义的类型一致;
- 如果没有默认值,代表后续使用注解时必须给该类型元素赋值。
常用的元注解
java中有四种元注解:@Retention、@Inherited、@Documented、@Target
@Retention 注解的保留位置(枚举RetentionPolicy),RetentionPolicy可选值:
- SOURCE:注解仅存在于源码中,在class字节码文件中不包含(如果一个注解被定义为RetentionPolicy.SOURCE,则它将被限定在Java源文件中,那么这个注解即不会参与编译也不会在运行期起任何作用,这个注解就和一个注释是一样的效果,只能被阅读Java文件的人看到)
- CLASS:默认的保留策略,注解在class字节码文件中存在,但运行时无法获得(如果一个注解被定义为RetentionPolicy.CLASS,则它将被编译到Class文件中,那么编译器可以在编译时根据注解做一些处理动作,但是运行时JVM(Java虚拟机)会忽略它,我们在运行期也不能读取到)
-
RUNTIME:注解在class字节码文件中存在,在运行时可以通过反射获取到(如果一个注解被定义为RetentionPolicy.RUNTIME,那么这个注解可以在运行期的加载阶段被加载到Class对象中。那么在程序运行阶段,我们可以通过反射得到这个注解,并通过判断是否有这个注解或这个注解中属性的值,从而执行不同的程序代码段。我们实际开发中的自定义注解几乎都是使用的RetentionPolicy.RUNTIME,在默认的情况下,自定义注解是使用的RetentionPolicy.CLASS)
@Inherited 声明子类可以继承此注解,如果一个类A使用此注解,则类A的子类也继承此注解
@Documented 声明注解能够被javadoc等识别
@Target 用来声明注解范围(枚举ElementType),ElementType可选值:
- TYPE:接口、类、枚举、注解
- FIELD:字段、枚举的常量
- METHOD:方法
- PARAMETER:方法参数
- CONSTRUCTOR:构造函数
- LOCAL_VARIABLE:局部变量
- ANNOTATION_TYPE:注解
- PACKAGE:包
首先,定义一个注解、和一个供注解修饰的简单Java类
定义注解格式:
public @interface 注解名 {定义体} 例:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OperationLog {
//表示操作是那个服务哪个模块下的操作
String module() default "xxxx服务";
//操作的类型,添加,更新,删除
String type() default "add";
//操作者
String user() default "system";
//操作描述
String operation() default "";
}
二、Aspect类
import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.text.SimpleDateFormat; @Aspect @Component public class OperationLogAspect { private ThreadLocal<SimpleDateFormat> format = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); //切点表达式,表示加了OperationLog注解的都是切点,路径是自定义注解的全路径 @Pointcut("@annotation(com.alice.springboot.demo.OperationLog)") public void pointcut(){ } @Around("@annotation(operationLog)")//拦截加了OperationLog注解的方法,也可以直接拦截上面定义的切点(@Around("pointcut()")) public Object operationLogRecord(ProceedingJoinPoint joinPoint, OperationLog operationLog){ //获取请求 HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); //响应 ResponseResult<Object> responseResult = null; //判断原方法是否正常执行的标志 boolean isNormalProcess = false; try{ //返回切点处继续执行原方法,并接收原方法的返回值 responseResult = (ResponseResult<Object>) joinPoint.proceed(); //如果顺利执行,那么说明原方法执行正常,就可以进行日志记录。因为,如果原方法的增删改出问题了,那么日志就不需要记录了,不用记录失败的操作。 isNormalProcess = true; }catch (Throwable e){ System.out.println("原方法报错,不需要记录日志"); }try { if (isNormalProcess){ //如果原方法正常执行完毕,那么需要记录操作日志 saveOperationLog(joinPoint, operationLog, request); } }catch (Exception e){ System.out.println("保存操作日志出错"); } return responseResult; } private void saveOperationLog(ProceedingJoinPoint joinPoint, OperationLog operationLog, HttpServletRequest request){ //用来记录参数的值 StringBuilder contentBuilder = new StringBuilder(); //从切点获取切点的所有参数 Object[] allParams = joinPoint.getArgs(); for (Object param: allParams){ contentBuilder.append(JSON.toJSONString(param) + ","); } //删除最后一个多余的逗号 contentBuilder.delete(contentBuilder.length() - 1, contentBuilder.length()); //执行数据库操作,将信息保存到数据库,笔者这里使用的是mongodb,仅供参考,主要看获取自定义注解里面的值 Document document = new Document(); //获取自定义注解里面的值 document.append("module", operationLog.module()) .append("type", operationLog.type()) .append("user", operationLog.user()) .append("operation", operationLog.operation()) .append("content", contentBuilder.toString()); logDao.saveLogs("mongo collection name", document); } }
三、使用方法
package com.alice.springboot.demo; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping(value = "/test") public class OperationLogController { @RequestMapping(value = "/add", method = RequestMethod.POST) @OperationLog(module = "xxx服务", type = "add", operation = "添加xxx") //这里使用到了自定义注解,并且赋值了自定义注解里面的某些值,最后在aspect里面可以获取到这些值 public ResponseResult<String> addOperation(String user, String content){ ResponseResult<String> result = new ResponseResult<>(); try{ //执行添加操作 result.setStatus(ResponseStatusEnum.SUCCESS); result.setMessage("添加操作成功"); }catch (Exception e){ result.setStatus(ResponseStatusEnum.FAIL); result.setMessage("添加操作失败" + e.toString()); } return result; } }
原文连接:https://blog.csdn.net/jmdonghao/article/details/78880899
以上是关于自定义注解的主要内容,如果未能解决你的问题,请参考以下文章