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>

 

posted @ 2020-12-29 18:07  意犹未尽  阅读(2175)  评论(0编辑  收藏  举报