说明
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(genarateClassName); try(Writer w = f.openWriter();){ PrintWriter pw = new PrintWriter(w); pw.println("package " + generatePackageName + ";"); pw.println(" public 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(" "); } 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