thinking in java 中对注解的优点的描述是这样的:
advantage of cleaner looking code, compile-time type checking and the annotation API to help build processing tools for your annotations.
简单来说使用注解可以
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()获得字段注解;
最后把生成数据库表的命令打印出来:
(如果使用Intellj Idea的话可以在Run->edit configurations里面修改Program Arguments作为main函数的输入参数)
注解是一中很有用的东西,他可以给我们的编程带来很多便利,在JDK8以前java还提供了APT作为注解解释工具,方便我们解析注解。在java8中,APT被移出了JDK,取而代之的是更优的JSR 269 API “Pluggable Annotation Processing API”.
完。