¶Ô±ÈÐÂÎļþ |
| | |
| | | /* |
| | | * 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. |
| | | * <p>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<String, String> factories = new MultiSetMap<>(); |
| | | /** |
| | | * å
ç´ è¾
å©ç±» |
| | | */ |
| | | private Elements elementUtils; |
| | | |
| | | @Override |
| | | public synchronized void init(ProcessingEnvironment processingEnv) { |
| | | super.init(processingEnv); |
| | | elementUtils = processingEnv.getElementUtils(); |
| | | } |
| | | |
| | | @Override |
| | | protected boolean processImpl(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { |
| | | if (roundEnv.processingOver()) { |
| | | generateFactoriesFiles(); |
| | | } else { |
| | | processAnnotations(annotations, roundEnv); |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | private void processAnnotations(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { |
| | | // æ¥å¿ æå°ä¿¡æ¯ gradle build --debug |
| | | log(annotations.toString()); |
| | | Set<? extends Element> elementSet = roundEnv.getRootElements(); |
| | | log("All Element set: " + elementSet.toString()); |
| | | |
| | | // è¿æ»¤ TypeElement |
| | | Set<TypeElement> 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<? extends AnnotationMirror> 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()); |
| | | } |
| | | } |