Java 中提供了注解的功能。涉及到的包有:java.lang.annotation
,java.lang.reflect
。
也就是说,实际上包含两点:注解和反射。
因为仅仅自定义注解的话,几乎没有任何作用,除非是基于第三方框架。而不依赖任何第三方框架的话,就必须使用到反射来解析自定义的注解。(似乎也有其他的方式来解析)
这里就使用反射的方式来解析。
关于自定义注解本身,其实内容很少。
比如,下面有自定义一个注解类:
@Target(value = {ElementType.TYPE, ElementType.METHOD, ElementType.LOCAL_VARIABLE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Type {
String params() default "intelliJ";
int pos();
char[] value();
}
简单说明一下:
@Target
指定该注解使用的位置。比如上面的注解表示,该注解可以用于类,方法,局部变量。@Retention
指定该注解的生命周期。比如上面的注解表示,该注解在运行时依然存在。*特别注意,如果自定义注解需要被反射解析,必须要指定生命周期为RetentionPolicy.RUNTIME
。parmas , pos , value
这些属性必须被指定,在使用注解的时候。除非定义的时候,对应的属性后面跟上了default xxx
。然后看一下对应的注解使用类:
@Type(params = "idea", pos = 1, value = {'a', 'b', 'm'})
public class TypeObject {
@Type(pos = 3, value = 'd')
private int add(int a, int b) {
@Type(params = "potato", value = 'f', pos = 12)
int c = a + b;
return c;
}
}
使用注解比较简单,这里给出使用的案例,只是对照一下上面的说明。
以上,自定义注解的内容其实就差不多结束了。
但是,到此为止,并没有发现自定义注解有什么意义。
而且,到目前为止,确实没有任何意义。
以上注解,到目前为止确实没有对TypeObject
产生任何影响。
如果只是像上面这样,自定义了注解,并使用了注解。并没有什么实际意义。而自定义的解析,就可以解析被注解的类的注解内容。这样就能产生相关效果。
下面来弄一个简单的orm
小工具 ,用到以上的自定义注解,以及自定义注解的解析。
大致实现效果,就是有一个POJO
类,然后给它自定义一些注解,最后解析这些注解,生成相应的 sql
语句。(实际的orm
框架,肯定还会去执行这些sql
语句,但是这里只是展示一些自定义注解的解析。就不去执行了)
POJO
类 --> User.java
@BindTable(value = "tb_user")
public class User {
public static final int GENDER_SECRET = 0;
public static final int GENDER_MAIL = 1;
public static final int GENDER_FEMAIL = 2;
@BindField(type = "varchar(50)", notNull = true, columnName = "_name")
private String name;
@BindField(type = "int(3)", columnName = "_age")
private int age;
@BindField(type = "int(1)", notNull = true, columnName = "_gender")
private int gender;
@BindField(type = "int(1)", primaryKey = true, columnName = "_uid")
private long uid;
// get/set 去掉了 ...
BindTable.java , BindField.java
@Target(value = ElementType.TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface BindTable {
/**
* table name
*
* @return table name
*/
String value();
String charset() default "utf8";
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BindField {
// `_uid` int(32) NOT NULL AUTO_INCREMENT,
String columnName() default "";
/**
* 字段类型:varchar , int(25)
*
* @return field type
*/
String type();
boolean notNull() default false;
boolean primaryKey() default false;
boolean unique() default false;
boolean autoIncrement() default false;
}
TbUserParser.java
public class TbUserParser {
private TbUserParser() {
}
private static TableInfo parseClass() {
BindTable annotation = (BindTable) ((Class) User.class).getAnnotation(BindTable.class);
// System.out.println(annotation);
String charset = annotation.charset();
String tbName = annotation.value();
return new TableInfo(charset, tbName);
}
private static List parserField() {
Field[] fields = User.class.getDeclaredFields();
ArrayList infoSet = new ArrayList<>();
for (Field f : fields) {
// 没有添加该注解的字段就会返回 null
BindField bindF = f.getAnnotation(BindField.class);
// System.out.println("bindF==" + bindF);
if (Objects.nonNull(bindF)) {
String columnName = bindF.columnName();
String type = bindF.type();
boolean unique = bindF.unique();
boolean autoIncrement = bindF.autoIncrement();
boolean notNull = bindF.notNull();
boolean primaryKey = bindF.primaryKey();
FieldInfo info = new FieldInfo(columnName, type, notNull,
autoIncrement, unique, primaryKey);
infoSet.add(info);
}
}
return infoSet;
}
public static String createTable() {
TableInfo tableInfo = parseClass();
List fieldInfoSet = parserField();
String head = String.format(Locale.getDefault(),
"CREATE TABLE IF NOT EXISTS %s(", tableInfo.tbName);
StringBuilder item = new StringBuilder();
for (FieldInfo fi : fieldInfoSet) {
// `chat_id` INT UNSIGNED AUTO_INCREMENT,
item.append(String.format(Locale.getDefault(), "`%s` ", fi.columnName))
.append(String.format(Locale.getDefault(), "%s ", fi.type))
.append(fi.primaryKey ? "PRIMARY KEY " : "")
.append(fi.notNull ? "NOT NULL " : "")
.append(fi.unique ? "UNIQUE " : "")
.append(fi.autoIncrement ? "AUTO_INCREMENT," : ",");
}
if (item.charAt(item.length() - 1) == ',') {
item.deleteCharAt(item.length() - 1);
}
String tail = String.format(Locale.getDefault(), ") CHARSET=%s;", tableInfo.charset);
return head + item + tail;
}
static class TableInfo {
String charset;
String tbName;
TableInfo(String charset, String tbName) {
this.charset = charset;
this.tbName = tbName;
}
}
static class FieldInfo {
String columnName;
String type;
boolean notNull;
boolean autoIncrement;
boolean unique;
boolean primaryKey;
FieldInfo(String columnName, String type, boolean notNull,
boolean autoIncrement, boolean unique, boolean primaryKey) {
this.columnName = columnName;
this.type = type;
this.notNull = notNull;
this.autoIncrement = autoIncrement;
this.unique = unique;
this.primaryKey = primaryKey;
}
}
}
最后是调用:
public static void main(String[] args) {
String table = TbUserParser.createTable();
System.out.println("table==\n" + table);
}
输出如下:
table==
CREATE TABLE IF NOT EXISTS tb_user(`_name` varchar(50) NOT NULL ,`_age` int(3) ,`_gender` int(1) NOT NULL ,`_uid` int(1) PRIMARY KEY ) CHARSET=utf8;
这样,就完成了对自定义注解的解析。
简单说明一下自定义注解的解析,其实核心代码很少,就是对反射的使用。
// 对类上面注解的信息获取
BindTable annotation = (BindTable) ((Class) User.class).getAnnotation(BindTable.class);
// System.out.println(annotation);
String charset = annotation.charset();
String tbName = annotation.value();
// 对字段上面的注解信息获取:
Field[] fields = User.class.getDeclaredFields();
ArrayList infoSet = new ArrayList<>();
for (Field f : fields) {
// 没有添加该注解的字段就会返回 null
@Nullable
BindField bindF = f.getAnnotation(BindField.class);
// System.out.println("bindF==" + bindF);
if (Objects.nonNull(bindF)) {
String columnName = bindF.columnName();
String type = bindF.type();
boolean unique = bindF.unique();
boolean autoIncrement = bindF.autoIncrement();
boolean notNull = bindF.notNull();
boolean primaryKey = bindF.primaryKey();
FieldInfo info = new FieldInfo(columnName, type, notNull,
autoIncrement, unique, primaryKey);
}
}
对于方法上面的注解解析,这里没有做,应该也是类似的调用。
以上。戳我,给我 star
, 看运行效果 star
2333
特别感谢:Java课程 Java300集教程 SXT
ps 真不是帮打广告。我对自定义注解的理解,全是基于此讲解。