自定义注解
admin
2024-04-03 17:56:20
0次

注解也被称为元数据,它为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便的使用这些数据。

注解是受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

允许子类继承父类中的注解

编写注解

一个注解得以工作要有三个要素:

  1. 定义注解
  2. 使用注解
  3. 注解处理器

下面用一个对象/关系映射功能 ORM的简单例子来展现如何编写自定义注解:

  1. @DBTable 将此注解用于 JavaBean 上,以便生成一个数据库表
  2. @Constrains此注解用于字段上,描述该字段有一些约束,比如不能为空,要唯一等
  3. @SQLString 将此注解用于字段上,会生成一个 varchar 类型的列
  4. @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)@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);}
}

注解使用注意

  • 不同注解可以组合使用,但同类型的注解不能重复使用
  • 目前注解不支持继承
  • 注解中的属性不能为 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)@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

相关内容

热门资讯

AI做攻略、“双向奔赴”……国... 本文转自:人民网人民网记者 许维娜今年国庆假期,AI(人工智能)成了不少年轻人的“旅行搭子”。行前做...
合肥的这个“梗”, 翻篇了 社交平台的评论区,曾长期流传一句专属于合肥的黑色幽默。有人问合肥哪里好玩,本地网友半开玩笑地回复:合...
7万+旅游中高层都在的群,今天... 做旅游这行,资源卡位、渠道互通、项目落地——往往就差一个“对的机会”。我们的行业社群已聚集70,00...
一场帐篷音乐节,如何让井研从“... 帐篷音乐节现场封面新闻记者 周洪攀10月的研溪湿地,晚风里裹着柑橘的清甜与民谣的回响。夜幕降临,10...
白鹤翩翩落星愿,上海迪士尼度假... 10月9日,世界候鸟日前夕,上海迪士尼度假区星愿公园的湖风里多了一分白鹤的灵动。三座白鹤雕塑正式揭幕...
成都锦江边赏初秋枫叶飘飘 目前,成都锦江边国华街枫叶树红了。近年来,国华街附近的几条街都栽种了红枫树,是城区较集中看枫叶的点位...
GBTA:商务旅行正在复苏,但... 亚太正在成为全球商务旅行增长的重要引擎。在经历疫情后的快速反弹之后,全球商务旅行正在进入一个新的增长...
从景区焕新到节赛引流 邹城双节... 齐鲁网·闪电新闻10月9日讯 中秋、国庆“双节”假期,邹城市文化和旅游局提前谋划、周密部署,立足自身...
江山如此多娇 且看石楼今朝 金秋十月,黄河岸畔秋意盎然,丰收盛会喜迎国庆。本次石楼国庆文旅活动以“江山如此多娇,且看石楼今朝”为...
海南国庆假期接待游客475.4... 2026年国庆假期,海南接待游客475.40万人次,较2025年增长13.1%,较2024年增长15...