xiejun
2024-11-01 80b6cbfc9c861469146318d0b3dd5f8b8b525b8a
Source/BladeX-Tool/blade-core-auto/src/main/java/org/springblade/core/auto/factories/AutoFactoriesProcessor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,199 @@
/*
 *      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());
   }
}