java注解的使用

java 中注解的使用

1. 说再前面

使用注解开发的好处

1. 使代码更加干净易读,易于维护修改。比如,以前使用 spring 的开发,都是基于 xml 文件实现了统一的配置管理,但是缺点也是显而易见的,就是随着项目的越来越大,xml 文件会变得越来越复杂,维护成本也会越来越高。使用注解就可以提供更大的便捷性,易于维护修改。

2 可以实现代码的类型检查,特别是在编译器的角度实现一些类型检查,比如预检查 (@Override) 和抑制警告 (@SuppressWarnings) 等。

3 自定义注解,作为额外信息的载体,存储有关程序的额外信息

2 注解的分类以及使用

​ Java 注解是附加在代码中的一些元信息,用于编译和运行时进行解析和使用,起到说明、配置的功能。

注解不会影响代码的实际逻辑,仅仅起到辅助性的作用。包含在 java.lang.annotation 包中。注解的定义类似于接口的定义,使用 @interface 来定义,定义一个方法即为注解类型定义了一个元素,方法的声明不允许有参数或 throw 语句,返回值类型被限定为原始数据类型、字符串 String、Class、enums、注解类型,或前面这些的数组,方法可以有默认值。注解并不直接影响代码的语义,但是他可以被看做是程序的工具或者类库。它会反过来对正在运行的程序语义有所影响。注解可以从源文件、class 文件或者在运行时通过反射机制多种方式被读取。

一般来说,注解一般分为三种类型: 元注解,标准注解,自定义注解

2.1 元注解

元注解是专职负责注解其他注解,主要是标明该注解的使用范围,生效范围。我们是不能改变它的,只能用它来定义我们自定义的注解。

包括 @Retention @Target @Document @Inherited 四种。(java.lang.annotation 中提供,为注释类型)。

注解 说明
@Target 定义注解的作用目标,也就是可以定义注解具体作用在类上,方法上,还是变量上
@Retention 定义注解的保留策略。RetentionPolicy.SOURCE: 注解仅存在于源码中,在 class 字节码文件中不包含;RetentionPolicy.CLASS: 默认的保留策略,注解会在 class 字节码文件中存在,但运行时无法获得;RetentionPolicy.RUNTIME: 注解会在 class 字节码文件中存在,在运行时可以通过反射获取到。
@Document 说明该注解将被包含在 javadoc 中
@Inherited 说明子类可以继承父类中的该注解

Target 类型主要依赖于 ElementType 这个类型,具体的类型如下:

Target 类型 说明
ElementType.TYPE 接口、类、枚举、注解
ElementType.FIELD 字段、枚举的常量
ElementType.METHOD 方法
ElementType.PARAMETER 方法参数
ElementType.CONSTRUCTOR 构造函数
ElementType.LOCAL_VARIABLE 局部变量
ElementType.ANNOTATION_TYPE 注解
ElementType.PACKAGE

2.2 标准注解

Java 标准注解提供了三个,定义在 java.lang 中的注解,我认为这三个注解的作用更多的是一种注释

  • @Override 表示当前方法覆盖父类中的方法。

  • @Deprecated 标记一个元素为已过期,避免使用

支持的元素类型为:CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE

  • @SuppressWarnings 不输出对应的编译警告

    一个简单的使用 demo:

    @SuppressWarnings(value = {"unused", "rawtypes"})
    public class Children  extends Parent{
        @Override
        public void work() {
            System.out.println("我是一个被重写的方法");
        }
    
    <span class="hljs-meta">@Deprecated</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">play</span><span class="hljs-params">()</span>{
        System.out.println(<span class="hljs-string">"这个方法不推荐使用了"</span>);
    }
    

    }

2.3 自定义注解实现一个 sql 语句的拼接

需要注意的方面:注解的定义类似于接口的定义,使用 @interface 来定义,定义一个方法即为注解类型定义了一个元素,方法的声明不允许有参数或 throw 语句,返回值类型被限定为原始数据类型、字符串 String、Class、enums、注解类型,或前面这些的数组,方法可以有默认值。

自定义注解一般可以分为三步: 定义注解,使用注解,读取注解

定义注解

@Target(ElementType.TYPE) // 注解加载类上
@Retention(RetentionPolicy.RUNTIME) // 运行时读取注解

public @interface Table {
String value();
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public@interface UserFiled {
String name();
String type();
int length();
}

使用注解

// 将自定义的注解加在用户上, 实现一个表的映射
@Table(value = "user_table")
public class User {
<span class="hljs-meta">@UserFiled(name = "user_id",type = "int",length = 8)</span>
<span class="hljs-keyword">private</span> <span class="hljs-type">int</span> userId;

<span class="hljs-meta">@UserFiled(name = "user_name",type = "varchar",length = 16)</span>
<span class="hljs-keyword">private</span> String userName;

<span class="hljs-meta">@UserFiled(name = "password",type = "varchar",length = 16)</span>
<span class="hljs-keyword">private</span> String password;

<span class="hljs-keyword">public</span> <span class="hljs-type">int</span> <span class="hljs-title function_">getUserId</span><span class="hljs-params">()</span> {
    <span class="hljs-keyword">return</span> userId;
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">setUserId</span><span class="hljs-params">(<span class="hljs-type">int</span> userId)</span> {
    <span class="hljs-built_in">this</span>.userId = userId;
}

<span class="hljs-keyword">public</span> String <span class="hljs-title function_">getUserName</span><span class="hljs-params">()</span> {
    <span class="hljs-keyword">return</span> userName;
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">setUserName</span><span class="hljs-params">(String userName)</span> {
    <span class="hljs-built_in">this</span>.userName = userName;
}

<span class="hljs-keyword">public</span> String <span class="hljs-title function_">getPassword</span><span class="hljs-params">()</span> {
    <span class="hljs-keyword">return</span> password;
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">setPassword</span><span class="hljs-params">(String password)</span> {
    <span class="hljs-built_in">this</span>.password = password;
}

}

读取注解的内容

/**
 * 读取注解中的值
 */
public class GetUser {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class userClass = Class.forName("annocation.blog.User");
    <span class="hljs-comment">// 读取类上的注解</span>
    <span class="hljs-type">Table</span> <span class="hljs-variable">table</span> <span class="hljs-operator">=</span> (Table) userClass.getAnnotation(Table.class);
    System.out.println(table.value());

    <span class="hljs-comment">// 读取属性上注解</span>
    <span class="hljs-type">Field</span> <span class="hljs-variable">userId</span> <span class="hljs-operator">=</span> userClass.getDeclaredField(<span class="hljs-string">"userId"</span>);
    <span class="hljs-type">UserFiled</span> <span class="hljs-variable">userFiledId</span> <span class="hljs-operator">=</span> userId.getAnnotation(UserFiled.class);
    System.out.println(userFiledId.length() + <span class="hljs-string">"----"</span> + userFiledId.type() + <span class="hljs-string">"-----"</span> + userFiledId.name());

    <span class="hljs-type">Field</span> <span class="hljs-variable">userName</span> <span class="hljs-operator">=</span> userClass.getDeclaredField(<span class="hljs-string">"userName"</span>);
    <span class="hljs-type">UserFiled</span> <span class="hljs-variable">userFiledName</span> <span class="hljs-operator">=</span> userName.getAnnotation(UserFiled.class);
    System.out.println(userFiledName.length()+<span class="hljs-string">"----"</span>+userFiledName.type()+<span class="hljs-string">"----"</span>+userFiledName.name());

    <span class="hljs-type">Field</span> <span class="hljs-variable">password</span> <span class="hljs-operator">=</span> userClass.getDeclaredField(<span class="hljs-string">"password"</span>);
    <span class="hljs-type">UserFiled</span> <span class="hljs-variable">userFiledPassword</span> <span class="hljs-operator">=</span> password.getAnnotation(UserFiled.class);
    System.out.println(userFiledPassword.name() + <span class="hljs-string">"-----"</span> + userFiledPassword.type() + <span class="hljs-string">"-----"</span> + userFiledPassword.length());

    <span class="hljs-comment">// 拼接一个sql语句</span>
    <span class="hljs-type">String</span> <span class="hljs-variable">name</span> <span class="hljs-operator">=</span> <span class="hljs-string">"chenwei"</span>;
    <span class="hljs-type">String</span> <span class="hljs-variable">sql</span> <span class="hljs-operator">=</span><span class="hljs-string">"select * from"</span> + table.value()+<span class="hljs-string">"where"</span>+userFiledName.name()+<span class="hljs-string">"="</span>+name;
}

}

结果:

user_table
user_id----int-----8
user_name----varchar----16
password-----varchar-----16

自定义注解的实现过程:

1,定义注解

2,使用注解,根据自己定义的注解来达到一些目的,本例中,就是使用注解来完成数据库表和实体类的映射关系

3 读取注解的内容,也是比较重要的一部分,核心还是利用了反射的思想,得到使用注解的这个类,根据类中的 getAnnotion 的方法得到定义的注解,获得注解上的值。

3 注解的实现的原理

注解的实现的原理很大的一部分是基于反射实现。

​ 反射可以获取到 Class 对象,进而获取到 Constructor、Field、Method 等实例,点开源码结构发现 Class、Constructor、Field、Method 等均实现了AnnotatedElement接口,AnnotatedElement接口的方法如下

// 判断该元素是否包含指定注解,包含则返回 true
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)

// 返回该元素上对应的注解,如果没有返回 null
<T extends Annotation> T getAnnotation(Class<T> annotationClass);

// 返回该元素上的所有注解,如果没有任何注解则返回一个空数组
Annotation[] getAnnotations();

// 返回指定类型的注解,如果没有返回空数组
T[] getAnnotationsByType(Class<T> annotationClass)

// 返回指定类型的注解,如果没有返回空数组,只包含直接标注的注解,不包含 inherited 的注解
T getDeclaredAnnotation(Class<T> annotationClass)

// 返回指定类型的注解,如果没有返回空数组,只包含直接标注的注解,不包含 inherited 的注解
T[] getDeclaredAnnotationsByType

// 返回该元素上的所有注解,如果没有任何注解则返回一个空数组,只包含直接标注的注解,不包含 inherited 的注解
Annotation[] getDeclaredAnnotations();

通过一个实例再次说明一下注解的使用过程:

定义注解

@Documented
@Target({ElementType.TYPE, ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotaion {
    String getValue() default "this is myAnntaion";
    int order() default 0;
}

使用注解

@MyAnnotaion(getValue = "annotation on class")
public class Demo {
<span class="hljs-meta">@MyAnnotaion(getValue = "annotation on filed")</span>
<span class="hljs-keyword">public</span> String name;

<span class="hljs-meta">@MyAnnotaion(getValue = "annotation on method")</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">hello</span><span class="hljs-params">()</span>{
}

<span class="hljs-meta">@MyAnnotaion</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">defaultMethod</span><span class="hljs-params">()</span>{

}

}

利用反射读取注解中的值。

public class TestDemo {
    public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException {
        /**
         * 获取类上的注解
         */
        Class<Demo> demoClass = Demo.class;
        Annotation[] annotaion = demoClass.getAnnotations();
        printAnnotation(annotaion);
    <span class="hljs-comment">/**
     * 读取成员变量上的注解
     */</span>
    <span class="hljs-type">Field</span> <span class="hljs-variable">name</span> <span class="hljs-operator">=</span> demoClass.getField(<span class="hljs-string">"name"</span>);
    Annotation[] getOnFiled = name.getAnnotations();
    printAnnotation(getOnFiled);

    <span class="hljs-comment">/**
     * 读取方法上的注解
     */</span>
    <span class="hljs-type">Method</span> <span class="hljs-variable">hello</span> <span class="hljs-operator">=</span> demoClass.getMethod(<span class="hljs-string">"hello"</span>, <span class="hljs-literal">null</span>);
    <span class="hljs-type">MyAnnotaion</span> <span class="hljs-variable">onMethod</span> <span class="hljs-operator">=</span> hello.getAnnotation(MyAnnotaion.class);
    System.out.println(onMethod.getValue());

    <span class="hljs-comment">/**
     * 获取默认方法上的注解
     */</span>
    <span class="hljs-type">Method</span> <span class="hljs-variable">defaultMethod</span> <span class="hljs-operator">=</span> demoClass.getMethod(<span class="hljs-string">"defaultMethod"</span>, <span class="hljs-literal">null</span>);
    <span class="hljs-type">MyAnnotaion</span> <span class="hljs-variable">onDefaultMethod</span> <span class="hljs-operator">=</span> defaultMethod.getAnnotation(MyAnnotaion.class);
    System.out.println(onDefaultMethod.getValue());

}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">printAnnotation</span><span class="hljs-params">(Annotation... annotations)</span> {
    <span class="hljs-keyword">for</span> (Annotation annotation : annotations) {
        System.out.println(annotation);
    }
}

}

运行结果

@annocation.MyAnnotaion(getValue=annotation on class, order=0)
@annocation.MyAnnotaion(getValue=annotation on filed, order=0)
annotation on method
this is myAnntaion

参考资料:

http://blog.kimzing.com/java/Java 注解入门到精通 - 学这一篇就够了 /

《java 编程思想》