Open dccmmtop opened 1 year ago
注解也被称为元数据,它为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便的使用这些数据。
注解是受c#启发,在 javaSE5 中引入的,虽然javaSE5 预先定义了一些注解,但一般来说,主要还是程序员添加新的注解,并按自己的方式使用他们。
java SE5 内置了三种注解:
之外,java还提供了四种注解,专门负责新注解的创建,也被称为元注解。
用来定义你的注解将用于什么地方,例如用在方法上,类上 还是字段上等, 使用方式: @Target(ElementType.METHOD), 其中 ElementType 的取值有下面几种:
@Target(ElementType.METHOD)
CONSTRUCTOR
FIELD
LOCAL_VARIABLE
METHOD
PACKAGE
PAEAMETER
TYPE
表示在什么级别保存该注解的信息,例如源码中,类文件中,或者运行时,使用方式: @Retention(RetentionPolicy.RUNTIME), 其中 RetentionPolicy 的取值有:
@Retention(RetentionPolicy.RUNTIME)
SOURCE
CLASS
RUNTIME
将此注解包含在 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) @SQLInt private int id; @SQLInt private 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<String> columnNameList = new ArrayList<String>(); // 反射的方式遍历类中的字段 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) @SQLInt private int id; @SQLInt private 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<String> columnNameList = new ArrayList<String>(); // 反射的方式遍历类中的字段 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
注解也被称为元数据,它为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便的使用这些数据。
注解是受c#启发,在 javaSE5 中引入的,虽然javaSE5 预先定义了一些注解,但一般来说,主要还是程序员添加新的注解,并按自己的方式使用他们。
内置注解
java SE5 内置了三种注解:
之外,java还提供了四种注解,专门负责新注解的创建,也被称为元注解。
元注解
@Target
用来定义你的注解将用于什么地方,例如用在方法上,类上 还是字段上等, 使用方式:
@Target(ElementType.METHOD)
, 其中 ElementType 的取值有下面几种:CONSTRUCTOR
构造器FIELD
域, 包括 enum 的实例LOCAL_VARIABLE
局部变量METHOD
方法上PACKAGE
包PAEAMETER
参数TYPE
类, 接口,包括注解类型 或 enum@Retention
表示在什么级别保存该注解的信息,例如源码中,类文件中,或者运行时,使用方式:
@Retention(RetentionPolicy.RUNTIME)
, 其中 RetentionPolicy 的取值有:SOURCE
注解将被编译器丢弃CLASS
注解在class文件中可用,但会被 JVM 丢弃RUNTIME
JVM 在运行期也保留注解,因此可以通过反射机制读取注解信息@Documented
将此注解包含在 javadoc中
@Inherited
允许子类继承父类中的注解
编写注解
一个注解得以工作要有三个要素:
下面用一个对象/关系映射功能 ORM的简单例子来展现如何编写自定义注解:
定义注解
注解的使用
注解处理器
注解使用注意
完整代码
为了测试方便,把所有类都放在一个文件中了:
结果: