简介
注解(元数据)是一种描述数据的数据,用于对代码(包、类、接口、字段、方法参数、局部变量等)进行说明。从Jdk 1.5 版本引入。
注解的属性
注解中只有属性(成员变量),没有方法,变量名就是使用注解括号中对应的参数名,变量返回值注解括号中对应参数类型。
@Repeatable注解中的变量则类型则是对应Annotation(接口)的泛型Class。
/**注解Repeatable源码*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
/**
* Indicates the <em>containing annotation type</em> for the
* repeatable annotation type.
* @return the containing annotation type
*/
Class<? extends Annotation> value();
}
注解用途
- 生成文档,通过
@Documented
生成 javadoc 文档; - 编译检查,通过代码里标识的元数据让编译器在编译期间进行检查验证(如:
@Autowired
); - 编译时动态处理,编译时通过代码里标识的元数据动态处理(如:动态生成代码);
- 运行时动态处理,运行时通过代码里标识的元数据动态处理(如:使用反射注入实例)。
使用
注解属性赋值
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyTestAnnotation {
String name() default "mao";
int age() default 18;
}
@MyTestAnnotation(name = "father", age = 50)
public class Father {
}
获取注解属性
可以通过反射获取注解属性。
/**是否存在对应 Annotation 对象*/
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
return GenericDeclaration.super.isAnnotationPresent(annotationClass);
}
/**获取 Annotation 对象*/
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
Objects.requireNonNull(annotationClass);
return (A) annotationData().annotations.get(annotationClass);
}
/**获取所有 Annotation 对象数组*/
public Annotation[] getAnnotations() {
return AnnotationParser.toArray(annotationData().annotations);
}
/**获取类注解属性*/
Class<Father> fatherClass = Father.class;
boolean annotationPresent = fatherClass.isAnnotationPresent(MyTestAnnotation.class);
if(annotationPresent){
MyTestAnnotation annotation = fatherClass.getAnnotation(MyTestAnnotation.class);
System.out.println(annotation.name());
System.out.println(annotation.age());
}
/**获取方法注解属性*/
try {
Field age = fatherClass.getDeclaredField("age");
boolean annotationPresent1 = age.isAnnotationPresent(Age.class);
if(annotationPresent1){
Age annotation = age.getAnnotation(Age.class);
System.out.println(annotation.value());
}
Method play = PlayGame.class.getDeclaredMethod("play");
if (play!=null){
People annotation2 = play.getAnnotation(People.class);
Game[] value = annotation2.value();
for (Game game : value) {
System.out.println(game.value());
}
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
注解的本质
注解就是一个由@interface
修饰的类,编译后会自动继承java.lang.annotation.Annotation
接口,定义方法就相当于注解属性(成员变量)。
/**Annotation接口源码*/
public interface Annotation {
boolean equals(Object obj);
int hashCode();
Class<? extends Annotation> annotationType();
}
注意:注解不支持继承,不能使用关键字extends
来继承某个@interface
。
注解分类
一、预置注解,使用这些注解后编译器就会进行检查;
- @Deprecated 过时/废弃
- @Override 重写
- @SuppressWarnings 忽略警告
- @SafeVarargs 参数安全类型(Java 1.7)
- @FunctionalInterface 函数式接口( Java 1.8 )
二、元注解,用于定义注解的注解
@Documented 文档
将注解中的元素包含到 Javadoc 中。
@Retention 保留期
- RetentionPolicy.SOURCE:注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
- RetentionPolicy.CLASS:注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。如@Override、@Deprecated、@SuppressWarnning等
- RetentionPolicy.RUNTIME:注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。如SpringMvc中的@Controller、@Autowired、@RequestMapping等。
@Target 目标(限定运用场景)
- ElementType.ANNOTATION_TYPE:可以给一个注解进行注解
- ElementType.CONSTRUCTOR:可以给构造方法进行注解
- ElementType.FIELD:可以给属性进行注解
- ElementType.LOCAL_VARIABLE:可以给局部变量进行注解
- ElementType.METHOD:可以给方法进行注解
- ElementType.PACKAGE:可以给一个包进行注解
- ElementType.PARAMETER:可以给一个方法内的参数进行注解
- ElementType.TYPE:可以给一个类型进行注解,比如类、接口、枚举
@Inherited 继承
如果一个类(非接口、非方法)用上了@Inherited修饰的注解,那么其子类也会继承这个注解。
@Repeatable 重复(Java 1.8)
在需要对同一种注解多次使用时,往往需要借助@Repeatable。
三、自定义注解,可以根据自己的需求定义注解
注解通常配合反射
或者切面
一起使用来实现相应的业务逻辑。
定义一个系统日志注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
/**
* 操作说明
*/
String value() default "";
}
找到使用了注解的方法,利用切面进行日志记录。
这里有些类或者参数只做演示,没有给出具体的实现(如SysLogMapper、SysLogEntity等),以看懂注解配合切面使用为目的。
@Aspect
@Component
public class SysLogAspect {
@Autowired
private SysLogMapper sysLogMapper;
@Pointcut("@annotation(com.jourwon.annotation.SysLog)")
public void logPointCut() {
}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
long beginTime = System.currentTimeMillis();
//执行方法
Object result = point.proceed();
//执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
//保存日志
saveSysLog(point, time);
return result;
}
private void saveSysLog(ProceedingJoinPoint joinPoint, long time) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
SysLogEntity sysLog = new SysLogEntity();
SysLog syslog = method.getAnnotation(SysLog.class);
if (syslog != null) {
//注解上的描述
sysLog.setOperation(syslog.value());
}
//保存系统日志
sysLogMapper.insertSelective(sysLog);
}
}
在方法中使用注解
@SysLog("保存用户")
@PostMapping("/save")
public Result save(@RequestBody SysUserEntity user) {
sysUserService.save(user);
return Result.ok();
}