annotation java 数组_自定义Java里的注解处理器(一)

声明注解与元注解

当我们需要自定义注解时,我们需要元注解(描述注解的注解)来协助,Java提供了四种基本的元注解,这四种注解分别为@Target, @Retention,@Documented,@Inherited。

1.@Target

表示注解应该应用到什么地方。07fdc857bfafc7e7a0a5f2e00942c280.png

而对于@Target本身的定义

@Documented

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.ANNOTATION_TYPE)

public @interface Target {

ElementType[] value();

}

它的作用范围就是注解上,而因为它是个元注解,所以可以用来表述自己。其实真正的写法是@Target(value = ElementType.METHOD。这里简写的原因是因为Target类中有一个value()属性,如果定义成别的变量,则必须要写全了。

2.@Retention

用来约束注解的生命周期,分别有三个值,源码级别(source),类文件级别(class),运行时级别(runtime),其含有如下:eadfb736143b8d5dea35ed8709c1dd15.png

其他两个注解,@Documented的注解会包含在JavaDoc中;@Inherited表明该注解允许子类继承父类中的注解。

自定义注解

事先说明一下,Java及第三方框架已经为我们提供了大量的注解,我们其实没必要自己定义注解使用,除非真的没有什么可以满足我们的注解,这时可以考虑自己定义。

例如:

@Retention(RUNTIME)//保留到运行时

@Target(TYPE)//作用在类上

public @interface DBTable {

String name() default "";

}

定义一个注解和定义一个interface非常相似,只不过多了一个@符号。在定义注解时,使用元注解,比如@Target、 @Retention来表明注解的应用范围,生命周期。

而定义注解时,可以看到定义体里面包含了一些比较特殊的方法。这些方法称为配置参数。只能是public或者default访问权限,方法的返回值就是配置参数的类型,并且我们可以通过default关键字指定默认值,但要注意不可为null,所以这里使用了””;

配置参数可以使用的类型如下:

所有的基本类型。

String

Class

enum

Annotation

以上类型的数组

定义注解时要注意:

1. 注解可以嵌套。

2.  注解不可继承,因为定义注解时,已经默认继承了java.lang.annotation.Annotation。

3.  配置参数必须要提供确定的值,要不使用时提供,要不定义时提供默认值,绝不能为null.

4. 注解中如果定义了名为value的配置参数(不论value为何类型),该参数是唯一需要赋值的一个参数,直接在括号内给出value的值就可以了。否则配置必须以key-value的形式提供。

注解与反射

Java里有一个AnnotatedElement接口,通过官方文档可以看到Class, Constructor, Executable, Field, Method, Package, Parameter都实现了这个接口,这就意味着我们可以通过反射获取在这上面应用的注解。但是因为使用的反射,所以每个注解的生命周期约束必须为Runtime。a3f96c9f7fbdd6f9ba327d135c508a62.png

一个经典的例子就是通过注解为一个Bean创建表。

例如:

@DBTable(name = "user")

public class User {

@SQLString(name = "user_id", length = 10,constraints = @Constraint(primaryKey = true))

private String id;

@SQLString(name = "user_name",length = 40)

private String name;

//省略

}

对于上面这个简单Java类,我们希望当扫描到它时,能直接生成一张表,其中DBTable来指定表的名称,SQLString指定列的名称,当我们没有显示提供配置参数的值时,它将使用类名作为表名、属性名作为列名。Constraint是一个列的约束,例如来说明是否为主键、是否允许为空等。

具体定义如下:

@Retention(RUNTIME)//保留到运行时

@Target(TYPE)//作用在类上

public @interface DBTable {

//指定数据表的名称

String name() default "";

}

@Retention(RUNTIME)

@Target(FIELD)

public @interface SQLString {

//指定列名称

String name() default "";

//指定长度,例如varchar(20)

int length() default 0;

//列约束

Constraint constraints() default @Constraint;

}

@Retention(RUNTIME)

@Target(FIELD)

public @interface Constraint {

// 是否作为主键约束

boolean primaryKey() default false;

// 是否允许为null

boolean allowNull() default false;

// 是否唯一

boolean unique() default false;

}

然后就可以通过反射编写一个处理器了。

public static String createSql(String className) throws Exception {

if (className == null || className.length() < 1) {

throw new NullPointerException("the className must be not null!");

}

//加载类

Class> cl = Class.forName(className);

DBTable dbTable = Objects.requireNonNull(cl.getAnnotation(DBTable.class));

String tableName = dbTable.name();

if (tableName.length() < 1) {// 判断配置参数是否为默认值,只需判断是否小于1即可,他不可能为null

tableName = cl.getName().toUpperCase();

}

List cols = new ArrayList<>();

for (Field field : cl.getDeclaredFields()) {

String colName = null;// 每次遍历需要重新设置colName的值

Annotation[] annos = field.getAnnotations();

if (annos.length < 1) {

continue; // 如果一个属性上没有注解,就继续遍历下一个

}

if (annos[0] instanceof SQLString) {// 每个属性上只用了一个注解,如果有多个需要在此循环遍历

SQLString sqlString = (SQLString) annos[0];

if (sqlString.name().length() < 1) { // 判断是否显示的提供了属性配置

colName = field.getName().toUpperCase();

} else {

colName = sqlString.name();

}

cols.add(colName + " varchar(" + sqlString.length() + ")" + getConstraints(sqlString.constraints()));

}

} // 遍历注解结束

StringBuilder sb = new StringBuilder();

sb.append("create table " + tableName + " (");

for (String col : cols) {

sb.append("\n" + col + ",");//注意这里的逗号,如果留空格,最后剪掉的时候要注意多裁剪一位

}

String tableCreate = sb.substring(0, sb.length() - 1) + ")";// 去掉最后一个逗号

return tableCreate;

}

//这个方法用来获取约束

private static String getConstraints(Constraint constraint) {

String strCon = "";// 默认约束为"",注意不是null!sql里约束没有null

if (!constraint.allowNull()) {

strCon += " not null ";// 注意前后要留有空格

}

if (constraint.primaryKey()) {

strCon += " primary key ";

}

if (constraint.unique()) {

strCon += " unique ";

}

return strCon;

}

注:如果你使用了一个可重复注解,即@Repeatable来定义你要处理的注解,那么请在处理器中使用getDeclaredAnnotationsByType() 和 getAnnotationsByType()这两个两个方法。

我们是通过运行时(Runtime)反射机制来处理注解,这就要求你必须使用Runtime,然而很多框架都可以在编译处理注解。这个过程也不算复杂,只需要我们自定义注解处理器( Processor)即可。下一篇我将介绍如何在编译期间处理注解,并将你定义的处理器注册到编译器中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值