java@ 注解原理与使用

Java 反射

         java 反射机制的定义:

在运行转态时(动态的)时。

对于任意一个类,都能够知道这个类的所有属性和方法

对于任意一个对象,都能够知道调用它的任意属性和方法

Class 对象

java 中用对象来对现实生活中的事物进行抽象,如人(现实生活)抽象到一个 person 类(java 对象)。但有没有想过,java 中的类(现实生活)其实也是一个 Class 对象(对象)。因此,这个 Class 类就包含了所有你定义的 Class 信息,包括所有的方法(私有,公有)、构造器、实现了那些方法、哪些注解信息、所有的属性等相关的信息,它还提供 newInstance() 方法来生成实例。

         Class 对象是一个相当特殊的对象,是一个超泛化的对象,是所有对象的对象。因此智能由 JVM 在创建其他对象的时候自动创建,因此 Class 对象又称包含了其他对象的字节码的信息的对象。

反射实现方法

 Class 类是反射机制的起源,我们得到 Class 类对象有 3 种方法:

  第一种:通过类名获得

  Class<?> class = ClassName.class;

  第二种:通过类名全路径获得:

  Class<?> class = Class.forName("类名全路径");

  第三种:通过实例对象获得:

Class<?> class = object.getClass();

 

总结:第一种方法:类字面常量使得创建 Class 对象的引用时不会自动地初始化该对象,而是按照之前提到的加载,链接,初始化三个步骤,这三个步骤是个懒加载的过程,不使用的时候就不加载。

   第二种方法:Class 类自带的方法。

   第三种方法:是所有的对象都能够使用的方法,因为 getClass()方法是 Object 类的方法,所有的类都继承了 Object,因此所有类的对象也都具有 getClass() 方法。

 

建议:使用类名.class,这样做即简单又安全,因为在编译时就会受到检查,因此不需要置于 try 语句块中,并且它根除了对 forName() 方法的调用,所以也更高效。

反射作用

         看完反射的定义,我们就知道,反射机制就是增加了程序的灵活性,最重要的用途就是来开发通用型的框架,如 Spring,将所有类的 bean 交给 spring 容器管理。无论是 XML 配置还是注解配置,当我们获取 bean 时,容器都会读取配置,这些配置就是类的信息,容器根据反射,生成实例返回。具体的作用还有动态代理、切面逻辑等等,其本质就是反射。

 

Java 注解相关概念

什么是注解

         注解 (Annotation) 就是 Java 提供了一种元程序中的元素关联任何信息或者任何元数据(metadata)的途径和方法,Annotation(注解)是一个接口,程序可以通过放射来获取指定的程序元素的 Annotation 对象,然后通过 Annotation 对象来获取注解里面的元数据。

         Annotation 能被用来为某个程序元素(类、方法、成员变量等)关联任何的信息。需要注意的是,这里存在着一个基本的规则:Annotation 不能影响程序代码的执行,无论增加、删除 Annotation,代码都始终如一的执行;另外,尽管一些 annotation 通过 java 的放射 api 方法在运行时被访问,而 java 语言解释器在工作时忽略了这些 annotation。正是由于 java 虚拟机忽略了 annotation,导致了 annotation 类型在代码中“不起作用”的;只有通过某种配套的工具才会对 annotation 类型中的信息进行访问和处理。

什么是 metadata(元数据)

 元数据从 metadata 一词译来,就是“关于数据的数据”的意思。
  元数据的功能作用有很多,比如:你可能用过 Javadoc 的注释自动生成文档。这就是元数据功能的一种。总的来说,元数据可以用来创建文档,跟踪代码的依赖性,执行编译时格式检查,代替已有的配置文件。如果要对于元数据的作用进行分类,目前还没有明确的定义,不过我们可以根据它所起的作用,大致可分为三类: 
    1. 编写文档:通过代码里标识的元数据生成文档
    2. 代码分析:通过代码里标识的元数据对代码进行分析
    3. 编译检查:通过代码里标识的元数据让编译器能实现基本的编译检查
  在 Java 中元数据以标签的形式存在于 Java 代码中,元数据标签的存在并不影响程序代码的编译和执行,它只是被用来生成其它的文件或针在运行时知道被运行代码的描述信息。
  综上所述:
    第一,元数据以标签的形式存在于 Java 代码中。
    第二,元数据描述的信息是类型安全的,即元数据内部的字段都是有明确类型的。
    第三,元数据需要编译器之外的工具额外的处理用来生成其它的程序部件。
    第四,元数据可以只存在于 Java 源代码级别,也可以存在于编译之后的 Class 文件内部。

Annotation 和 Annotation 类型:

Annotation:

  Annotation 使用了在 java5.0 所带来的新语法,它的行为十分类似 public、final 这样的修饰符。每个 Annotation 具有一个名字和成员个数 >=0。每个 Annotation 的成员具有被称为 name=value 对的名字和值(就像 javabean 一样),name=value 装载了 Annotation 的信息。

  Annotation 类型:

  Annotation 类型定义了 Annotation 的名字、类型、成员默认值。一个 Annotation 类型可以说是一个特殊的 java 接口,它的成员变量是受限制的,而声明 Annotation 类型时需要使用新语法。当我们通过 java 反射 api 访问 Annotation 时,返回值将是一个实现了该 annotation 类型接口的对象,通过访问这个对象我们能方便的访问到其 Annotation 成员。

注解的分类:

  根据注解参数的个数,我们可以将注解分为三类:
    1. 标记注解: 一个没有成员定义的 Annotation 类型被称为标记注解。这种 Annotation 类型仅使用自身的存在与否来为我们提供信息。比如后面的系统注解 @Override;
    2. 单值注解
    3. 完整注解  

根据注解使用方法和用途,我们可以将 Annotation 分为三类:
    1.JDK 内置系统注解
    2. 元注解
    3. 自定义注解

系统内置标准注解:

注解的语法比较简单,除了 @符号的使用外,他基本与 Java 固有的语法一致,JavaSE 中内置三个标准注解,定义在 java.lang 中:
    @Override:用于修饰此方法覆盖了父类的方法;
    @Deprecated:用于修饰已经过时的方法;
    @SuppressWarnnings: 用于通知 java 编译器禁止特定的编译警告。

元注解:

  元注解的作用就是负责注解其他注解。Java5.0 定义了 4 个标准的 meta-annotation 类型,它们被用来提供对其它 annotation 类型作说明。Java5.0 定义的元注解:
    1.@Target,
    2.@Retention,
    3.@Documented,
    4.@Inherited

@Target

         @Target 说明了 Annotation 所修饰的对象范围。取值(ElementType)有

    1.CONSTRUCTOR: 用于描述构造器
    2.FIELD: 用于描述域
    3.LOCAL_VARIABLE: 用于描述局部变量
    4.METHOD: 用于描述方法
    5.PACKAGE: 用于描述包
    6.PARAMETER: 用于描述参数
    7.TYPE: 用于描述类、接口 (包括注解类型) 或 enum 声明

@Retention

         @Retention 定义了该 Annotation 被保留的时间长短,如某些 Annotation 仅出现在源码中,而被编译器丢弃。取值(RetentionPoicy)有:

1.SOURCE: 在源文件中有效(即源文件保留)
    2.CLASS: 在 class 文件中有效(即 class 保留)
    3.RUNTIME: 在运行时有效(即运行时保留)

属性值是 RUTIME, 这样注解处理器可以通过反射,获取到该注解的属性值,从而去做一些运行时的逻辑处理属性值是 RUTIME, 这样注解处理器可以通过反射,获取到该注解的属性值,从而去做一些运行时的逻辑处理

@Documented

         @Documented 用于描述其他类型的 annotation 应该被作为被标注的程序成员公共的 API,因此可以被例如 javadoc 此类的工具文档化。Documented 是一个标记注解,没有成员。

@Target(ElementType.FIELD)

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface Column {

</span><span style="color: rgba(0, 0, 255, 1)">public</span> String name() <span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">fieldName</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 0, 255, 1)">public</span> String setFuncName() <span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">setField</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 0, 255, 1)">public</span> String getFuncName() <span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">getField</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 0, 255, 1)">public</span> boolean defaultDBValue() <span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;

}

 

@Inherited

@Inherited 元注解是一个标记注解,@Inherited 阐述了某个被标注的类型是被继承的。如果一个使用了 @Inherited 修饰的 annotation 类型被用于一个 class,则这个 annotation 将被用于该 class 的子类。

自定义注解

package annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
String value()
default "";
}

 

四、注解组合使用

Java spring 开发时,经常在一个类上有多个注解,如 SpringMVC 注解

@RestController
@RequestMapping(‘/person’)

可以合并成一个

@PathRestController(‘/person’)

实现是

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.core.annotation.AliasFor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RestController
@RequestMapping
public @interface PathRestController {
@AliasFor(
"path")
String[] value()
default {};

@AliasFor(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">value</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
String[] path() </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> {};

}