/* * Copyright (c) 2018-2028, DreamLu All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * Neither the name of the dreamlu.net developer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * Author: DreamLu 卢春梦 (596392912@qq.com) */ package org.springblade.core.auto.factories; import org.springblade.core.auto.annotation.AutoIgnore; import org.springblade.core.auto.common.AbstractBladeProcessor; import org.springblade.core.auto.common.BootAutoType; import org.springblade.core.auto.common.MultiSetMap; import org.springblade.core.auto.service.AutoService; import javax.annotation.processing.*; 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 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; /** * spring boot 自动配置处理器 * * @author L.cm */ @AutoService(Processor.class) @SupportedAnnotationTypes("*") @SupportedOptions("debug") public class AutoFactoriesProcessor extends AbstractBladeProcessor { /** * 处理的注解 @FeignClient */ private static final String FEIGN_CLIENT_ANNOTATION = "org.springframework.cloud.openfeign.FeignClient"; /** * Feign 自动配置 */ private static final String FEIGN_AUTO_CONFIGURE_KEY = "org.springblade.core.cloud.feign.BladeFeignAutoConfiguration"; /** * The location to look for factories. *

Can be present in multiple JAR files. */ private static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; /** * devtools,有 Configuration 注解的 jar 一般需要 devtools 配置文件 */ private static final String DEVTOOLS_RESOURCE_LOCATION = "META-INF/spring-devtools.properties"; /** * 数据承载 */ private final MultiSetMap factories = new MultiSetMap<>(); /** * 元素辅助类 */ private Elements elementUtils; @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); elementUtils = processingEnv.getElementUtils(); } @Override protected boolean processImpl(Set annotations, RoundEnvironment roundEnv) { if (roundEnv.processingOver()) { generateFactoriesFiles(); } else { processAnnotations(annotations, roundEnv); } return false; } private void processAnnotations(Set annotations, RoundEnvironment roundEnv) { // 日志 打印信息 gradle build --debug log(annotations.toString()); Set elementSet = roundEnv.getRootElements(); log("All Element set: " + elementSet.toString()); // 过滤 TypeElement Set typeElementSet = 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(elementUtils, typeElement, AutoIgnore.class.getName())) { log("Found @AutoIgnore annotation,ignore Element: " + typeElement.toString()); } else if (isAnnotation(elementUtils, typeElement, FEIGN_CLIENT_ANNOTATION)) { log("Found @FeignClient Element: " + typeElement.toString()); ElementKind elementKind = typeElement.getKind(); // Feign Client 只处理 接口 if (ElementKind.INTERFACE != elementKind) { fatalError("@FeignClient Element " + typeElement.toString() + " 不是接口。"); continue; } String factoryName = typeElement.getQualifiedName().toString(); if (factories.containsVal(factoryName)) { continue; } log("读取到新配置 spring.factories factoryName:" + factoryName); factories.put(FEIGN_AUTO_CONFIGURE_KEY, factoryName); } else { for (BootAutoType autoType : BootAutoType.values()) { String annotation = autoType.getAnnotationName(); if (isAnnotation(elementUtils, typeElement, annotation)) { log("Found @" + annotation + " Element: " + typeElement.toString()); String factoryName = typeElement.getQualifiedName().toString(); if (factories.containsVal(factoryName)) { continue; } log("读取到新配置 spring.factories factoryName:" + factoryName); factories.put(autoType.getConfigureKey(), factoryName); } } } } } private void generateFactoriesFiles() { if (factories.isEmpty()) { return; } Filer filer = processingEnv.getFiler(); try { // 1. spring.factories FileObject factoriesFile = filer.createResource(StandardLocation.CLASS_OUTPUT, "", FACTORIES_RESOURCE_LOCATION); FactoriesFiles.writeFactoriesFile(factories, factoriesFile.openOutputStream()); String classesPath = factoriesFile.toUri().toString().split("classes")[0]; Path projectPath = Paths.get(new URI(classesPath)).getParent(); // 2. devtools 配置,因为有 @Configuration 注解的需要 devtools String projectName = projectPath.getFileName().toString(); FileObject devToolsFile = filer.createResource(StandardLocation.CLASS_OUTPUT, "", DEVTOOLS_RESOURCE_LOCATION); 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(); // 如果是 java 元注解,继续循环 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()); } }