背景
最近一次开发的时候,我们一般都会和数据库打交道,同时我们自己也会创建一些Entity
对象,比如User
,它里面有一些属性
@Entity(name = "t_user")
@Getter
@Setter
@NoArgsConstructor
public class User {
private String email;
private Long age;
}
当我在做一些CRUD
操作的时候,难免我会需要用到User
里的field
的名字,也就是字符串"email"
和"age"
,一般我们可能不会注意这种问题,可能直接就在需要用到的地方直接写上字符串"email"
但其实这么写某种意义上算是一种硬编码了,毕竟若是这个field
名字在代码中到处都有使用,可能我们要修改field
名字时,就需要改很多地方
于是我们可能顺其自然的,想到建立一个常量类,类似UserConstants
,来存放User的属性的名字的静态常量
public interface UserConstants {
String EMAIL = "email";
String AGE = "age";
}
这样我们貌似可以解决了,若是想要修改field
名字,我们只用改UserConstants
里的值即可
不过正因为有这样的想法,假如我们有20个Entity
,我们就要再写20个Contants
类,真的太麻烦了,而且之前在Entity
里写的属性名,就还要在Contants
里再写一遍。。。真的太麻烦了
于是我想,能不能当我创建了某个Entity
,它就可以自动生成对应的Contants
类呢
hibernate-jpamodelgen
别说,还真有,我发现了一个工具hibernate-jpamodelgen
,这看名字也是hibernate
出的,其实就是根据JPA
标准的@Entity
注解的类来自动生成了对应的元数据,有些叫元模型,注意哈,这里不是运行时,而是编译的时候,编译之后,就会生成新的类,下面是生成之后的类
@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(User.class)
public abstract class User_ {
public static volatile SingularAttribute<User, String> name;
public static volatile SingularAttribute<User, Long> age;
}
而根据此类的给的field
是SingularAttribute
类型,通过该类你可以获取这个field
的很多信息,不仅仅是我们之前想要的field
的名字,
看起来好像解决问题,但。。。。我用的不是关系型数据库啊,也就没用JPA
,那更谈不上hibernate
了,我用的mongodb
,但是我也有类似Entity
这样的数据结构,mongodb
是@Document
,类似这样
@Document
@Getter
@Setter
@NoArgsConstructor
public class User {
private String name;
private Long age;
}
或者更往开的想,能不能做一个类似只要我标注的类,就可以自动生成它对应的类里的field
的属性名常量类
由于hibernate-jpamodelgen
的实现思路跟我想要的效果一样,于是我去了解一下它的实现原理,就引申出Pluggable Annotation Processing
Pluggable Annotation Processing
Pluggable Annotation Processing
其实是已经推出很久的API
,从1.6开始的,不过知道的人不算特别多,但是很多人都是在用着该API
提供的便利。该API
只能是用于生成新文件,而不是用来更改现有文件
Pluggable Annotation Processing
,翻译过来可以叫可插拔(插件)化注解处理,是根据[JSR 269] 来做出来的
(JSR
其实就是Java Specification Requests的缩写,意思是Java 规范提案,如果把Java语言本身看成一个产品,那JSR
就是产品经理提的需求,而产品经理其实就是JCP
这个组织)
如果简单理解编译过程是把源文件.java
转换为.class
文件的话,那Pluggable Annotation Processing
就是允许你在这个过程中可以根据注解做一些额外的事,比如生成一些新的文件
这个用面向接口编程的思想很好理解,就是定义一个接口在编译过程中调用,这样有多少实现类,就可以用多少定制的实现
AutoConstants
了解了上面Pluggable Annotation Processing
的功能,那我们就可以按照它的API
来定制自己的实现了,由于Pluggable Annotation Processing
是注解处理,因此,我们定制其实就是要关注两个点,这两个关注点输入给Pluggable Annotation Processing API
,那应该就可以自动被执行了
- 你要处理或关注什么注解
- 你要怎么处理被该注解标注的目标
那按照之前我自己的需求,我希望有一个注解可以标注某个类,然后可以自动生成该类的field
名字的常量类
因此我们首先创建一个注解@AutoConstants
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface AutoConstants {
}
当然这样就解决了我们第一个要关注的点
接着我们做一个处理类AutoConstantsProcessor
,来处理被AutoConstants
标注的类,这里要介绍Pluggable Annotation Processing
的关键类AbstractProcessor
,这其实是一个抽象类,它的实现的接口Processor
才是上层抽象,不过AbstractProcessor
帮我们做了一些默认实现,让我们只关注处理的定制业务本身
public class AutoConstantsProcessor extends AbstractProcessor {
// 你要处理哪个注解
@Override
public Set<String> getSupportedAnnotationTypes() {
return ImmutableSet.of(AutoConstants.class.getName());
}
// 你处理支持的java版本
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
// 初始化参数
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
}
// 关键方法,你怎么处理,这里面你可以做你想要做的
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
}
}
当然你需要指定你是接口Processor
的实现类,当然不是语法层面的告诉,而是直接告诉编译器,你是Processor
的实现类,因此,这里要用到ServiceLoader API
的方式,在META-INF
下新建一个文件夹services
,并在services
下新建一个文件,以接口的全限定名为文件名,内容为实现类的全限定名
补充一点,其实ServiceLoader
这个大家平常也都很常用到,只是大家没有发觉而已,比如数据库驱动,JDBC
标准里定义的驱动其实是一个接口java.sql.Driver
,大家每次使用某个数据库,需要加一个驱动包,这个驱动包的也有个类似的配置(以Mysql
为例)
言归正传,这样的话,需要的工程直接简单依赖,然后加上@AutoConstants
注解即可
@Document
@Getter
@Setter
@NoArgsConstructor
@AutoConstants
public class User {
private String name;
private Long age;
}
编译后自动生成常量类User_
中间实现过程不再描述拉,感兴趣的可以自己尝试一下,我生成代码用到了JavaPoet
,多半需要熟悉哈Pluggable Annotation Processing API
,当然其实大家熟知的Lombok
也有用到Pluggable Annotation Processing API
,不过我们知道Lombok
其实是修改了原文件的,所以那里用到的API
不在Pluggable Annotation Processing
之内
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。