书成

再这样堕落下去就给我去死啊你这混蛋!!!

0%

Java注解

Java 注解

Java 注解从 Java5 开始提供,用于标识(让编译器检查代码)或者注入信息。Jdk 内置了一套注解,分为基本注解与元注解。

基本注解

基本注解在 java.lang 包下,主要有以下5个:

注解 使用
@Override 标识方法为重写方法,覆盖超类中声明的方法
@Deprecated 标识过时方法
@FunctionalInterface 标识函数式接口,让编译器检查接口定义是否符合函数式接口定义
@SuppressWarnings 抑制unchecked警告
@SafeVarargs 抑制堆污染警告

元注解

元注解是注解的注解,方便实现自定义注解的功能,主要包括以下几种:

注解 使用
@Retention 表示注解保留在源码,字节码或运行期,用枚举类 RetentionPolicy 表示保留的时期,默认是字节码时期
@Target 表示注解的作用范围,例如字段,方法等范围,通过枚举类 ElementType 表示
@Documented 将注解的元素包含到 Javadoc 中去
@Inherited 如果它修饰的注解修饰了一个父类,子类会继承父类的注解
@Repeatable 被它修饰的注解可以同时作用一个对象多次,每次注解可以代表不同的含义
@Native 表示一个字段可能涉及 native code

自定义注解

注解本身是 Annotation 接口的子接口,注解中的属性相当于接口的方法,因此自定义注解中的属性需要带有括号。

注解中的属性类型可以有以下的类型:

  • 基本数据类型
  • String
  • 枚举类型
  • 注解类型
  • Class 类型
  • 以上类型的数组

一个自定义的注解如下所示,将 @Retention 注解的值设置为 RUNTIME,后续可以通过反射获取注解中的值。

1
2
3
4
5
6
7
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Target({ElementType.TYPE})
public @interface MyAnnotation {
String name() default "root";
String password() default "123456";
}

自定义注解注入信息

自定义注解后,可以通过反射将注解的属性注入到对象或方法中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Main {

@MyAnnotation(name = "testName", password = "123456")
public static void testMyAnnotation(String name, String password) {
System.out.println(name + "***" + password);
}

public static void main(String[] args) throws Exception {
Class clazz = Main.class;
Method method = clazz.getDeclaredMethod("testMyAnnotation", String.class, String.class);
boolean annotationPresent = method.isAnnotationPresent(MyAnnotation.class);
if (annotationPresent) {
MyAnnotation myAnnotation = (MyAnnotation) method.getAnnotation(MyAnnotation.class); // 获得该注解
// 将注解的值注入方法参数中
method.invoke(null, myAnnotation.name(), myAnnotation.password());

}
}
}

自定义注解注入对象

注解的属性只有上面提到的六种类型,有的时候需要注入的不仅仅是这六种类型数据,而是一个对象。注入对象相对比较麻烦。

定义一个 User 类,一个 InjectUser 注解与一个 UserDao,为UserDao注入User属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public class User {
private String name;
private String password;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}

@Retention(RetentionPolicy.RUNTIME)
public @interface InjectUser {

String name();
String password();
}

public class UserDao {
private User user;

public User getUser() {
return user;
}

// 通过注解注入属性
@InjectUser(name="testName", password = "testPassword")
public void setUser(User user) {
this.user = user;
}

@Override
public String toString() {
return "UserDao{" +
"user=" + user +
'}';
}
}

然后修改 main 方法如下,为 UserDao 类注入 User 对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public static void main(String[] args) throws Exception {
// 使用内省,获得 UserDao 中属性 user 的方法
PropertyDescriptor descriptor = new PropertyDescriptor("user", UserDao.class);
// 创建注入的具体对象
User user = (User) descriptor.getPropertyType().newInstance();
// 得到 UserDao 中 user 属性的写方法,即 setUser
Method method = descriptor.getWriteMethod();
// 获得注解
Annotation annotation = method.getAnnotation(InjectUser.class);
// 获得注解的属性
Method[] methods = annotation.getClass().getMethods();
for (Method m : methods) {
String name = m.getName(); // 得到注解属性的名字
try{
PropertyDescriptor descriptor1 = new PropertyDescriptor(name, User.class);
Method method1 = descriptor1.getWriteMethod(); // 得到 User 对象的属性的写方法
Object o = m.invoke(annotation, null); // 得到注解中的属性值
method1.invoke(user, o); // 将注解中的属性值设置到 user 中去
}catch (Exception e){
continue;
}
}
UserDao userDao = new UserDao();
method.invoke(userDao, user); // 调用 UserDao 的 setUser 方法设置对象即可
System.out.println(userDao.getUser());
}