自定义注解
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

相关内容

热门资讯

linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
C++ 机房预约系统(六):学... 8、 学生模块 8.1 学生子菜单、登录和注销 实现步骤: 在Student.cpp的...
A.机器学习入门算法(三):基... 机器学习算法(三):K近邻(k-nearest neigh...
数字温湿度传感器DHT11模块... 模块实例https://blog.csdn.net/qq_38393591/article/deta...
有限元三角形单元的等效节点力 文章目录前言一、重新复习一下有限元三角形单元的理论1、三角形单元的形函数(Nÿ...
Redis 所有支持的数据结构... Redis 是一种开源的基于键值对存储的 NoSQL 数据库,支持多种数据结构。以下是...
win下pytorch安装—c... 安装目录一、cuda安装1.1、cuda版本选择1.2、下载安装二、cudnn安装三、pytorch...
MySQL基础-多表查询 文章目录MySQL基础-多表查询一、案例及引入1、基础概念2、笛卡尔积的理解二、多表查询的分类1、等...
keil调试专题篇 调试的前提是需要连接调试器比如STLINK。 然后点击菜单或者快捷图标均可进入调试模式。 如果前面...
MATLAB | 全网最详细网... 一篇超超超长,超超超全面网络图绘制教程,本篇基本能讲清楚所有绘制要点&#...
IHome主页 - 让你的浏览... 随着互联网的发展,人们越来越离不开浏览器了。每天上班、学习、娱乐,浏览器...
TCP 协议 一、TCP 协议概念 TCP即传输控制协议(Transmission Control ...
营业执照的经营范围有哪些 营业执照的经营范围有哪些 经营范围是指企业可以从事的生产经营与服务项目,是进行公司注册...
C++ 可变体(variant... 一、可变体(variant) 基础用法 Union的问题: 无法知道当前使用的类型是什...
血压计语音芯片,电子医疗设备声... 语音电子血压计是带有语音提示功能的电子血压计,测量前至测量结果全程语音播报࿰...
MySQL OCP888题解0... 文章目录1、原题1.1、英文原题1.2、答案2、题目解析2.1、题干解析2.2、选项解析3、知识点3...
【2023-Pytorch-检... (肆十二想说的一些话)Yolo这个系列我们已经更新了大概一年的时间,现在基本的流程也走走通了,包含数...
实战项目:保险行业用户分类 这里写目录标题1、项目介绍1.1 行业背景1.2 数据介绍2、代码实现导入数据探索数据处理列标签名异...
记录--我在前端干工地(thr... 这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前段时间接触了Th...
43 openEuler搭建A... 文章目录43 openEuler搭建Apache服务器-配置文件说明和管理模块43.1 配置文件说明...