javac AbstractProcessor
说明
Annotation Processor是javac的一个工具,它用来在编译时扫描和处理注解,通过Annotation Processor可以获取到注解和被注解类的相关信息,然后根据注解自动生成Java代码,省去了手动编写,提高了编码效率。
它可以做什么
在编译的时候动态生成类或者改变类的代码!如:
lomock:减少get 和set方法的模板代码生成
mapstruct: 动态生成po vo 互转的Convert类
hibernate-jpamodelge 动态生成PO类的元数据映射,减少操作字段的模板代码
编译时代理
需求
公司内部自己实现了一套基于redis 的CRUD的ORM框架
//保存活动信息 BookingActivitySettingRo bookingActivitySettingRo=new BookingActivitySettingRo(); bookingActivitySettingRo.setId(1L); bookingActivitySettingRo.setActivityName("哈哈哈"); bookingActivitySettingRedisDao.save(bookingActivitySettingRo); //查询 bookingActivitySettingRedisDao.findOne(1L); //批量查询 bookingActivitySettingRedisDao.findByIds(Arrays.asList(1l,2l)); //删除 bookingActivitySettingRedisDao.delete(bookingActivitySettingRo.getId()); //编辑 BookingActivitySettingRo settingRedisDaoOne= bookingActivitySettingRedisDao.findOne(1L); settingRedisDaoOne.setActivityName("我是修改名字"); bookingActivitySettingRedisDao.save(settingRedisDaoOne);
为了解决以下问题
针对并发场景只操作RO部分字段,优化以下场景的写法导致魔法值过多不易维护问题
1.并发编辑操作,只操作指定字段,避免整个ro回填,覆盖了其他线程修改的值
2.并发查询操作,追求性能,RO字段过多,只查询关心部分字段 hmget
基础工程redis版本1.3.0-SNAPSHO,增加了编译时自动生成映射类
自定义Processor
参考了hibernate-jpamodelge的实现
1.实现自定义Processor继承AbstractProcessor重写process方法
/** * @Project 商品uaa * @PackageName cn.wine.ms.common.gennerator * @ClassName RedisRoAbstractProcessor * @Author qiang.li * @Date 2020/12/28 1:13 下午 * @Description 用于编译时针对打了RO注解的类 生成key的映射,优化hset等redis操作部分字段写魔法值的问题 */ @SupportedAnnotationTypes({"cn.wine.base.redis.annotation.Ro"})//你的注解的全名称 @SupportedSourceVersion(SourceVersion.RELEASE_8)//jdk环境为java8 public class RedisRoProcessor extends AbstractProcessor { /** * {@inheritDoc} * @param annotations * @param roundEnvironment */ @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) { //遍历所有打了注解的类 if (!roundEnvironment.processingOver() && annotations.size() != 0) { Set<? extends Element> elements = roundEnvironment.getRootElements(); Iterator var4 = elements.iterator(); while (var4.hasNext()) { Element element = (Element) var4.next(); //只处理打了RO注解的类 if (this.isRoEntity(element)) { try { createClass(element); }catch (Exception e){ processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,e.getMessage()); } } } } return true; } /** * 创建class文件 * @param element * @throws IOException */ public void createClass(Element element) throws IOException { //获得full类名 String className=(((TypeElement)element).getQualifiedName()).toString(); //获得包名 String metaModelPackage=className.substring(0,className.lastIndexOf(".")); //获得属性元数据 List<? extends Element> fieldsOfClass = ElementFilter.fieldsIn(element.getEnclosedElements()); //生成classBody String classBody=generateClassBody(fieldsOfClass); //用户截取原始类名的startIndex Integer index=className.lastIndexOf(".")+1; //获得class名字 String simpleClassName=className.substring(index,className.length()); //新类的className String newClassName=String.format("%s_",simpleClassName); //根据名字创建class文件 createFile(newClassName,metaModelPackage,classBody); } public void createFile(String genarateClassName,String metaModelPackage,String body) throws IOException { //生成包名 String generatePackageName = metaModelPackage; //创建Java 文件 拼上类的全名称会生成文件夹 JavaFileObject f =processingEnv.getFiler().createSourceFile(metaModelPackage+"."+genarateClassName); try(Writer w = f.openWriter();){ PrintWriter pw = new PrintWriter(w); pw.println("package " + generatePackageName + ";"); pw.println("\npublic class " + genarateClassName + " { "); pw.println(body); pw.println(" }"); pw.flush(); } } /** * 构建class内容 * @param fieldsOfClass * @return */ public String generateClassBody(List<? extends Element> fieldsOfClass){ StringBuilder body=new StringBuilder(); for(Element element:fieldsOfClass){ body.append(String.format(" public final static String %s=\"%s\";",element.getSimpleName(),element.getSimpleName())); body.append("\n\n"); } return body.toString(); } /** * 是否是打了RO注解的Entity * @param element * @return */ private boolean isRoEntity(Element element) { return containsAnnotation(element, new String[]{"cn.wine.base.redis.annotation.Ro"}); } /** * 是否有打了指定注解 * @param element * @return */ public static boolean containsAnnotation(Element element, String... annotations) { assert element != null; assert annotations != null; List<String> annotationClassNames = new ArrayList(); Collections.addAll(annotationClassNames, annotations); List<? extends AnnotationMirror> annotationMirrors = element.getAnnotationMirrors(); Iterator var4 = annotationMirrors.iterator(); AnnotationMirror mirror; do { if (!var4.hasNext()) { return false; } mirror = (AnnotationMirror)var4.next(); } while(!annotationClassNames.contains(mirror.getAnnotationType().toString())); return true; } }
2.在resource下新增META-INF/services并创建文件javax.annotation.processing.Processor
将自定义的processor的全名称配置进去
#注:如果搭配jpa和mapstract或者lomack的processr冲突 通过以下类似配置解决 # <plugin> # <groupId>org.apache.maven.plugins</groupId> # <artifactId>maven-compiler-plugin</artifactId> # <version>3.7.0</version> # <configuration> # <source>${java.version}</source> # <target>${java.version}</target> # <annotationProcessorPaths> # <path> # <groupId>org.projectlombok</groupId> # <artifactId>lombok</artifactId> # <version>${lombok.version}</version> # </path> # <path> # <groupId>org.mapstruct</groupId> # <artifactId>mapstruct-processor</artifactId> # <version>1.2.0.Final</version> # </path> # <path> # <groupId>org.hibernate</groupId> # <artifactId>hibernate-jpamodelgen</artifactId> # <version>5.2.17.final</version> # </path> # </annotationProcessorPaths> # </configuration> # </plugin> # cn.wine.base.redis.gennerator.RedisRoProcessor
然后在其他使用的地方引入这个jar就行了
与其他Processor使用冲突解决
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> <configuration> <source>${java.version}</source> <target>${java.version}</target> <annotationProcessorPaths> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> </path> <path> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>1.2.0.Final</version> </path> <path> <groupId>org.hibernate</groupId> <artifactId>hibernate-jpamodelgen</artifactId> <version>5.2.17.final</version> </path> <path> <groupId>cn.wine</groupId> <artifactId>support-redis</artifactId> <version>${support-redis.version}</version> </path> </annotationProcessorPaths> </configuration> </plugin>
如何调试
1.新建一个remote
2.指定mvnDebugger
解决错误
如果出现maven编译时找不到Process类
服务配置文件不正确, 或构造处理程序对象javax.annotation.processing.Processor .... not found cn.wine.base.redis.gennerator.RedisRoProcessor
修改pom文件
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<source>${jdk.version}</source>
<target>${jdk.version}</target>
<encoding>UTF-8</encoding>
</configuration>
<!--maven默认生命周期里面后编译RedisRoProcessor,修改编译周期先编译Processor再编译整个项目避免报错找不到RedisRoProcessor类-->
<executions>
<execution>
<id>default-compile</id>
<configuration>
<compilerArgument>-proc:none</compilerArgument>
<includes>
<include>cn/wine/base/redis/generator/RedisRoProcessor.java</include>
<!--include dependencies required for LogMeCustomAnnotationProcessor -->
</includes>
</configuration>
</execution>
<execution>
<id>compile-project</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!