說明
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("\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