1. 元注解
元注解是 Java 提供的一些基本注解,使用这些元注解区可疑创建新的注解;可以先大致看一下元注解,然后去看后面自定义注解的例子。
元注解有 @Retention, @Documented, @Target, @Inherited,@Repeatable 五种。
1.1 @Retention
@Retention 可以定义注解的生命周期,注解的存活时间有如下三种:
- RetentionPolicy.SOURCE: 注解只在源码阶段保留,在编译器完整编译之后,它将被丢弃忽视;
例:@Override,源码如下所示:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
- RetentionPolicy.CLASS: 注解只被保留到编译进行的时候,注解不会被加载到 JVM 中;
- RetentionPolicy.RUNTIME: 注解可以保留到程序运行的时候,注解会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。
例: @Service 拥有 @Retention(RetentionPolicy.RUNTIME) 注解,所以在程序运行时可以捕获到它们。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
@AliasFor(
annotation = Component.class
)
String value() default "";
}
1.2 @Target
@Target 表示该注解用于什么地方,可以理解为:当一个注解被 @Target 注解时,这个注解就被限定了运用的场景。可以使用的 ElementType 参数:
- ElementType.CONSTRUCTOR: 对构造方法进行注解;
- ElementType.ANNOTATION_TYPE: 对注解进行注解;
- ElementType.FIELD: 对属性、成员变量、成员对象(包括 enum 实例)进行注解;
- ElementType.LOCAL_VARIABLE: 对局部变量进行注解;
- ElementType.METHOD: 对方法进行注解;
- ElementType.PACKAGE: 对包进行注解;
- ElementType.PARAMETER: 对描述参数进行注解;
- ElementType.TYPE: 对类、接口、枚举进行注解;
@Override 是 ElementType.METHOD,对方法进行注解;
@Service 是 ElementType.TYPE,对类、接口、枚举进行注解;
1.3 @Documented
@Documented 是一个简单的标记注解,表示是否将注解信息添加在 Java 文档,即 Javadoc 中。
1.4 @Inherited
Inherited 是指继承,@Inherited 定义了一个注释与子类的关系。如果一个超类带有 @Inherited 注解,那么对于该超类,它的子类如果没有被任何注解应用的话,那么这个子类就继承了超类的注解。
1.5 @Repeatable
@Repeatable 是 Java 8 中加入的,是指可重复的意思。通常使用 @Repeatable 的时候指注解的值可以同时取多个。例如:@MapperScan 这个注解。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({MapperScannerRegistrar.class})
@Repeatable(MapperScans.class)
public @interface MapperScan {
// 略
}
2. 自定义注解
自定义了个 HelloWorld 注解 ,声明注解使用 @ Interface,如下所示。
public @interface HelloWorld
完整的一个注解,注解有两个属性,user 用户,message 名称(默认为helloworld)。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface HelloWorld {
public String user();
public String message() default "helloWorld";
}
在方法上标明注解的例子:
public class Main {
public String user;
public String message;
public Main(String user, String message) {
this.user = user;
this.message = message;
}
@HelloWorld(user="syrdbt", message = "hello world")
void show() {
System.out.println("show");
}
@HelloWorld(user="syrdbt")
void show1() {
System.out.println("show1");
}
@Override
public String toString() {
return "Main{" +
"user='" + user + '\'' +
", message='" + message + '\'' +
'}';
}
}
访问注解:
public class Test {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
// 传入字节码
trackUseCases(Main.class);
}
public static void trackUseCases(Class<?> cl) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
for (Method m : cl.getDeclaredMethods()) {
// 获得注解的对象
HelloWorld helloWorld = m.getAnnotation(HelloWorld.class);
// 访问注解上面的属性
if (helloWorld != null) {
System.out.println("Found Use Case:" + helloWorld.user() + " "
+ helloWorld.message());
}
}
}
}
运行截图如下所示,可以知道获取注解的属性也不是有序的。
3. 使用注解产生一个 bean
这里使用注解产生一个 bean ,使用从注解的获取属性,然后使用反射获取 Main 类的构造函数,调用构造函数和从注解的获取属性 生成 bean 。
public class Test {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
// 传入字节码
makeBean(Main.class);
}
public static void makeBean(Class<?> cl) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 获取构造函数
Constructor<?> constructor = cl.getConstructor(String.class, String.class);
for (Method m : cl.getDeclaredMethods()) {
//获得注解的对象
HelloWorld helloWorld = m.getAnnotation(HelloWorld.class);
if (helloWorld != null) {
System.out.println("Found Use Case:" + helloWorld.user() + " "
+ helloWorld.message());
// 使用构造函数生成 bean
Object obj = constructor.newInstance(helloWorld.user(), helloWorld.message());
System.out.println(obj.toString());
}
}
}
}
运行截图如下所示,可以发现获取注解的属性也不是有序的。