我对java的理解(一)——注解就是贴标签

  在现实生活中,贴标签这种现象比比皆是。去超市,去商场,每个或者每类物品都会有它的标签,甚至在我们自己身上也会有标签,比如,程序猿、逗逼、单身狗、80/90 后、屌丝……呵呵,太多了。有时候,我们也会戏谑朋友同事,给他们贴个标签逗逗乐。但是这篇的正题是注解,下面我会说说为什么我理解的注解就是贴标签,虽然有时候也会称它为“扣帽子”。

元注解

   在 java 提供了四个元注解,元注解就是注解的注解。它们是:

1.@Target,

2.@Retention,

3.@Documented,

4.@Inherited

  在我们刚出生的时候,就已经被贴上标签了,我们的户籍按照地域划分,会被贴上河南河北、山东山西、湖南湖北等等 23 个省和四个直辖市中的其中一个。另外在我们的生命过程中也会经历标签的变换,小孩、未成年人、成年人、丈夫、妻子、父亲、母亲、爷爷奶奶等等。国家还会给我们贴上一个最普通的标签“公民”以及我们的继承的身份,农民或者城里人。为什么要说这些呢,因为 JAVA 中提供的四个元注解要表达的东西和提到的有共通之处。

 

@Target

  @Target 说明了注解修饰的范围,就好像我们有地域划分,而 Java 中也会有包、接口、类、枚举、属性、方法、构造函数等。那么为了表示不同的区别,注解是这么写的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1、包
@Target(ElementType.PACKAGE)<br>
2、接口、类、枚举、注解
@Target(ElementType.TYPE)<br>
3、注解
@Target(ElementType.ANNOTATION_TYPE)<br>
4、构造函数
@Target(ElementType.CONSTRUCTOR)<br>
5、局部变量
@Target(ElementType.LOCAL_VARIABLE)<br>
6、方法
@Target(ElementType.METHOD)<br>
7、方法的参数
@Target(ElementType.PARAMETER)

 

@Retention

  @Retention 表示了注解的生命周期。我们在生命过程中会随着时间的变化而引起标签的变化,那么我们的标签本身也就说明了它有一定的生命周期,当我们未满 18 周岁的时候叫未成年人,在我们满 18 周岁的时候叫做成年人,等头发白了,走不动就被叫做老年人,而有的标签会伴随一生,例如男人或者女人。在 JAVA 中,注解也有它的生命周期,从源代码到编译后的 Class 文件,再到被 JDK 加载后的运行时就是整个周期。@Retention 指定了它所要注解的注解的生命周期是什么,它会存在多长时间。JAVA 中我们最常见的两个注解 @SuppressWarnings 和 @Override,他们就只会存在于源代码中。下面是 @Retention 的取值:

1
2
3
4
5
6
1、在源代码中保留
@Retention( RetentionPolicy.SOURCE)<br>
2、在Class文件中保留
@Retention( RetentionPolicy.CLASS)<br>
3、在运行时保留
@Retention( RetentionPolicy.RUNTIME)

  @SuppressWarnings 注解的源代码如下:

1
2
3
4
5
6
7
8
9
10
11
package java.lang;
 
import java.lang.annotation.*;
import java.lang.annotation.ElementType;
import static java.lang.annotation.ElementType.*;
 
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}

 

@Documented

  默认情况下 javadoc 是不包括注解信息的,但是当某一个注解被贴上 @Documented 标签的时候,那么当某个类或者方法的使用了被贴上 @Documented 这个标签的注解的时候,javadoc 就会把注解信息也包含在生成的文档中。这个注解就不过度解读了,如果有兴趣的可以从网上自行查找相关资料。

 

 

@Inherited

 

  @Inherited 注解主要说明了一种继承性,意思是子类可以继承父类中的该注解(注意:只有当被贴上 @Inherited 标签的注解被用在类上的时候子类才能获得这个注解)。就像之前提到的,如果父母是农村户口,那么他们的孩子也默认就是农村户口了。Spring 中的 @Qualifier 注解, 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package org.springframework.beans.factory.annotation;
 
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {
 
    String value() default "";
 
}

   @Qualifier 注解可以被用在字段、方法、方法参数、类、接口、注解、枚举上,但是只有 @Qualifier 被用在类上的时候才能被它的子类继承,子类才可以获取到这个注解。

自定义注解

  除了一些我们生来就被贴上的标签意外,我们在日常生活中不管是主动的还是被动都会去主动的给别人贴一些标签以及被别人贴一些标签。当我们在公司,按照职能划分,会被贴上不同的岗位标签,便于安排工作;迟到,早退,全勤这些标签用于考勤;优秀员工,技术达人,管理精英等标签用来树立标杆,有助于提高积极性和向心力。那么这些标签被创造出来有没有用处呢,对于公司来说自然是有用处的。但是呢,逗逼这个标签对公司来说有没有用处呢,我想,除非公司愿意因为员工逗逼颁发逗逼奖这才有点用处,否则的话谁关心呢!贴个标签大多数时候并不会影响正常的生活,除了个人或者组织关注了你身上的标签的时候才会有影响,就像现在很多人一看到河南人三个字就开骂了一样……。其实这也是为了说明,注解并不会影响程序的正常运行,有或者没有并不影响什么,只有关注了这些注解的程序获得并且解析了注解进行处理以后才会更改程序本身的行为。

  我们知道 JAVA 的 JDK 中很多地方都用了注解,到我们使用 Spring 的时候,注解就被使用的更广泛了,Spring 用注解来对 bean 进行分类,注册,注入以及管理,这些事情是 Spring 在扫描 Bean 的时候通过反射来获取 Bean 的注解信息,配合完成 Bean 的自动装配,进而进行管理,给我们提供了丰富多彩的功能。下面写一个利用反射来处理自定义注解的例子,这个例子就是简单生成一个建表语句,代码如下:

  1、创建 @Table 注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package person.lb.annotation;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
/**
 * 表名
 * @author nobounds
 *
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
    String value() default "";
}

   2、创建 @Column 注解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package person.lb.annotation;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
/**
 * 字段
 * @author nobounds
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
    String name() default "";
    String dataType() default "varchar(20)";
    String comment() default "";
}

   3、创建实体类 Users:

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
package person.lb.annotation;
 
@Table("users")
public class Users {
    @Column(name="ID", dataType="int")
    private int id;
     
    @Column(comment="用户名")
    private String userName;
     
    @Column(name="pwd", comment="密码")
    private String password;
     
    @Column(dataType="varchar(25)", comment="邮箱")
    private String email;
 
    public String getUserName() {
        return userName;
    }
 
    public void setUserName(String userName) {
        this.userName = userName;
    }
 
    public String getPassword() {
        return password;
    }
 
    public void setPassword(String password) {
        this.password = password;
    }
 
    public String getEmail() {
        return email;
    }
 
    public void setEmail(String email) {
        this.email = email;
    }
}

   4、创建注解处理器 AnnotationHandler:

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
57
58
package person.lb.annotation;
 
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
 
public class AnnotationHandler {
 
    public static void main(String[] args) {
        StringBuilder sql = new StringBuilder("CREATE TABLE ");
        try {
            Class clazz = Class.forName("person.lb.annotation.Users");
            //获取Users类上的Table注解
            Table tableAnnotation = (Table) clazz.getAnnotation(Table.class);
            //获取表名
            String tableName = tableAnnotation.value().toUpperCase();
            if("".equals(tableName)) {
                tableName = clazz.getName().toUpperCase();
            }
            sql.append(tableName);
            sql.append(" ( \n");
            //获取类中的所有字段
            Field[] fields = clazz.getDeclaredFields();
            for(Field field : fields) {
                //获取字段上的所有注解
                Annotation[] fieldAnnotations = field.getAnnotations();
                if(fieldAnnotations.length > 0) {
                    //遍历注解
                    for(Annotation fieldAnnotation : fieldAnnotations) {
                        //如果是@Field注解,那么进行处理
                        if(fieldAnnotation instanceof Column) {
                            //获取字段名
                            String columnName = ((Column) fieldAnnotation).name().toUpperCase();
                            if("".equals(columnName)) {
                                columnName = field.getName().toUpperCase();
                            }
                            //获取数据类型
                            String dataType = ((Column) fieldAnnotation).dataType().toUpperCase();
                            //获取注释
                            String comment = ((Column) fieldAnnotation).comment();
                            if("".equals(comment)) {
                                sql.append(columnName + "\t" + dataType + ",\n");
                            } else {
                                sql.append(columnName + "\t" + dataType + " COMMENT '" + comment + "',\n");
                            }
                        }
                    }
                }
            }
            sql.append(" ) ");
            System.out.println("生成的sql语句为:\n" + sql.toString());
             
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
         
    }
     
}

 执行结果如下:

生成的 sql 语句为:
CREATE TABLE USERS ( 
ID    INT,
USERNAME    VARCHAR(20) COMMENT '用户名',
PWD    VARCHAR(20) COMMENT '密码',
EMAIL    VARCHAR(25) COMMENT '邮箱',
 ) 

 

上面是一个简单的生成 sql 的例子,例子中忽略了某些非空判断,逻辑并不严谨,仅作为参考使用。如果大家觉得有问题请留言!