一、概念
注解是一种关联元程序中元素信息与元数据的方法和途径。Annotation程序可以通过Java的反射机制来获取程序中元素的Annotation对象,通过Annotation对象则可以获取到注解中的元数据信息。
二、元注解
元注解用于注解其他注解,以下是四种标准元注解:
1、@Target注解定义Annatation修饰对象的作用范围
可被用于packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(成员变量、catch变量)。在Annotation中使用了Target,可更明晰其修饰目标。参数如下:
- TYPE : 类、接口或enum声明
- FIELD: 域(属性)声明
- METHOD: 方法声明
- PARAMETER: 参数声明
- CONSTRUCTOR: 构造方法声明 LOCAL_VARIABLE:局部变量声明
- ANNOTATION_TYPE:注释类型声明
- PACKAGE: 包声明
2、@Retention定义Annotation被保留的时间长短
定义注解信息保存的级别,描述注解的生命周期。取值如下:
- SOURCE:在源文件中有(源文件保留)
- CLASS:在Class中有效(Class保留)
- RUNTIME:在运行时有效(运行时有效)
3、@Documented 描述Java-doc
用于描述其它类型的 Annotation 中应该被标注的程序成员的公共 API,因此可以被javadoc 此类的工具文档化。
3、@Inherited 阐述被标注的类型是被继承的
如果一 个使用了@Inherited Annotation 类型被用于一个 class,则这个 annotation 将被用于该 class 的子类,但是@Inherited 修饰接口时不可继承。
三、注解处理器
注解处理器用于处理Annotation,使注解生效,产生预期效果。
四、自定义注解实现
1、自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SysLog {
/**
* 操作模块
*/
String module() default "";
/**
* 操作内容
*/
String operation() default "";
/**
* 不记录返回值,默认false,记录返回值。
*/
boolean ignoreResponse() default false;
/**
* 不记录的参数,默认全记录。配置参数名称。
*/
String[] ignoreParameters() default {};
}
2、自定义注解处理器
@Slf4j
@Aspect
@Component
public class SysLogAspect {、
private ObjectMapper mapper = new ObjectMapper();
/**
* 异步记录日志
*/
private static ExecutorService logExecutorService = Executors.newSingleThreadExecutor();
// 定义自定义注解为切点
@Pointcut("@annotation(com.example.demo.study.SysLog)")
public void logPointCut() {
}
@AfterReturning(pointcut = "logPointCut()", returning = "response")
public void saveSysLog(JoinPoint joinPoint, Object response) {
// The method of obtaining the weaving point from the cut in point by reflection mechanism
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
// 获取请求类名
String className = joinPoint.getTarget().getClass().getName();
// 获取请求方法名
String methodName = method.getName();
methodName = className + "." + methodName;
String operation = null;
// 获取注解信息
SysLog sysLog = method.getAnnotation(SysLog.class);
// 注解中操作属性
if (sysLog != null) {
operation = "[" + sysLog.module() + "]" + sysLog.operation();
}
// 参数信息
StringJoiner sjArgsInfo = new StringJoiner(",");
Parameter[] parameters = method.getParameters();
Object[] args = joinPoint.getArgs();
outer:
for (int i = 0; i < args.length; i++) {
try {
String paramName = parameters[i].getName();
if (sysLog != null) {
String[] ignoreParameters = sysLog.ignoreParameters();
if (ignoreParameters.length > 0) {
for (String ignore : ignoreParameters) {
if (paramName.equals(ignore)) {
continue outer;
}
}
}
}
String argInfo = null;
if (sysLog != null && "toString".equalsIgnoreCase(sysLog.serializationType())) {
argInfo = args[i].toString();
} else {
argInfo = mapper.writeValueAsString(args[i]);
}
sjArgsInfo.add(paramName + ":" + argInfo);
} catch (Exception e) {
e.printStackTrace();
}
}
// 返回信息
String returnInfo = null;
if (sysLog != null && !sysLog.ignoreResponse()) {
try {
if (sysLog != null && "toString".equalsIgnoreCase(sysLog.serializationType())) {
returnInfo = response.toString();
} else {
returnInfo = mapper.writeValueAsString(response);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 处理注解信息
* 可用于写文件或者存数据库
*/
}
}
3、自定义注解使用方式
@SysLog(module = "模块名",operation = "操作内容",ignoreParameters = "忽略的参数",ignoreResponse = true)
@PostMapping("/user/login")
public UserRequest login(@RequestBody User user) {
System.out.println(user);
}