Java Annotation Processor(二)

简介: Java Annotation Processor
  • 为了让代码更加优雅,我们将类元信息分装到一个类中
    FactoryClassInfo
package com.my.annotation.process.processor;
import com.my.annotaion.process.annotation.Factory;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.MirroredTypeException;
/**
 * 工厂类信息
 *
 * @author Zijian Liao
 * @since 1.0.0
 */
public class FactoryClassInfo {
    /**
     * 子类的类元信息
     */
    private final TypeElement typeElement;
    /**
     * 组件的id, 用于确认生成哪个组件
     */
    private final String id;
    /**
     * 注解上的值的类名信息
     */
    private String simpleName;
    /**
     * 注解上的值的全限定类名信息
     */
    private String qualifiedName;
    public FactoryClassInfo(Element element){
        this.typeElement = (TypeElement) element;
        // 获取注解信息
        Factory annotation = typeElement.getAnnotation(Factory.class);
        // apple
        this.id = annotation.id();
        try{
            // 获取注解的值 若该类未被编译,此处将抛出异常
            Class<?> type = annotation.type();
            // 获取注解上的全限定类名信息
            this.qualifiedName = type.getCanonicalName();
            // 获取注解上的类名信息
            this.simpleName = type.getSimpleName();
        }catch (MirroredTypeException e){
            // 所幸的是,该异常里也具备了我们需要的信息
            DeclaredType classTypeMirror = (DeclaredType) e.getTypeMirror();
            TypeElement classTypeElement = (TypeElement) classTypeMirror.asElement();
            // com.my.annotation.process.example.Fruit
            this.qualifiedName = classTypeElement.getQualifiedName().toString();
            // Fruit
            this.simpleName = classTypeElement.getSimpleName().toString();
        }
    }
    public TypeElement getTypeElement() {
        return typeElement;
    }
    public String getId() {
        return id;
    }
    public String getSimpleName() {
        return simpleName;
    }
    public String getQualifiedName() {
        return qualifiedName;
    }
}

  • 最后,通过这些类元信息生成我们的工厂类
    FactoryHandler
package com.my.annotation.process.processor;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import javax.annotation.processing.Filer;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
 * 处理器,用于生成Java File
 *
 * @author Zijian Liao
 * @since 1.0.0
 */
public class FactoryHandler {
    /**
     * 创建出的类名后缀
     */
    private static final String SUFFIX = "Factory";
    /**
     * 存放类信息
     */
    private static final Map<String, List<FactoryClassInfo>> FACTORY_CLASS_INFO_MAP = new ConcurrentHashMap<>(32);
    /**
     * 存放类信息
     * @param factoryClassInfo 类信息
     */
    public static void putIfAbsent(FactoryClassInfo factoryClassInfo){
        String qualifiedName = factoryClassInfo.getQualifiedName();
        if(!FACTORY_CLASS_INFO_MAP.containsKey(qualifiedName)){
            FACTORY_CLASS_INFO_MAP.put(qualifiedName, new ArrayList<>(4));
        }
        List<FactoryClassInfo> factoryClassInfos = FACTORY_CLASS_INFO_MAP.get(qualifiedName);
        factoryClassInfos.add(factoryClassInfo);
    }
    /**
     * 生成Java文件
     * @param elementUtils 元素工具
     * @param filer 文件工具
     * @throws IOException 异常
     */
    public static void generateJavaFile(Elements elementUtils, Filer filer) throws IOException {
        if(FACTORY_CLASS_INFO_MAP.isEmpty()){
            return;
        }
        for (Map.Entry<String, List<FactoryClassInfo>> entry : FACTORY_CLASS_INFO_MAP.entrySet()) {
            String qualifiedName = entry.getKey();
            List<FactoryClassInfo> factoryClassInfos = entry.getValue();
            // 1.得到接口(抽象类)的类名信息
            TypeElement superClassElement = elementUtils.getTypeElement(qualifiedName);
            // 2.创建出工厂方法
            MethodSpec.Builder method = MethodSpec.methodBuilder("create") // 方法名
                    .addParameter(String.class, "id") //方法参数 (类型|名称)
                    .addModifiers(Modifier.PUBLIC) // 修饰符
                    .addModifiers(Modifier.STATIC)
                    .returns(TypeName.get(superClassElement.asType())); // 返回类型
            // 3.遍历子类创建出代码信息
            factoryClassInfos.forEach(factoryClassInfo -> {
                // $S 表示String类型占位符
                // $L 表示名称占位符 给啥就是啥
                method.beginControlFlow("if($S.equals(id))", factoryClassInfo.getId()) // 开启一个控制语句
                        .addStatement("return new $L()", factoryClassInfo.getTypeElement().getQualifiedName().toString()) // 添加一条语句
                        .endControlFlow(); // 结束控制语句
            });
            // 由于工厂方法需要返回值,若最后都不匹配则抛出异常
            // 添加一条抛异常语句
            method.addStatement("throw new IllegalArgumentException($S + id)", "Unknown id = ");
            // 4. 创建工厂类
            // 接口(抽象类)的类名
            String superSimpleName = superClassElement.getSimpleName().toString();
            // 类名则为 superSimpleName + Factory
            String className = superSimpleName + SUFFIX;
            TypeSpec typeSpec = TypeSpec.classBuilder(className)
                    .addModifiers(Modifier.PUBLIC)
                    .addMethod(method.build())
                    .build();
            // 5.写入java文件
            // 获取到包名信息
            PackageElement packageElement = elementUtils.getPackageOf(superClassElement);
            String packageName = packageElement.getQualifiedName().toString();
            JavaFile.builder(packageName, typeSpec).build().writeTo(filer);
        }
    }
    public static void clear(){
        FACTORY_CLASS_INFO_MAP.clear();
    }
}

测试我们的案例

  1. 增加一个橘子的水果类
@Factory(id = "orange", type = Fruit.class)
public class Orange implements Fruit {
    @Override
    public Float getPrice() {
        return 3.5F;
    }
}
  1. 进行编译
  2. 查看编译后的水果工厂
public class FruitFactory {
    public FruitFactory() {
    }
    public static Fruit create(String id) {
        if ("orange".equals(id)) {
            return new Orange();
        } else if ("banana".equals(id)) {
            return new Banana();
        } else if ("apple".equals(id)) {
            return new Apple();
        } else {
            throw new IllegalArgumentException("Unknown id = " + id);
        }
    }
}

成功了~

如何调试Processor

因为我们的Processor是在编译时生效的,那么我们怎么进行debug调试呢?

  1. 打开Idea的配置

    edit_config.png
  2. 添加一个maven配置

    add_maven.png
  3. 编写配置

    maven_config.png

主要编辑Working directoryCommand line两项配置

Working directory: 填写使用Processor的模块

Command line: maven命令,-X表示debug模式

  1. 开始调试
    断点到我们的FactoryProcessor类中,点击IDEA的debug启动按钮

注意:调试前一定要先把processor模块打包

案例地址

https://github.com/lzj960515/annotation-processor-demo/tree/main

目录
相关文章
|
4月前
|
安全 Java 编译器
Java其他: 什么是Java中的注解(Annotation)?
Java其他: 什么是Java中的注解(Annotation)?
70 0
|
SQL XML SpringCloudAlibaba
Java独有特性:注解(annotation)
注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。它本身并不起任何作用,可以说有它没它都不影响程序的正常运行,注解的作用在于**「注解的处理程序」**,注解处理程序通过捕获
151 0
|
4月前
|
算法 安全 Ubuntu
8 种 Java 内存溢出之八 -Kill process or sacrifice child
8 种 Java 内存溢出之八 -Kill process or sacrifice child
|
4月前
|
Java 编译器 开发者
Java注解(Annotation)技术深入解析
Java注解(Annotation)技术深入解析
429 1
|
11月前
|
Java Maven
【异常】java: Internal error in the mapping processor: java.lang.NullPointerException
【异常】java: Internal error in the mapping processor: java.lang.NullPointerException
262 0
|
4月前
|
Java
【Java】注解(Annotation)
【Java】注解(Annotation)
30 0
|
安全 Java 编译器
Java中的String实例化、Annotation注解类、继承的多态和Object类(附带相关面试题)
1.java中String两种实例化对象2.Annotation注解类 3.继承的多态 4.Object类
106 0
Java中的String实例化、Annotation注解类、继承的多态和Object类(附带相关面试题)
|
设计模式 缓存 Java
Java反射(反射与代理设计模式、反射与Annotation、自定义Annotation、反射整合工厂设计模式和代理设计模式)
1.反射与代理设计模式,2.反射与Annotation,3.自定义Annotation,4.Annotation整合工厂设计模式和代理设计模式
62 0
|
Java 测试技术 程序员
Java的注解(Annotation)
Java的注解(Annotation)
124 0
|
SQL Java 数据库连接
Java 注解 Annotation自定义实战
Java 注解 Annotation自定义实战
124 0