在Java中,我们可以创建自定义注解,这是一种应用于类、方法、变量、参数和包等元素的元数据。自定义注解可以帮助我们更好地组织和处理代码。需要注意的是,自定义注解本身不会改变代码的行为。它们只是一种元数据,可以被其他代码读取和处理。要使注解有用,我们需要编写一些处理这些注解的代码,例如反射。
对注解有了一个基本的认识:注解其实就是一种标记,可以在程序代码中的关键节点(类、方法、变量、参数、包)上打上这些标记,然后程序在编译时或运行时可以检测到这些标记从而执行一些特殊操作。因此可以得出自定义注解使用的基本流程:
第一步,定义注解——相当于定义标记; |
---|
第二步,配置注解——把标记打在需要用到的程序代码中; |
第三步,解析注解——在编译期或运行时检测到标记,并进行特殊操作。 |
① @Override
重写
②@Deprecated
已过时
③@SuppressWarnings(value = “unchecked”)
压制编辑器警告
@Retention、@Target、@Inherited、@Documented这些注解是元注解,用于修饰我们自定义的注解。
元注解用于修饰其他的注解
① @Retention:定义注解的保留策略
@Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码文件中不包含
@Retention(RetentionPolicy.CLASS) //默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,
@Retention(RetentionPolicy.RUNTIME) //注解会在class字节码文件中存在,在运行时可以通过反射获取到
② @Target:指定被修饰的Annotation可以放置的位置(被修饰的目标)
@Target(ElementType.TYPE) //接口、类
@Target(ElementType.FIELD) //属性
@Target(ElementType.METHOD) //方法
@Target(ElementType.PARAMETER) //方法参数
@Target(ElementType.CONSTRUCTOR) //构造函数
@Target(ElementType.LOCAL_VARIABLE) //局部变量
@Target(ElementType.ANNOTATION_TYPE) //注解
@Target(ElementType.PACKAGE) //包
注:可以指定多个位置,例如:
@Target({ElementType.METHOD, ElementType.TYPE}),也就是此注解可以在方法和类上面使用
③ @Inherited:指定被修饰的Annotation将具有继承性
④@Documented:指定被修饰的该Annotation可以被javadoc工具提取成文档.
package com.hh.test.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Author {
String name() default "HH";
String email() default "unknown";
}
使用@Author注解
package com.hh.test.annotation;
@Author(name = "wk", email = "[email protected]")
public class Hello {
public static void main(String[] args) {
}
@Log(value = "测试日志写入", type = "2")
public void testLog() {
}
}
通过反射获取注解的值
Class> clazz = Hello.class;
if (clazz.isAnnotationPresent(Author.class)) {
Author author = clazz.getAnnotation(Author.class);
System.out.println("Author: " + author.name() + ", Email: " + author.email());
}
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface Log {
String value() default "";
String type() default "1";
}
使用@Log注解
@Log(value = "测试日志写入", type = "2")
public void testLog() {
}
通过反射获取注解的值
Log log = clazz.getMethod("testLog").getAnnotation(Log.class);
System.out.printf("日志的内容:%s \n", log.value());
System.out.printf("日志的类型:%s", log.type());
首先,我们创建一个Spring Boot项目,添加maven依赖spring-boot-starter-aop
,spring-boot-starter-aop
模块自身提供了针对 spring-aop
、aspectjrt
和 aspectjweaver
的依赖,包含了我们实现自定义注解的所有功能。
org.springframework.boot
spring-boot-starter-aop
创建一个方法级别的注解
import java.lang.annotation.*;
/**
* 定义一个方法级别的@SaveSystemLog注解,用于标注需要监控的方法:
*/
@Target(ElementType.METHOD)
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
public @interface SaveSystemLog {
/**
* 操作名称
*/
String value() default "";
/**
* 日志类别
*/
String type() default "1";
}
创建切面实现类SystemLogAspect
public class SystemLogAspect {
@Pointcut("execution(* com.hh.test.controller.*.*(..)) && @annotation(com.hh.test.annotation.SaveSystemLog)")
public void pointcut() {
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint point) {
Object result = null;
long beginTime = System.currentTimeMillis();
try {
// 执行方法
result = point.proceed();
} catch (Throwable e) {
log.error(e.getMessage());
}
// 执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
// 保存日志
saveLog(point, time);
return result;
}
private void saveLog(ProceedingJoinPoint joinPoint, long time) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
SaveSystemLog logAnnotation = method.getAnnotation(SaveSystemLog.class);
System.out.println("日志保存成功:"+logAnnotation.value());
}
}
测试一下
public class Test {
@RequestMapping("/log")
@SaveSystemLog(value = "测试日志")
public void saveLog(){
System.out.println("123");
}
}
主要是重点关注下@Target
和@Retention
这两个注解。
@Target
注解,他的作用是将这个注解放在什么地方,比如类上、方法上、构造器上、变量上等,他的值是一个枚举类型的。
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}
@Retention
注解,它的作用是为了说明这个注解的生命周期,也可以说是注解的保留位置。
在注解中有三个生命周期,即