注解也被称为元数据,它为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便的使用这些数据。
注解是受c#启发,在 javaSE5 中引入的,虽然javaSE5 预先定义了一些注解,但一般来说,主要还是程序员添加新的注解,并按自己的方式使用他们。
内置注解
java SE5 内置了三种注解:
- @Override, 表示当前的方法覆盖超类中的方法。如果拼写错误,或者方法签名对不上被覆盖的方法,编译器就会发出错误提示,
- @Deprecated, 标记方法被弃用了,如果使用了被它标记的元素,编译器会发出警告信息
- @SuppressWarnings, 关闭不当的编译器警告信息。
之外,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的简单例子来展现如何编写自定义注解:
- @DBTable 将此注解用于 JavaBean 上,以便生成一个数据库表
- @Constrains此注解用于字段上,描述该字段有一些约束,比如不能为空,要唯一等
- @SQLString 将此注解用于字段上,会生成一个 varchar 类型的列
- @SQLInt 将此注解用于字段上,会生成一个 int 类型的列
定义注解
/**
* 注解定义
*/
@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);
}
}
注解使用注意
- 不同注解可以组合使用,但同类型的注解不能重复使用
- 目前注解不支持继承
- 注解中的属性不能为 null, 本例中的 @SQLString name() 不能为 null, 必须设置 default
- 注解元素只能一下几种,否则编译器会报错
- 所有基本类型(int, float, boolean 等)
- String
- Class
- enum
- Annotation
- 以上类型的数组
完整代码
为了测试方便,把所有类都放在一个文件中了:
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