Introduction to Java Annotations

foreword

Annotations are a very welcome addition introduced by java, providing a structured and type-checked new way to allow programmers to add metadata to their code without cluttering the code and making it difficult to read. Using annotations can help us avoid writing cumbersome deployment descriptors and other generated files.

The syntax of annotations is relatively simple. Except for the use of the @ symbol, it is basically the same as the inherent syntax of java. However, since there are few built-in annotations provided in the java source code, most students do not know much about annotations, although we have all come into contact with them, such as several built-in annotations in java:

    @Override,表示当前的方法定义将覆盖超类中的方法。
    @Deprecated,表示当前方法即将废弃,不推荐使用。
    @SuppressWarnings,表示忽略编译器的警告信息。
  • 1
  • 2
  • 3
  • 4

But this does not allow us to appreciate the power and convenience of annotations. In fact, Java also provides four additional annotations, which are responsible for the creation of new annotations. Today we will learn how to create and use annotations.

Definition of Annotated Classes

First look at the definition of an annotation class:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Constraints {
    boolean primaryKey() default false;
    boolean allowNull() default true;
    boolean unique() default false;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Aside from the @ symbol, an annotated class is defined much like an empty interface. When defining annotations, some meta-annotations are required , such as @Target and @Retention. Java provides four meta-annotations, which are defined as follows:

@Target: Indicates where the annotation can be used. The values ​​(ElementType) include: CONSTRUCTOR: used to describe the constructor FIELD: used to describe the field LOCAL_VARIABLE: used to describe the local variable METHOD: used to describe the method PACKAGE: used to describe the package PARAMETER: used to describe the parameter TYPE: used to describe class, interface (including annotation types) or enum declaration 
 
 
 
 
 
 
 

@Retention: Indicates at what level the annotation information needs to be saved. The values ​​(RetentionPolicy) include: SOURCE: valid in the source file (that is, the source file is retained) CLASS: valid in the class file (that is, the class is retained) RUNTIME: valid at runtime (that is, the runtime is retained), so it can pass the reflection mechanism Read the annotation information. 
 
 
 

@Documented: Indicates that this annotation is included in the javadoc.

@Inherited: Indicates that subclasses are allowed to inherit the annotations in the superclass.

It can be seen that the definition annotation format is:   public @interface annotation name {definition body} 

The elements defined in the annotation class are called annotation elements , and the types of annotation elements available are as follows:

  所有基本数据类型(int,float,boolean,byte,double,char,long,short)
  String类型
  Class类型
  enum类型
  Annotation类型
  以上所有类型的数组
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

If you use other types, the compiler will complain. Note that no wrapping types are allowed either, but due to automatic wrapping, this is less of a restriction. Annotations can also be used as element types. For example, we define another annotation class:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLString {
    int value() default 0;
    String name() default "";
    Constraints constraints() default @Constraints;
    //@Constraints后没有括号表明使用默认值,可以加括号,在里面定义初始值,比如@Constraints(unique=true)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

As you can see, all annotation elements have a default value. The compiler is a bit picky about the default value of an element. First, an element must have a default value; second, it cannot use null as a default value. So we can only define some special values ​​ourselves, such as empty strings or negative numbers, to indicate that an element does not exist.

The use of annotation classes

The annotation class is defined, how to use it? In order to reflect the convenience and power of annotations, here we write a demo that uses annotations to automatically generate a SQL command to create a database table. In addition, we provide two more annotation classes: 
 

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBTable {
    public String name() default "";
}
  • 1
  • 2
  • 3
  • 4
  • 5
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLInteger {
    String name() default "";
    Constraints constraints() default @Constraints;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

好,现在我们一共有4个注解类。 
@DBTable 代表数据库表,注解元素name表示表名; 
@Constraints 代表对数据表每一列的条件补充,有primaryKey是不是主键,默认false,allowNull是否允许为空,默认true,unique数据是否唯一,默认false; 
@SQLString 代表表中的String列,注解元素value表示列长度,name表示列名,constraints表示对列的一些约束条件; 
@SQLInteger 代表表中的int列,name表示列名,constraints表示对列的一些约束条件; 
下面我们定义一个简单的Bean类,在其中应用以上4个注解类:

@DBTable(name = "MEMBER")
public class Member {
    @SQLString(30) String firstName;
    @SQLString(50) String lastName;
    @SQLInteger int age;
    @SQLString(value = 30, constraints = @Constraints(primaryKey = true)) String handle;
    public String getFirstName() {
        return firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public int getAge() {
        return age;
    }
    public String getHandle() {
        return handle;
    }
    public String toString() {
        return handle;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

注解的元素在使用时表现为名-值对的形式,并需要置于 @注解类名 声明之后的括号内。如果没有使用括号,代表全部使用默认值。

1、类的注解@DBTable给定了值MEMBER,它将会用来作为表的名字; 
2、Bean的属性firstName和lastName,都被注解为@SQLString类型,这些注解有两个有趣的地方:第一,他们都使用了嵌入的@Constraints注解的默认值;第二,它们都使用了快捷方式。何谓快捷方式?如果程序员的注解中定义了名为value的元素,并且在应用该注解的时候,如果该元素是唯一需要赋值的一个元素,那么此时无需使用名-值对的这种语法,而只需在括号内给出value元素所需的值即可。这可以应用于任何合法类型的元素。当然了,这也限制了程序员必须将此元素命名为value。 
Bean属性age全部使用默认值,handle为主键。 
3、Bean属性age全部使用默认值,handle为主键。

实现注解处理器

注解类使用上了,我们还需要一个注解处理器来解析我们定义的Bean,这样才能将注解转换成我们需要的操作。 
以下定义解析Bean的注解处理器,我们的目的是要输出一条SQL语句。主要用到了反射技术。

public class TableCreator {
    public static void main(String[] args) throws Exception{
//传入我们定义好的Bean类名,带上包名       
Class<?> cl = Class.forName("annotation.Member");
//检查我们传入的类是否带有@DBTable注解
        DBTable dbTable = cl.getAnnotation(DBTable.class);
        List<String> columnDefs = new ArrayList<String>();
//得到类中的所有定义的属性
        for(Field filed : cl.getDeclaredFields()){
            String columnName = null;
//得到属性的注解,对一个目标可以使用多个注解
            Annotation[] anns = filed.getAnnotations();
            if(anns.length < 1){
                continue;
            }
//SQLString注解走着
            if(anns[0] instanceof SQLString){
                SQLString sString = (SQLString)anns[0];
//name()使用的是默认值,所以这里取属性名
                if(sString.name().length() < 1){
                    columnName = filed.getName().toUpperCase();
                }else{
                    columnName = sString.name();
                }
//构建SQL语句
                columnDefs.add(columnName + " VARCHAR(" + sString.value() + ")" + getConstraints(sString.constraints()));
            }
//SQLInteger注解走着
            if(anns[0] instanceof SQLInteger){
                SQLInteger sInt = (SQLInteger)anns[0];
                if(sInt.name().length() < 1){
                    columnName = filed.getName().toUpperCase();
                }else{
                    columnName = sInt.name();
                }
                columnDefs.add(columnName + " INT" + getConstraints(sInt.constraints()));
            }
        }
        StringBuilder creator = new StringBuilder("CREATE TABLE " + dbTable.name() + "( ");
        for(String c : columnDefs){
            creator.append("\n" + c + ",");
        }
        creator.deleteCharAt(creator.length()-1);
        creator.append(")");
        System.out.println(creator.toString());
    }

    private static String getConstraints(Constraints con) {
        String constraints = "";
        if(!con.allowNull()){
            constraints += " NOT NULL";
        }
        if(con.primaryKey()){
            constraints += " PRIMARY KEY";
        }
        if(con.unique()){
            constraints += " UNIQUE";
        }
        return constraints;
    }
}
  • 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
  • 59
  • 60
  • 61

最后的输出: 
CREATE TABLE MEMBER( 
FIRSTNAME VARCHAR(30), 
LASTNAME VARCHAR(50), 
AGE INT, 
HANDLE VARCHAR(30) PRIMARY KEY)

总结

最后,通过这篇文章,我们要学到一下几点: 
1、了解java 4种元注解的说明与使用; 
2、会自定义注解类; 
3、了解注解元素的定义和规则; 
4、会使用注解类; 
5、能写一个简单的注解处理器解析注解。

———————————————————

 

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326570602&siteId=291194637