注解也被称为元数据,它为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便的使用这些数据。
注解是受c#启发,在 javaSE5 中引入的,虽然javaSE5 预先定义了一些注解,但一般来说,主要还是程序员添加新的注解,并按自己的方式使用他们。
java SE5 内置了三种注解:
之外,java还提供了四种注解,专门负责新注解的创建,也被称为元注解。
用来定义你的注解将用于什么地方,例如用在方法上,类上 还是字段上等, 使用方式: @Target(ElementType.METHOD), 其中 ElementType 的取值有下面几种:
CONSTRUCTOR 构造器FIELD 域, 包括 enum 的实例LOCAL_VARIABLE 局部变量METHOD 方法上PACKAGE 包PAEAMETER 参数TYPE 类, 接口,包括注解类型 或 enum表示在什么级别保存该注解的信息,例如源码中,类文件中,或者运行时,使用方式: @Retention(RetentionPolicy.RUNTIME), 其中 RetentionPolicy 的取值有:
SOURCE 注解将被编译器丢弃CLASS 注解在class文件中可用,但会被 JVM 丢弃RUNTIME JVM 在运行期也保留注解,因此可以通过反射机制读取注解信息将此注解包含在 javadoc中
允许子类继承父类中的注解
一个注解得以工作要有三个要素:
下面用一个对象/关系映射功能 ORM的简单例子来展现如何编写自定义注解:
/*** 注解定义*/@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBTable{// 表名, 规定:必须要有一个默认值,String name() default "";
}/*** 字段的约束*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Constraints{// 是否是主键boolean primary() default false;// 是否可以为空boolean allowNull() default true;// 是否唯一boolean unique() default false;
}/*** 生成 varchar 类型的列*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLString{/*** 让value代表 varchar的长度,value 是个比较特殊的名字,在使用该注解的时候,* 如果该元素是唯一需要赋值的元素。那么可以简写,无需使用 名———值 语法,而是* @SQLString(30)*/int value() default 0;// 列的名字String name() default "";
}/*** 生成 int 类型的列*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLInt{// 列的名字String name() default "";
}
/*** 注解使用*/
@DBTable(name = "users")// 表名是useres
public class User {/*** 设置id是主键,唯一, 不能为空*/@Constraints(primary = true, allowNull = false, unique = true)@SQLIntprivate int id;@SQLIntprivate int age;@SQLString(20)private String name;public int getId() {return id;}public void setId(int id) {this.id = id;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}
/*** 注解处理器*/
public class TableCreator{public String createTableSQLString(Class> cl) {// 获取类上的 DBTable 注解DBTable table = cl.getAnnotation(DBTable.class);if(table == null) {return "";}// 获取 DBTable 注解中的 name 参数String tableName = table.name();// 保存所有列名List columnNameList = new ArrayList();// 反射的方式遍历类中的字段for (Field field : cl.getDeclaredFields()) {// 列名String columnName = "";// 遍历字段上的注解for (Annotation annotation : field.getAnnotations()) {// 如果包含 SQLInt 注解if(annotation instanceof SQLInt) {SQLInt sint = (SQLInt) annotation;if(sint.name().length() == 0) {columnName = field.getName().toLowerCase();}else{// 如果没有设置name属性,就用字段名作为列名columnName = sint.name();}columnName += " INT";// 获取字段上的约束类型的注解Constraints con = field.getAnnotation(Constraints.class);columnName += buildConstraints(con);// 否则检查是否包含 @SQLString 注解}else if(annotation instanceof SQLString) {SQLString sString = (SQLString) annotation;if(sString.name().length() == 0) {columnName = field.getName().toLowerCase();}else{// 如果没有设置name属性,就用字段名作为列名columnName = sString.name();}// 获取字段长度属性columnName += " VARCHAR(" + sString.value() + ")";// 获取字段上的约束类型的注解Constraints con = field.getAnnotation(Constraints.class);columnName += buildConstraints(con);}}columnNameList.add(columnName);}StringBuilder createConmmand = new StringBuilder("");createConmmand.append("CREATE TABLE ").append(tableName).append("(");int length = columnNameList.size();for (int i = 0; i < length; i ++) {createConmmand.append("\n ").append(columnNameList.get(i));if(i != length -1){createConmmand.append(",");}}createConmmand.append(");");return createConmmand.toString();}public String buildConstraints(Constraints con){if(con == null) {return "";}if(!con.allowNull()){return " NOT NULL";}if(con.primary()){return " PRIMARY KEY";}if(con.unique()) {return " UNIQUE";}return "";}
}public class AnnotationDemo {public static void main(String[] args) throws ClassNotFoundException {Class> userClass = Class.forName("io.dc.User");String sql = new TableCreator().createTableSQLString(userClass);// 直接下面的也行
// String sql = new TableCreator().createTableSQLString(User.class);System.out.println(sql);}
}
为了测试方便,把所有类都放在一个文件中了:
package io.dc;import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;/*** 注解定义*/@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface DBTable{// 表名, 规定:必须要有一个默认值,String name() default "";
}/*** 字段的约束*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Constraints{// 是否是主键boolean primary() default false;// 是否可以为空boolean allowNull() default true;// 是否唯一boolean unique() default false;
}/*** 生成 varchar 类型的列*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface SQLString{/*** 让value代表 varchar的长度,value 是个比较特殊的名字,在使用该注解的时候,* 如果该元素是唯一需要赋值的元素。那么可以简写,无需使用 名———值 语法,而是* @SQLString(30)*/int value() default 0;// 列的名字String name() default "";
}/*** 生成 int 类型的列*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface SQLInt{// 列的名字String name() default "";
}/*** 注解使用*/
@DBTable(name = "users")// 表名是useres
class User {/*** 设置id是主键,唯一, 不能为空*/@Constraints(primary = true, allowNull = false, unique = true)@SQLIntprivate int id;@SQLIntprivate int age;@SQLString(20)private String name;public int getId() {return id;}public void setId(int id) {this.id = id;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}/*** 注解处理器*/
class TableCreator{public String createTableSQLString(Class> cl) {// 获取类上的 DBTable 注解DBTable table = cl.getAnnotation(DBTable.class);if(table == null) {return "";}// 获取 DBTable 注解中的 name 参数String tableName = table.name();// 保存所有列名List columnNameList = new ArrayList();// 反射的方式遍历类中的字段for (Field field : cl.getDeclaredFields()) {// 列名String columnName = "";// 遍历字段上的注解for (Annotation annotation : field.getAnnotations()) {// 如果包含 SQLInt 注解if(annotation instanceof SQLInt) {SQLInt sint = (SQLInt) annotation;if(sint.name().length() == 0) {columnName = field.getName().toLowerCase();}else{// 如果没有设置name属性,就用字段名作为列名columnName = sint.name();}columnName += " INT";// 获取字段上的约束类型的注解Constraints con = field.getAnnotation(Constraints.class);columnName += buildConstraints(con);// 否则检查是否包含 @SQLString 注解}else if(annotation instanceof SQLString) {SQLString sString = (SQLString) annotation;if(sString.name().length() == 0) {columnName = field.getName().toLowerCase();}else{// 如果没有设置name属性,就用字段名作为列名columnName = sString.name();}// 获取字段长度属性columnName += " VARCHAR(" + sString.value() + ")";// 获取字段上的约束类型的注解Constraints con = field.getAnnotation(Constraints.class);columnName += buildConstraints(con);}}columnNameList.add(columnName);}StringBuilder createConmmand = new StringBuilder("");createConmmand.append("CREATE TABLE ").append(tableName).append("(");int length = columnNameList.size();for (int i = 0; i < length; i ++) {createConmmand.append("\n ").append(columnNameList.get(i));if(i != length -1){createConmmand.append(",");}}createConmmand.append(");");return createConmmand.toString();}public String buildConstraints(Constraints con){if(con == null) {return "";}if(!con.allowNull()){return " NOT NULL";}if(con.primary()){return " PRIMARY KEY";}if(con.unique()) {return " UNIQUE";}return "";}
}public class AnnotationDemo {public static void main(String[] args) throws ClassNotFoundException {Class> userClass = Class.forName("io.dc.User");String sql = new TableCreator().createTableSQLString(userClass);// 直接下面的也行
// String sql = new TableCreator().createTableSQLString(User.class);System.out.println(sql);}
}
结果:
CREATE TABLE users(id INT NOT NULL,age INT,name VARCHAR(20));Process finished with exit code 0