This commit is contained in:
2025-11-28 16:23:32 +08:00
commit a9e0e16c29
826 changed files with 89805 additions and 0 deletions

12
blade-core-auto/README.md Normal file
View File

@@ -0,0 +1,12 @@
# auto 代码自动生成
## 规划
1. 生成 java spi
2. 用来生成 `spring.factories`
3. 考虑生成 `spring-devtools.properties`
4. 考虑?生成 `swagger` 注解
## 参考
Google Auto: https://github.com/google/auto
Spring 5 - spring-context-indexerhttps://github.com/spring-projects/spring-framework/tree/master/spring-context-indexer

36
blade-core-auto/pom.xml Normal file
View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>BladeX-Tool</artifactId>
<groupId>org.springblade</groupId>
<version>${revision}</version>
</parent>
<artifactId>blade-core-auto</artifactId>
<name>${project.artifactId}</name>
<version>${project.parent.version}</version>
<packaging>jar</packaging>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.plugin.version}</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator-annotation-processor</artifactId>
<version>6.0.13.Final</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,44 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.auto.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
/**
* ApplicationContextInitializer 处理
*
* @author L.cm
*/
@Documented
@Retention(SOURCE)
@Target(TYPE)
public @interface AutoContextInitializer {
}

View File

@@ -0,0 +1,44 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.auto.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
/**
* EnvironmentPostProcessor 处理
*
* @author L.cm
*/
@Documented
@Retention(SOURCE)
@Target(TYPE)
public @interface AutoEnvPostProcessor {
}

View File

@@ -0,0 +1,44 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.auto.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
/**
* FailureAnalyzer 处理
*
* @author L.cm
*/
@Documented
@Retention(SOURCE)
@Target(TYPE)
public @interface AutoFailureAnalyzer {
}

View File

@@ -0,0 +1,44 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.auto.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
/**
* AutoIgnore 处理
*
* @author L.cm
*/
@Documented
@Retention(SOURCE)
@Target(TYPE)
public @interface AutoIgnore {
}

View File

@@ -0,0 +1,44 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.auto.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
/**
* ApplicationListener 处理
*
* @author L.cm
*/
@Documented
@Retention(SOURCE)
@Target(TYPE)
public @interface AutoListener {
}

View File

@@ -0,0 +1,44 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.auto.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
/**
* SpringApplicationRunListener 处理
*
* @author L.cm
*/
@Documented
@Retention(SOURCE)
@Target(TYPE)
public @interface AutoRunListener {
}

View File

@@ -0,0 +1,90 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.auto.common;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Set;
/**
* 抽象 处理器
*
* @author L.cm
*/
public abstract class AbstractBladeProcessor extends AbstractProcessor {
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
/**
* AutoService 注解处理器
* @param annotations 注解 getSupportedAnnotationTypes
* @param roundEnv 扫描到的 注解新
* @return 是否完成
*/
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
try {
return processImpl(annotations, roundEnv);
} catch (Exception e) {
fatalError(e);
return false;
}
}
protected abstract boolean processImpl(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv);
protected void log(String msg) {
if (processingEnv.getOptions().containsKey("debug")) {
processingEnv.getMessager().printMessage(Kind.NOTE, msg);
}
}
protected void error(String msg, Element element, AnnotationMirror annotation) {
processingEnv.getMessager().printMessage(Kind.ERROR, msg, element, annotation);
}
protected void fatalError(Exception e) {
// We don't allow exceptions of any kind to propagate to the compiler
StringWriter writer = new StringWriter();
e.printStackTrace(new PrintWriter(writer));
fatalError(writer.toString());
}
protected void fatalError(String msg) {
processingEnv.getMessager().printMessage(Kind.ERROR, "FATAL ERROR: " + msg);
}
}

View File

@@ -0,0 +1,78 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.auto.common;
import org.springblade.core.auto.annotation.*;
/**
* 注解类型
*
* @author L.cm
*/
public enum BootAutoType {
/**
* Component组合注解添加到 spring.factories
*/
COMPONENT(BootAutoType.COMPONENT_ANNOTATION, "org.springframework.boot.autoconfigure.EnableAutoConfiguration"),
/**
* ApplicationContextInitializer 添加到 spring.factories
*/
CONTEXT_INITIALIZER(AutoContextInitializer.class.getName(), "org.springframework.context.ApplicationContextInitializer"),
/**
* ApplicationListener 添加到 spring.factories
*/
LISTENER(AutoListener.class.getName(), "org.springframework.context.ApplicationListener"),
/**
* SpringApplicationRunListener 添加到 spring.factories
*/
RUN_LISTENER(AutoRunListener.class.getName(), "org.springframework.boot.SpringApplicationRunListener"),
/**
* FailureAnalyzer 添加到 spring.factories
*/
FAILURE_ANALYZER(AutoFailureAnalyzer.class.getName(), "org.springframework.boot.diagnostics.FailureAnalyzer"),
/**
* EnvironmentPostProcessor 添加到 spring.factories
*/
ENV_POST_PROCESSOR(AutoEnvPostProcessor.class.getName(), "org.springframework.boot.env.EnvironmentPostProcessor");
private final String annotationName;
private final String configureKey;
BootAutoType(String annotationName, String configureKey) {
this.annotationName = annotationName;
this.configureKey = configureKey;
}
public final String getAnnotationName() {
return annotationName;
}
public final String getConfigureKey() {
return configureKey;
}
public static final String COMPONENT_ANNOTATION = "org.springframework.stereotype.Component";
}

View File

@@ -0,0 +1,158 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.auto.common;
import java.util.*;
/**
* MultiSetMap
*
* @author L.cm
*/
public class MultiSetMap<K, V> {
private transient final Map<K, Set<V>> map;
public MultiSetMap() {
map = new HashMap<>();
}
private Set<V> createSet() {
return new HashSet<>();
}
/**
* put to MultiSetMap
*
* @param key 键
* @param value 值
* @return boolean
*/
public boolean put(K key, V value) {
Set<V> set = map.get(key);
if (set == null) {
set = createSet();
if (set.add(value)) {
map.put(key, set);
return true;
} else {
throw new AssertionError("New set violated the set spec");
}
} else if (set.add(value)) {
return true;
} else {
return false;
}
}
/**
* 是否包含某个key
*
* @param key key
* @return 结果
*/
public boolean containsKey(K key) {
return map.containsKey(key);
}
/**
* 是否包含 value 中的某个值
*
* @param value value
* @return 是否包含
*/
public boolean containsVal(V value) {
Collection<Set<V>> values = map.values();
return values.stream().anyMatch(vs -> vs.contains(value));
}
/**
* key 集合
*
* @return keys
*/
public Set<K> keySet() {
return map.keySet();
}
/**
* put list to MultiSetMap
*
* @param key 键
* @param set 值列表
* @return boolean
*/
public boolean putAll(K key, Set<V> set) {
if (set == null) {
return false;
} else {
map.put(key, set);
return true;
}
}
/**
* put MultiSetMap to MultiSetMap
*
* @param data MultiSetMap
* @return boolean
*/
public boolean putAll(MultiSetMap<K, V> data) {
if (data == null || data.isEmpty()) {
return false;
} else {
for (K k : data.keySet()) {
this.putAll(k, data.get(k));
}
return true;
}
}
/**
* get List by key
*
* @param key 键
* @return List
*/
public Set<V> get(K key) {
return map.get(key);
}
/**
* clear MultiSetMap
*/
public void clear() {
map.clear();
}
/**
* isEmpty
*
* @return isEmpty
*/
public boolean isEmpty() {
return map.isEmpty();
}
}

View File

@@ -0,0 +1,52 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.auto.common;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 集合 工具类
*
* @author L.cm
*/
public class Sets {
/**
* 不可变 集合
*
* @param es 对象
* @param <E> 泛型
* @return 集合
*/
@SafeVarargs
public static <E> Set<E> ofImmutableSet(E... es) {
Objects.requireNonNull(es);
return Stream.of(es).collect(Collectors.toSet());
}
}

View File

@@ -0,0 +1,132 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.auto.common;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.QualifiedNameable;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Types;
import java.util.ArrayList;
import java.util.List;
/**
* Type utilities.
*
* @author Stephane Nicoll
* @since 5.0
*/
public class TypeHelper {
private final ProcessingEnvironment env;
private final Types types;
public TypeHelper(ProcessingEnvironment env) {
this.env = env;
this.types = env.getTypeUtils();
}
public String getType(Element element) {
return getType(element != null ? element.asType() : null);
}
public String getType(AnnotationMirror annotation) {
return getType(annotation != null ? annotation.getAnnotationType() : null);
}
public String getType(TypeMirror type) {
if (type == null) {
return null;
}
if (type instanceof DeclaredType) {
DeclaredType declaredType = (DeclaredType) type;
Element enclosingElement = declaredType.asElement().getEnclosingElement();
if (enclosingElement != null && enclosingElement instanceof TypeElement) {
return getQualifiedName(enclosingElement) + "$" + declaredType.asElement().getSimpleName().toString();
} else {
return getQualifiedName(declaredType.asElement());
}
}
return type.toString();
}
private String getQualifiedName(Element element) {
if (element instanceof QualifiedNameable) {
return ((QualifiedNameable) element).getQualifiedName().toString();
}
return element.toString();
}
/**
* Return the super class of the specified {@link Element} or null if this
* {@code element} represents {@link Object}.
*
* @param element Element
* @return Element
*/
public Element getSuperClass(Element element) {
List<? extends TypeMirror> superTypes = this.types.directSupertypes(element.asType());
if (superTypes.isEmpty()) {
// reached java.lang.Object
return null;
}
return this.types.asElement(superTypes.get(0));
}
/**
* Return the interfaces that are <strong>directly</strong> implemented by the
* specified {@link Element} or an empty list if this {@code element} does not
* implement any interface.
*
* @param element Element
* @return Element list
*/
public List<Element> getDirectInterfaces(Element element) {
List<? extends TypeMirror> superTypes = this.types.directSupertypes(element.asType());
List<Element> directInterfaces = new ArrayList<>();
// index 0 is the super class
if (superTypes.size() > 1) {
for (int i = 1; i < superTypes.size(); i++) {
Element e = this.types.asElement(superTypes.get(i));
if (e != null) {
directInterfaces.add(e);
}
}
}
return directInterfaces;
}
public List<? extends AnnotationMirror> getAllAnnotationMirrors(Element e) {
return this.env.getElementUtils().getAllAnnotationMirrors(e);
}
}

View File

@@ -0,0 +1,305 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* 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.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.LinkedHashSet;
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";
/**
* AutoConfiguration 注解
*/
private static final String AUTO_CONFIGURATION = "org.springframework.boot.autoconfigure.AutoConfiguration";
/**
* AutoConfiguration imports out put
*/
private static final String AUTO_CONFIGURATION_IMPORTS_LOCATION = "META-INF/spring/" + AUTO_CONFIGURATION + ".imports";
/**
* 数据承载
*/
private final MultiSetMap<String, String> factories = new MultiSetMap<>();
/**
* spring boot 2.7 @AutoConfiguration
*/
private final Set<String> autoConfigurationImportsSet = new LinkedHashSet<>();
/**
* 元素辅助类
*/
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()) {
// 1. 生成 spring boot 2.7.x @AutoConfiguration
generateAutoConfigurationImportsFiles();
// 2. 生成 spring.factories
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(TypeElement.class::isInstance)
.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 annotationignore 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 + " 不是接口。");
continue;
}
String factoryName = typeElement.getQualifiedName().toString();
if (factories.containsVal(factoryName)) {
continue;
}
log("读取到新配置 spring.factories factoryName" + factoryName);
factories.put(FEIGN_AUTO_CONFIGURE_KEY, factoryName);
} else {
// 1. 生成 2.7.x 的 spi
if (isAnnotation(elementUtils, typeElement, BootAutoType.COMPONENT_ANNOTATION)) {
String autoConfigurationBeanName = typeElement.getQualifiedName().toString();
autoConfigurationImportsSet.add(autoConfigurationBeanName);
log("读取到自动配置 @AutoConfiguration" + autoConfigurationBeanName);
}
// 2. 老的 spring.factories
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 {
// spring.factories 配置
MultiSetMap<String, String> allFactories = new MultiSetMap<>();
// 1. 用户手动配置项目下的 spring.factories 文件
try {
FileObject existingFactoriesFile = filer.getResource(StandardLocation.SOURCE_OUTPUT, "", FACTORIES_RESOURCE_LOCATION);
// 查找是否已经存在 spring.factories
log("Looking for existing spring.factories file at " + existingFactoriesFile.toUri());
MultiSetMap<String, String> existingFactories = FactoriesFiles.readFactoriesFile(existingFactoriesFile, elementUtils);
log("Existing spring.factories entries: " + existingFactories);
allFactories.putAll(existingFactories);
} catch (IOException e) {
log("spring.factories resource file not found.");
}
// 2. 增量编译,已经存在的 spring.factories 文件
try {
FileObject existingFactoriesFile = filer.getResource(StandardLocation.CLASS_OUTPUT, "", FACTORIES_RESOURCE_LOCATION);
// 查找是否已经存在 spring.factories
log("Looking for existing spring.factories file at " + existingFactoriesFile.toUri());
MultiSetMap<String, String> existingFactories = FactoriesFiles.readFactoriesFile(existingFactoriesFile, elementUtils);
log("Existing spring.factories entries: " + existingFactories);
allFactories.putAll(existingFactories);
} catch (IOException e) {
log("spring.factories resource file did not already exist.");
}
// 3. 处理器扫描出来的新的配置
allFactories.putAll(factories);
log("New spring.factories file contents: " + allFactories);
FileObject factoriesFile = filer.createResource(StandardLocation.CLASS_OUTPUT, "", FACTORIES_RESOURCE_LOCATION);
try (OutputStream out = factoriesFile.openOutputStream()) {
FactoriesFiles.writeFactoriesFile(allFactories, out);
}
// 4. devtools 配置,因为有 @Configuration 注解的需要 devtools
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);
try (OutputStream out = devToolsFile.openOutputStream()) {
FactoriesFiles.writeDevToolsFile(projectName, out);
}
} catch (IOException | URISyntaxException e) {
fatalError(e);
}
}
private void generateAutoConfigurationImportsFiles() {
if (autoConfigurationImportsSet.isEmpty()) {
return;
}
Filer filer = processingEnv.getFiler();
try {
// AutoConfiguration 配置
Set<String> allAutoConfigurationImports = new LinkedHashSet<>();
// 1. 用户手动配置项目下的 AutoConfiguration 文件
try {
FileObject existingFactoriesFile = filer.getResource(StandardLocation.SOURCE_OUTPUT, "", AUTO_CONFIGURATION_IMPORTS_LOCATION);
// 查找是否已经存在 spring.factories
log("Looking for existing AutoConfiguration imports file at " + existingFactoriesFile.toUri());
Set<String> existingSet = FactoriesFiles.readAutoConfigurationImports(existingFactoriesFile);
log("Existing AutoConfiguration imports entries: " + existingSet);
allAutoConfigurationImports.addAll(existingSet);
} catch (IOException e) {
log("AutoConfiguration imports resource file not found.");
}
// 2. 增量编译,已经存在的配置文件
try {
FileObject existingFactoriesFile = filer.getResource(StandardLocation.CLASS_OUTPUT, "", AUTO_CONFIGURATION_IMPORTS_LOCATION);
// 查找是否已经存在 spring.factories
log("Looking for existing AutoConfiguration imports file at " + existingFactoriesFile.toUri());
Set<String> existingSet = FactoriesFiles.readAutoConfigurationImports(existingFactoriesFile);
log("Existing AutoConfiguration imports entries: " + existingSet);
allAutoConfigurationImports.addAll(existingSet);
} catch (IOException e) {
log("AutoConfiguration imports resource file did not already exist.");
}
// 3. 处理器扫描出来的新的配置
allAutoConfigurationImports.addAll(autoConfigurationImportsSet);
log("New AutoConfiguration imports file contents: " + allAutoConfigurationImports);
FileObject autoConfigurationImportsFile = filer.createResource(StandardLocation.CLASS_OUTPUT, "", AUTO_CONFIGURATION_IMPORTS_LOCATION);
try (OutputStream out = autoConfigurationImportsFile.openOutputStream()) {
FactoriesFiles.writeAutoConfigurationImportsFile(allAutoConfigurationImports, out);
}
} catch (IOException 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();
String elementStr = element.toString();
// 如果是 java 元注解,继续循环
if (elementStr.startsWith("java.lang") || elementStr.startsWith("kotlin.")) {
continue;
}
// 递归处理 组合注解
if (isAnnotation(elementUtils, element, annotationFullName)) {
return true;
}
}
return false;
}
private boolean isAnnotation(String annotationFullName, AnnotationMirror annotation) {
return annotationFullName.equals(annotation.getAnnotationType().toString());
}
}

View File

@@ -0,0 +1,163 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.auto.factories;
import org.springblade.core.auto.common.MultiSetMap;
import javax.lang.model.util.Elements;
import javax.tools.FileObject;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
/**
* spring boot 自动化配置工具类
*
* @author L.cm
*/
class FactoriesFiles {
private static final Charset UTF_8 = StandardCharsets.UTF_8;
/**
* 读取 spring.factories 文件
*
* @param fileObject FileObject
* @return MultiSetMap
* @throws IOException 异常信息
*/
protected static MultiSetMap<String, String> readFactoriesFile(FileObject fileObject, Elements elementUtils) throws IOException {
// 读取 spring.factories 内容
Properties properties = new Properties();
try (InputStream input = fileObject.openInputStream()) {
properties.load(input);
}
MultiSetMap<String, String> multiSetMap = new MultiSetMap<>();
Set<Map.Entry<Object, Object>> entrySet = properties.entrySet();
for (Map.Entry<Object, Object> objectEntry : entrySet) {
String key = (String) objectEntry.getKey();
String value = (String) objectEntry.getValue();
if (value == null || value.trim().isEmpty()) {
continue;
}
// 解析 spring.factories
String[] values = value.split(",");
Set<String> valueSet = Arrays.stream(values)
.filter(v -> !v.isEmpty())
.map(String::trim)
// 校验是否删除文件
.filter((v) -> Objects.nonNull(elementUtils.getTypeElement(v)))
.collect(Collectors.toSet());
multiSetMap.putAll(key.trim(), valueSet);
}
return multiSetMap;
}
/**
* 读取已经存在的 AutoConfiguration imports
*
* @param fileObject FileObject
* @return Set
* @throws IOException IOException
*/
protected static Set<String> readAutoConfigurationImports(FileObject fileObject) throws IOException {
Set<String> set = new HashSet<>();
try (
InputStream input = fileObject.openInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(input))
) {
reader.lines()
.map(String::trim)
.filter(line -> !line.startsWith("#"))
.forEach(set::add);
}
return set;
}
/**
* 写出 spring.factories 文件
*
* @param factories factories 信息
* @param output 输出流
* @throws IOException 异常信息
*/
protected static void writeFactoriesFile(MultiSetMap<String, String> factories,
OutputStream output) throws IOException {
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output, UTF_8));
Set<String> keySet = factories.keySet();
for (String key : keySet) {
Set<String> values = factories.get(key);
if (values == null || values.isEmpty()) {
continue;
}
writer.write(key);
writer.write("=\\\n ");
StringJoiner joiner = new StringJoiner(",\\\n ");
for (String value : values) {
joiner.add(value);
}
writer.write(joiner.toString());
writer.newLine();
}
writer.flush();
output.close();
}
/**
* 写出 spring-devtools.properties
*
* @param projectName 项目名
* @param output 输出流
* @throws IOException 异常信息
*/
protected static void writeDevToolsFile(String projectName,
OutputStream output) throws IOException {
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output, UTF_8));
String format = "restart.include.%s=/%s[\\\\w-]+\\.jar";
writer.write(String.format(format, projectName, projectName));
writer.flush();
output.close();
}
/**
* 写出 AutoConfiguration imports
*
* @param allAutoConfigurationImports allAutoConfigurationImports
* @param output OutputStream
* @throws IOException IOException
*/
protected static void writeAutoConfigurationImportsFile(Set<String> allAutoConfigurationImports, OutputStream output) throws IOException {
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output, UTF_8));
StringJoiner joiner = new StringJoiner("\n");
for (String configurationImport : allAutoConfigurationImports) {
joiner.add(configurationImport);
}
writer.write(joiner.toString());
writer.flush();
}
}

View File

@@ -0,0 +1,56 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.auto.service;
import java.lang.annotation.*;
/**
* An annotation for service providers as described in {@link java.util.ServiceLoader}. The {@link
* AutoServiceProcessor} generates the configuration files which
* allows service providers to be loaded with {@link java.util.ServiceLoader#load(Class)}.
*
* <p>Service providers assert that they conform to the service provider specification.
* Specifically, they must:
*
* <ul>
* <li>be a non-inner, non-anonymous, concrete class
* <li>have a publicly accessible no-arg constructor
* <li>implement the interface type returned by {@code value()}
* </ul>
*
* @author google
*/
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface AutoService {
/**
* Returns the interfaces implemented by this service provider.
*
* @return interface array
*/
Class<?>[] value();
}

View File

@@ -0,0 +1,263 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.auto.service;
import org.springblade.core.auto.common.AbstractBladeProcessor;
import org.springblade.core.auto.common.MultiSetMap;
import org.springblade.core.auto.common.Sets;
import org.springblade.core.auto.common.TypeHelper;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedOptions;
import javax.lang.model.element.*;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleAnnotationValueVisitor8;
import javax.lang.model.util.Types;
import javax.tools.FileObject;
import javax.tools.StandardLocation;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.util.*;
import java.util.stream.Collectors;
/**
* java spi 服务自动处理器 参考google auto
*
* @author L.cm
*/
@SupportedOptions("debug")
public class AutoServiceProcessor extends AbstractBladeProcessor {
/**
* spi 服务集合key 接口 -> value 实现列表
*/
private final MultiSetMap<String, String> providers = new MultiSetMap<>();
private TypeHelper typeHelper;
@Override
public synchronized void init(ProcessingEnvironment env) {
super.init(env);
this.typeHelper = new TypeHelper(env);
}
@Override
public Set<String> getSupportedAnnotationTypes() {
return Sets.ofImmutableSet(AutoService.class.getName());
}
@Override
protected boolean processImpl(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (roundEnv.processingOver()) {
generateConfigFiles();
} else {
processAnnotations(annotations, roundEnv);
}
return true;
}
private void processAnnotations(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(AutoService.class);
log(annotations.toString());
log(elements.toString());
for (Element e : elements) {
TypeElement providerImplementer = (TypeElement) e;
AnnotationMirror annotationMirror = getAnnotationMirror(e, AutoService.class);
if (annotationMirror == null) {
continue;
}
Set<TypeMirror> typeMirrors = getValueFieldOfClasses(annotationMirror);
if (typeMirrors.isEmpty()) {
error("No service interfaces provided for element!", e, annotationMirror);
continue;
}
for (TypeMirror typeMirror : typeMirrors) {
String providerInterfaceName = typeHelper.getType(typeMirror);
Name providerImplementerName = providerImplementer.getQualifiedName();
log("provider interface: " + providerInterfaceName);
log("provider implementer: " + providerImplementerName);
if (checkImplementer(providerImplementer, typeMirror)) {
providers.put(providerInterfaceName, typeHelper.getType(providerImplementer));
} else {
String message = "ServiceProviders must implement their service provider interface. "
+ providerImplementerName + " does not implement " + providerInterfaceName;
error(message, e, annotationMirror);
}
}
}
}
private void generateConfigFiles() {
Filer filer = processingEnv.getFiler();
for (String providerInterface : providers.keySet()) {
String resourceFile = "META-INF/services/" + providerInterface;
log("Working on resource file: " + resourceFile);
try {
SortedSet<String> allServices = new TreeSet<>();
try {
FileObject existingFile = filer.getResource(StandardLocation.CLASS_OUTPUT, "", resourceFile);
log("Looking for existing resource file at " + existingFile.toUri());
Set<String> oldServices = ServicesFiles.readServiceFile(existingFile.openInputStream());
log("Existing service entries: " + oldServices);
allServices.addAll(oldServices);
} catch (IOException e) {
log("Resource file did not already exist.");
}
Set<String> newServices = new HashSet<>(providers.get(providerInterface));
if (allServices.containsAll(newServices)) {
log("No new service entries being added.");
return;
}
allServices.addAll(newServices);
log("New service file contents: " + allServices);
FileObject fileObject = filer.createResource(StandardLocation.CLASS_OUTPUT, "", resourceFile);
OutputStream out = fileObject.openOutputStream();
ServicesFiles.writeServiceFile(allServices, out);
out.close();
log("Wrote to: " + fileObject.toUri());
} catch (IOException e) {
fatalError("Unable to create " + resourceFile + ", " + e);
return;
}
}
}
/**
* Verifies {@link java.util.spi.LocaleServiceProvider} constraints on the concrete provider class.
* Note that these constraints are enforced at runtime via the ServiceLoader,
* we're just checking them at compile time to be extra nice to our users.
*/
private boolean checkImplementer(TypeElement providerImplementer, TypeMirror providerType) {
// TODO: We're currently only enforcing the subtype relationship
// constraint. It would be nice to enforce them all.
Types types = processingEnv.getTypeUtils();
return types.isSubtype(providerImplementer.asType(), providerType);
}
/**
* 读取 AutoService 上的 value 值
*
* @param annotationMirror AnnotationMirror
* @return value 集合
*/
private Set<TypeMirror> getValueFieldOfClasses(AnnotationMirror annotationMirror) {
return getAnnotationValue(annotationMirror, "value")
.accept(new SimpleAnnotationValueVisitor8<Set<TypeMirror>, Void>() {
@Override
public Set<TypeMirror> visitType(TypeMirror typeMirror, Void v) {
Set<TypeMirror> declaredTypeSet = new HashSet<>(1);
declaredTypeSet.add(typeMirror);
return Collections.unmodifiableSet(declaredTypeSet);
}
@Override
public Set<TypeMirror> visitArray(
List<? extends AnnotationValue> values, Void v) {
return values
.stream()
.flatMap(value -> value.accept(this, null).stream())
.collect(Collectors.toSet());
}
}, null);
}
/**
* Returns a {@link ExecutableElement} and its associated {@link AnnotationValue} if such
* an element was either declared in the usage represented by the provided
* {@link AnnotationMirror}, or if such an element was defined with a default.
*
* @param annotationMirror AnnotationMirror
* @param elementName elementName
* @return AnnotationValue map
* @throws IllegalArgumentException if no element is defined with the given elementName.
*/
public AnnotationValue getAnnotationValue(AnnotationMirror annotationMirror, String elementName) {
Objects.requireNonNull(annotationMirror);
Objects.requireNonNull(elementName);
for (Map.Entry<ExecutableElement, AnnotationValue> entry : getAnnotationValuesWithDefaults(annotationMirror).entrySet()) {
if (entry.getKey().getSimpleName().contentEquals(elementName)) {
return entry.getValue();
}
}
String name = typeHelper.getType(annotationMirror);
throw new IllegalArgumentException(String.format("@%s does not define an element %s()", name, elementName));
}
/**
* Returns the {@link AnnotationMirror}'s map of {@link AnnotationValue} indexed by {@link
* ExecutableElement}, supplying default values from the annotation if the annotation property has
* not been set. This is equivalent to {@link
* Elements#getElementValuesWithDefaults(AnnotationMirror)} but can be called statically without
* an {@link Elements} instance.
*
* <p>The iteration order of elements of the returned map will be the order in which the {@link
* ExecutableElement}s are defined in {@code annotation}'s {@linkplain
* AnnotationMirror#getAnnotationType() type}.
*
* @param annotation AnnotationMirror
* @return AnnotationValue Map
*/
public Map<ExecutableElement, AnnotationValue> getAnnotationValuesWithDefaults(AnnotationMirror annotation) {
Map<ExecutableElement, AnnotationValue> values = new HashMap<>(32);
Map<? extends ExecutableElement, ? extends AnnotationValue> declaredValues = annotation.getElementValues();
for (ExecutableElement method : ElementFilter.methodsIn(annotation.getAnnotationType().asElement().getEnclosedElements())) {
// Must iterate and put in this order, to ensure consistency in generated code.
if (declaredValues.containsKey(method)) {
values.put(method, declaredValues.get(method));
} else if (method.getDefaultValue() != null) {
values.put(method, method.getDefaultValue());
} else {
String name = typeHelper.getType(method);
throw new IllegalStateException(
"Unset annotation value without default should never happen: " + name + '.' + method.getSimpleName() + "()");
}
}
return Collections.unmodifiableMap(values);
}
public AnnotationMirror getAnnotationMirror(Element element, Class<? extends Annotation> annotationClass) {
String annotationClassName = annotationClass.getCanonicalName();
for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) {
String name = typeHelper.getType(annotationMirror);
if (name.contentEquals(annotationClassName)) {
return annotationMirror;
}
}
return null;
}
}

View File

@@ -0,0 +1,86 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.auto.service;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
/**
* A helper class for reading and writing Services files.
*
* @author L.cm
*/
class ServicesFiles {
private static final Charset UTF_8 = StandardCharsets.UTF_8;
/**
* Reads the set of service classes from a service file.
*
* @param input not {@code null}. Closed after use.
* @return a not {@code null Set} of service class names.
* @throws IOException
*/
static Set<String> readServiceFile(InputStream input) throws IOException {
HashSet<String> serviceClasses = new HashSet<>();
try (
InputStreamReader isr = new InputStreamReader(input, UTF_8);
BufferedReader r = new BufferedReader(isr)
) {
String line;
while ((line = r.readLine()) != null) {
int commentStart = line.indexOf('#');
if (commentStart >= 0) {
line = line.substring(0, commentStart);
}
line = line.trim();
if (!line.isEmpty()) {
serviceClasses.add(line);
}
}
return serviceClasses;
}
}
/**
* Writes the set of service class names to a service file.
*
* @param output not {@code null}. Not closed after use.
* @param services a not {@code null Collection} of service class names.
* @throws IOException
*/
static void writeServiceFile(Collection<String> services, OutputStream output) throws IOException {
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output, UTF_8));
for (String service : services) {
writer.write(service);
writer.newLine();
}
writer.flush();
}
}

View File

@@ -0,0 +1,2 @@
org.springblade.core.auto.service.AutoServiceProcessor
org.springblade.core.auto.factories.AutoFactoriesProcessor