spring中的100个问题-注解是如何工作的(一)

分类: SPRING 发布于:

注解类似class结构体中的tag,它解决了class垂直继承机制和interface水平切分机制都无法解决的问题,那就是异构聚合问题。

注解解决的基本问题

因为java的设计,可执行文件在执行前都要加载大量的class文件。这些classfile被加载到内存区以后,如何对各种不同类型的数据进行检索?

在框架设计时经常有这样的需求,因为它需要的是最基本的信息。

比如,当前场景下,需要一个桌子(desk)和一只狗(dog)的对象,使用继承和接口都无法优雅的解决这个问题。因为这两个是严重不相关的信息。

注解是什么?

在wikipedia中的定义

In the Java computer programming language, an annotation is a form of syntactic metadata that can be added to Java source code.[1] Classes, methods, variables, parameters and packages may be annotated. Like Javadoc tags, Java annotations can be read from source files. Unlike Javadoc tags, Java annotations can also be embedded in and read from class files generated by the compiler. This allows annotations to be retained by Java VM at run-time and read via reflection.[2] It is possible to create meta-annotations out of the existing ones in Java.[3]

上述可以理解为:

  • 注解是一种可以加入到源代码的语法元信息,包括class、方法、字段和包。

那么,什么又是元信息?https://en.wikipedia.org/wiki/Metadata

Metadata is "data [information] that provides information about other data".[1] Many distinct types of metadata exist, among these descriptive metadata, structural metadata, administrative metadata[2], reference metadata and statistical metadata[3]

简言之,元数据就是定义系统的基本数据。

在java中所有数据都被以class文件的形式定义,除了引用和命名空间之外不存在class之外定义的数据。

在C++描述java语法的层面,它把class文件打散后,以独立C++对象(oop)的形式描述java class的每一个组成部分,比如 class、Method、Instance、Parameter、annotation。

所以,注解在(c++)运行时层面就是存在的基本数据。

JDK中预置的注解

https://docs.oracle.com/javase/tutorial/java/annotations/predefined.html

注解是在何时(when)、何地(where)、如何(how)起作用的?

注解在java体系中的结构层次图,简要描述如下 Java annotations

当注解应用在字段时
class Foo {
    @FieldAnnotation("here") public Object object;

    @FieldAnnotation("there") public Object other;

    @MethodAnnotation public Object setObject(@ParameterAnnotation final Object object){
      this.object = object;
    }
}

(一)如何根据标注内容设置字段的值?

其中一种方式,使用class的静态方法

void setPropertyByAnnotationName(final Object object, final String name, final Object value) {
    final Field[] fields = object.getClass().getDeclaredFields();
    for (final Field field : fields) {
        final FieldAnnotation fieldAnnotation = field.getAnnotation(FieldAnnotation.class);
        if(fieldAnnotation!=null && name.equals(fieldAnnotation.value()){
            field.set(object, value);
        }
    }
}

设置方法

Foo foo = new Foo();
// 查找标注here的字段,设置为“this is set”
setPropertyByAnnotationName(foo, "here", "this is set!");
setPropertyByAnnotationName(foo, "there", Calendar.getInstance());

(二) 如何根据标注内容调起class内部方法?

使用类似的方法

void callMethodByAnnotation(final Object object, final Object value) {
    final Method[] methods = object.getClass().getDeclaredMethods();
    for (final Method method : methods) {
        final MethodAnnotation methodAnnotation = method.getAnnotation(MethodAnnotation.class);
        if(fieldAnnotation!=null){
            // 这里调用method.invoke方法
            method.invoke(object, value);
        }
    }
}

调用方式

Foo foo = new Foo();
callMethodByAnnotation(foo, ""this is set!");

(三) 如何设置方法参数的默认值

// 声明一个注解
@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME)
public @interface ParameterAnnotation { String value(); }

class Foo {
    @MethodAnnotation public Object setObject(@ParameterAnnotation("inside") final Object object){
        this.object = object;
    }
}

public void callMethodByAnnotation(final Object object, final Map map) {
    // 取到对应的注解方法
    final Method[] methods = object.getClass().getDeclaredMethods();
    for (final Method method : methods) {
        final MethodAnnotation methodAnnotation = method.getAnnotation(MethodAnnotation.class);
        if (methodAnnotation != null) {
            // 获取注解参数的注释
            final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
            final Object[] parameters = new Object[parameterAnnotations.length];
            for (int i = 0; i < parameterAnnotations.length; i++) {
                parameters[i] = null;

                final Annotation[] annotations = parameterAnnotations[i];
                for (final Annotation annotation : annotations) {
                    if (annotation instanceof ParameterAnnotation) {
                        // 取到注解中对应的参数值
                        parameters[i] = map.get(((ParameterAnnotation) annotation).value());
                    }
                }
            }
            method.invoke(object, parameters);
        }
    }
}

// 最后,方法调用方式如下
Map map = new Map();
map.put("inside", "this is set!");
Foo foo = new Foo();
callMethodByAnnotation(foo,map);