注解的定义和应用

注解的优点

thinking in java 中对注解的优点的描述是这样的:

advantage of cleaner looking code, compile-time type checking and the annotation API to help build processing tools for your annotations.

简单来说使用注解可以

  1. 使代码更简洁
  2. 编译时检查代码
  3. 存储额外的信息

java里面定义的注解可以分两类,基本类型的注解和元注解。
基本类型的注解包括:

@Override(编译器会有错误提示如果覆盖出现了错误)
@Deprecated(如果注解为@Deprecated的元素被使用,编译器会提示)
@SuppressWarnings(关闭不当的编译器警告信息)

元注解包括:
@Target
@Retention
@Doucumented
@Inherited

可以利用元注解来自定义自己的注解,这个特性非常有用,很多mvc框架和O/RM框架都利用这个特性来为开发提供方便。

应用实例

如果我们想要通过使用注解而不是xml的方式来为javabean生成数据库表,我们可以运用注解来达到这样的目标。

假设我们要为产品表生成一张product表,可以在实体类Product中添加如下注解:

@DBTable(name = "product")
public class Product {

    @SQLInteger(name = "id")
    @Constraints(primaryKey = true,allowNull = false)
    private int id;

    @SQLString(name = "name",value = 255)
    @Constraints(allowNull = false,unique = false)
    private String name;

}

其中@DBTable注解的定义如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBTable {
    String name() default " ";
}

@Target的参数ElementType.TYPE表示这个注解作用在类上,Target还可以用其他参数如ElementType.FIELD, ElementType.METHOD分别表示该注解作用在字段上和方法上。
@Retention的参数为RetentionPolicy.RUNTIME表示该注解运行时起作用。
String name()表示表的名字。
默认值:编译器对值的要求很挑剔,他要求值必须是确定的,不管是你指定的还是默认的,反正必须是确定的。而且非基本类型的默认值不能是null,因此我们可以指定name的默认值为空串来表示不存在。

定义完了@DBTable我们可以来定义@constraints注解,这个注解表示产品表中各字段的约束,比如我们定义product表中的id字段,这个字段是主键,而且不能为空,是唯一的。那这个约束怎么使用注解表示出来呢?我们只要使用定义@constraints注解,然后在id上使用就可以了。@constraints注解的定义如下:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Constraints {
    boolean primaryKey() default false;
    boolean allowNull() default true;
    boolean unique() default true;
}

接下来还要定义@SQLInteger和@SQLString注解:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLInteger {
    String name() default "";
    Constraints constrants() default @Constraints;
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLString {
    int value() default 0;
    String name() default "";
    Constraints constrants() default @Constraints;
}

定义完了这些注解了。可是java怎么读这些注解来为我们生成一张product的表呢?显然我们需要一个来读Product这个类的解释器。
这个解释器可以这样实现:

public class SQLCreator {

    public static void main(String[] args) throws ClassNotFoundException {
        //把要解析的类名当作参数传进来
        if(args.length<1){
            System.out.println("please specify the table name");
            System.exit(0);
        }
        //表名
        String tableName=null;

        //为传入的每个类创建SQL语句
        for(String className:args){
            //获得表名
            Class cl = Class.forName(className);
            DBTable dbTable = cl.getAnnotation(DBTable.class);
            if(dbTable!=null){
                tableName = dbTable.name();
            }
            //获得表中每一列的定义
            List columnDef = new ArrayList<>();
            for (Field field:cl.getDeclaredFields()) {
                //列名
                String columnName;
                //列约束
                String cols;
                Annotation []anns = field.getDeclaredAnnotations();
                if(anns[0]instanceof SQLString){
                    SQLString sqlS = (SQLString)anns[0];
                    //获得列名
                    columnName = sqlS.name().toUpperCase();
                    //获得列约束
                    cols = getConstrants((Constraints)anns[1]);
                    columnDef.add(columnName+" VARCHAR("+sqlS.value()+")"+cols);
                }else if(anns[0] instanceof SQLInteger){
                    SQLInteger sqlI = (SQLInteger)anns[0];
                    columnName = sqlI.name().toUpperCase();
                    cols = getConstrants((Constraints)anns[1]);
                    columnDef.add(columnName+" INT "+cols);
                }


            }
            StringBuilder createSQL = new StringBuilder("CREATE TABLE "+tableName+" (\n");
            for(String s:columnDef){
                createSQL.append(s);
            }
            String comd = createSQL.substring(0,createSQL.length()-1) +");";
            System.out.println(comd);
        }

    }
    //返回“约束”的定义
    private static String getConstrants(Constraints constraints){
        String s = "";
        if(!constraints.allowNull())
            s += " NOT NULL ";
        if(constraints.primaryKey())
            s += " PRIMARYKEY ";
        if(constraints.unique())
            s += " UNIQUE ";
        return s+"\n";
    }
}

将类名传进来,运用反射,通过getAnnotation()方法来获取类的注解;通过getDelaredFields()获得field,再通过field.getDeclaredAnnotations()获得字段注解;
最后把生成数据库表的命令打印出来:

注解的定义和应用_第1张图片

(如果使用Intellj Idea的话可以在Run->edit configurations里面修改Program Arguments作为main函数的输入参数)

总结

注解是一中很有用的东西,他可以给我们的编程带来很多便利,在JDK8以前java还提供了APT作为注解解释工具,方便我们解析注解。在java8中,APT被移出了JDK,取而代之的是更优的JSR 269 API “Pluggable Annotation Processing API”.

完。

你可能感兴趣的:(javase)