- 为了让代码更加优雅,我们将类元信息分装到一个类中
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(); } }
测试我们的案例
- 增加一个橘子的水果类
@Factory(id = "orange", type = Fruit.class) public class Orange implements Fruit { @Override public Float getPrice() { return 3.5F; } }
- 进行编译
- 查看编译后的水果工厂
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调试呢?
- 打开Idea的配置
edit_config.png - 添加一个maven配置
add_maven.png - 编写配置
maven_config.png
主要编辑
Working directory
和Command line
两项配置
Working directory
: 填写使用Processor
的模块
Command line
: maven命令,-X
表示debug模式
- 开始调试
断点到我们的FactoryProcessor
类中,点击IDEA的debug启动按钮
注意:调试前一定要先把
processor
模块打包
案例地址
https://github.com/lzj960515/annotation-processor-demo/tree/main