package com.qianwen.smart.core.auto.factories; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import javax.annotation.processing.Filer; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.annotation.processing.SupportedOptions; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; import javax.tools.FileObject; import javax.tools.StandardLocation; import com.qianwen.smart.core.auto.annotation.AutoIgnore; import com.qianwen.smart.core.auto.common.AbstractBladeProcessor; import com.qianwen.smart.core.auto.common.BootAutoType; import com.qianwen.smart.core.auto.common.MultiSetMap; @SupportedAnnotationTypes({"*"}) @SupportedOptions({"debug"}) public class AutoFactoriesProcessor extends AbstractBladeProcessor { private static final String FEIGN_CLIENT_ANNOTATION = "org.springframework.cloud.openfeign.FeignClient"; private static final String FEIGN_AUTO_CONFIGURE_KEY = "com.qianwen.core.cloud.feign.BladeFeignAutoConfiguration"; private static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; private static final String DEVTOOLS_RESOURCE_LOCATION = "META-INF/spring-devtools.properties"; private final MultiSetMap factories = new MultiSetMap<>(); private Elements elementUtils; public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); this.elementUtils = processingEnv.getElementUtils(); } protected boolean processImpl(Set annotations, RoundEnvironment roundEnv) { if (roundEnv.processingOver()) { generateFactoriesFiles(); } else { processAnnotations(annotations, roundEnv); } return false; } private void processAnnotations(Set annotations, RoundEnvironment roundEnv) { log(annotations.toString()); Set elementSet = roundEnv.getRootElements(); log("All Element set: " + elementSet.toString()); Set typeElementSet = (Set)elementSet.stream().filter(this::isClassOrInterface).filter(e -> e instanceof TypeElement).map(e -> (TypeElement)e).collect(Collectors.toSet()); if (typeElementSet.isEmpty()) { log("Annotations elementSet is isEmpty"); return; } for (TypeElement typeElement : typeElementSet) { if (isAnnotation(this.elementUtils, typeElement, AutoIgnore.class.getName())) { log("Found @AutoIgnore annotationElement: " + typeElement.toString()); continue; } else if (isAnnotation(this.elementUtils, typeElement, FEIGN_CLIENT_ANNOTATION)) { log("Found @FeignClient Element: " + typeElement.toString()); ElementKind elementKind = typeElement.getKind(); if (ElementKind.INTERFACE != elementKind) { fatalError("@FeignClient Element " + typeElement.toString() + " 不是接口。"); } else { String factoryName = typeElement.getQualifiedName().toString(); if (!this.factories.containsVal(factoryName)) { log("读取到新配置 spring.factories factoryName:" + factoryName); this.factories.put(FEIGN_AUTO_CONFIGURE_KEY, factoryName); } } }else { for (BootAutoType autoType : BootAutoType.values()) { String annotation = autoType.getAnnotationName(); if (isAnnotation(this.elementUtils, typeElement, annotation)) { log("Found @" + annotation + " Element: " + typeElement.toString()); String factoryName2 = typeElement.getQualifiedName().toString(); if (!this.factories.containsVal(factoryName2)) { log("读取到新配置 spring.factories factoryName:" + factoryName2); this.factories.put(autoType.getConfigureKey(), factoryName2); } } } } } } private void generateFactoriesFiles() { if (this.factories.isEmpty()) { return; } Filer filer = this.processingEnv.getFiler(); try { FileObject factoriesFile = filer.createResource(StandardLocation.CLASS_OUTPUT, "", FACTORIES_RESOURCE_LOCATION, new Element[0]); FactoriesFiles.writeFactoriesFile(this.factories, factoriesFile.openOutputStream()); String classesPath = factoriesFile.toUri().toString().split("classes")[0]; Path projectPath = Paths.get(new URI(classesPath)).getParent(); String projectName = projectPath.getFileName().toString(); FileObject devToolsFile = filer.createResource(StandardLocation.CLASS_OUTPUT, "", DEVTOOLS_RESOURCE_LOCATION, new Element[0]); FactoriesFiles.writeDevToolsFile(projectName, devToolsFile.openOutputStream()); } catch (IOException | URISyntaxException e) { fatalError(e); } } private boolean isClassOrInterface(Element e) { ElementKind kind = e.getKind(); return (kind == ElementKind.CLASS || kind == ElementKind.INTERFACE); } private boolean isAnnotation(Elements elementUtils, Element e, String annotationFullName) { List annotationList = elementUtils.getAllAnnotationMirrors(e); for (AnnotationMirror annotation : annotationList) { if (isAnnotation(annotationFullName, annotation)) return true; Element element = annotation.getAnnotationType().asElement(); if (element.toString().startsWith("java.lang")) continue; if (isAnnotation(elementUtils, element, annotationFullName)) return true; } return false; } private boolean isAnnotation(String annotationFullName, AnnotationMirror annotation) { return annotationFullName.equals(annotation.getAnnotationType().toString()); } }