注解实现及应用

注解一种标记式高耦合的配置方式,Java注解是从JDK1.5引入的。注解用于为Java代码提供元数据。作为元数据注解不直接影响代码执行。注解的本质就是一个Annotation接口。

1
2
3
4
5
6
7
8
9
public interface Annotation {
boolean equals(Object obj);

int hashCode();

String toString();

Class<? extends Annotation> annotationType();
}

注解本身就是Annotation接口的子接口,注解中其实可以有属性和方法,但接口中属性和方法都是static fianl的,对于注解没有意义,而定义接口的方法就相当于注解的属性,也就是为什么说注解只有成员变量,其实其就是接口的方法,也就是为什么成员变量会有括号。

注解属性类型可以有一下几种类型:

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

元注解

作用于注解的注解,JDK提供的元注解有@Retention@Target@Documented@Inherited以及JDK8新增的@Repeatable五种。

@Retention

表示注解存在阶段是保留在源码(编译期),字节码(类加载)或者运行期JVM中运行),在@Retention注解中使用枚举RetentionPolicy来表示注解的生命周期

  • @Retention(RetentionPolicy.SOURCE),注解仅存在于源码中,在class字节码文件中不包含

  • @Retention(RetentionPolicy.CLASS)默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得

  • @Retention(RetentionPolicy.RUNTIME), 注解会在class字节码文件中存在,在运行时可以通过反射获取

若自定义注解,自定义注解如果只存着源码中或者字节码文件中就无法发挥作用,而在运行期间能获取到注解才能实现我们目的,所以自定义注解中肯定是使用 @Retention(RetentionPolicy.RUNTIME)

1
2
3
4
5
6
7
8
9
10
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}

@Target

表示注解的作用目标,作用范围可以是方法方法参数等,通过枚举类ElementType表达作用类型。

  • @Target(ElementType.TYPE) 作用接口枚举注解

  • @Target(ElementType.FIELD) 作用属性字段枚举的常量

  • @Target(ElementType.METHOD) 作用方法

  • @Target(ElementType.PARAMETER) 作用方法参数

  • @Target(ElementType.CONSTRUCTOR) 作用构造函数

  • @Target(ElementType.LOCAL_VARIABLE)作用局部变量

  • @Target(ElementType.ANNOTATION_TYPE)作用于注解@Retention注解中就使用该属性)

  • @Target(ElementType.PACKAGE) 作用于

  • @Target(ElementType.TYPE_PARAMETER) 作用于类型泛型,即泛型方法、泛型类、泛型接口 (JDK8加入)

  • @Target(ElementType.TYPE_USE) 类型使用,可以用于标注任意类型除了 classJDK8加入)

1
2
3
4
5
6
7
8
9
10
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}

@Documented

其作用是能够将注解中的元素包含到 Javadoc 中去。

@Inherited

@Inherited注解了的注解修饰了一个父类,若其子类没有被其他注解修饰,则其子类也继承了父类的注解。

@Repeatable

被该元注解修饰的注解可以同时作用一个对象多次,但是每次作用注解又可以代表不同的含义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Documented
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = ElementType.METHOD)
public @interface Prople {
Game[] value();
}

@Documented
@Repeatable(Prople.class)
@Target(value = ElementType.METHOD)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Game {
String value() default "";
}

public class PlayGame {
@Game(value = "LOL")
@Game(value = "PUBG")
@Game(value = "NFS")
@Game(value = "Dirt4")
public void play() {
}
}

注解属性

注解的属性与类中定义的变量有异曲同工之处,注解中的变量都是成员变量,并且注解中是没有方法的,只有成员变量变量名就是使用注解括号中对应的参数名,变量返回值就是使用注解括号中对应参数类型。

@Repeatable注解中的变量则类型则是对应Annotation(接口)的泛型Class

1
2
3
4
5
6
7
8
9
10
11
@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();
}

JDK内置注解

JDK预定义了@Override@Deprecated@SuppressWarnings三种注解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}

应用

若注解有多个属性,给属性赋值时使用逗号隔开分别赋值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Documented
@Inherited
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = ElementType.TYPE)
public @interface InvokeListener {
String name() default "baseService";
int weight() default 25;
}

@InvokeListener(name = "taskService", weight = 50)
public class TaskServiceImpl implements TaskService {
@FieldListener(value = "baseTask")
private String taskName;
}

在自定义注解后在使用时通常需要获取注解的属性,需要通过反射的方式获取。获取类注解属性:

1
2
3
4
5
6
7
Class<TaskServiceImpl> taskServiceClass = TaskServiceImpl.class;
boolean annotationPresent = taskServiceClass.isAnnotationPresent(InvokeListener.class);
if (annotationPresent) {
InvokeListener annotation = taskServiceClass.getAnnotation(InvokeListener.class);
System.out.println(annotation.name());
System.out.println(annotation.weight());
}

获取方法注解属性:

1
2
3
4
5
6
7
8
Method play = PlayGame.class.getDeclaredMethod("play");
if (play != null) {
People annotation = play.getAnnotation(People.class);
Game[] value = annotation.value();
for (Game game : value) {
System.out.println(game.value());
}
}

获取属性注解属性:

1
2
3
4
5
6
7
Class<TaskServiceImpl> taskServiceClass = TaskServiceImpl.class;
Field field = taskServiceClass.getDeclaredField("taskName");
boolean annotationPresent = field.isAnnotationPresent(FieldListener.class);
if (annotationPresent) {
FieldListener annotation = field.getAnnotation(FieldListener.class);
System.out.println(annotation.value());
}

在Spring中使用自定义注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ListenerConfig implements ApplicationContextAware {

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, Object> beanMap = applicationContext.getBeansWithAnnotation(InvokeListener.class);
for (Object bean : beanMap.values()) {
Field[] fields = bean.getClass().getDeclaredFields();
for (Field field : fields) {
FieldListener fieldListener = field.getAnnotation(FieldListener.class);
if (fieldListener != null) {
System.out.println(fieldListener.value());
}
}
Method[] methods = bean.getClass().getMethods();
for (Method method : methods) {
MethodListener methodListener = method.getAnnotation(MethodListener.class);
if (methodListener != null) {
System.out.println(methodListener.value());
}
}
}
}

}