提交 6017ad41 编写于 作者: 如梦技术's avatar 如梦技术 🐛

🔖 v1.2.0

上级 a4b6abec
# 变更记录
## 发行版本
## [1.2.0] - 2019-12-06
### 添加
- 添加对 java spi 的支持,并精简代码。
- lombok、spring boot、google auto 依赖升级。
## [1.1.0] - 2019-05-24
### 添加
- 添加 `@AutoContextInitializer` 支持 `org.springframework.context.ApplicationContextInitializer`
......
......@@ -3,14 +3,19 @@ apply from: "${rootProject.projectDir}/gradle/publish-maven.gradle"
ext {
javaVersion = JavaVersion.VERSION_1_8
springBootVersion = "2.1.5.RELEASE"
lombokVersion = "1.18.8"
googleAutoServiceVersion = "1.0-rc5"
springBootVersion = "2.2.1.RELEASE"
lombokVersion = "1.18.10"
googleAutoServiceVersion = "1.0-rc6"
}
group = GROUPID
version = VERSION
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
}
dependencies {
api "org.springframework.boot:spring-boot-configuration-processor:$springBootVersion"
api "org.springframework.boot:spring-boot-autoconfigure-processor:$springBootVersion"
......
/*
* Copyright 2008 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.dreamlu.mica.auto.annotation;
import java.lang.annotation.*;
/**
* An annotation for service providers as described in {@link java.util.ServiceLoader}. The {@link
* net.dreamlu.mica.auto.service.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();
}
......@@ -19,12 +19,12 @@ package net.dreamlu.mica.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.lang.model.element.*;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic.Kind;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
import java.util.Set;
/**
......@@ -41,8 +41,9 @@ public abstract class AbstractMicaProcessor extends AbstractProcessor {
/**
* AutoService 注解处理器
*
* @param annotations 注解 getSupportedAnnotationTypes
* @param roundEnv 扫描到的 注解新
* @param roundEnv 扫描到的 注解新
* @return 是否完成
*/
@Override
......@@ -57,12 +58,118 @@ public abstract class AbstractMicaProcessor extends AbstractProcessor {
protected abstract boolean processImpl(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv);
/**
* 判断为类
*
* @param e Element
* @return {boolean}
*/
protected boolean isClass(Element e) {
ElementKind kind = e.getKind();
return kind == ElementKind.CLASS;
}
/**
* 判断为类或者接口
*
* @param e Element
* @return {boolean}
*/
protected boolean isClassOrInterface(Element e) {
ElementKind kind = e.getKind();
return kind == ElementKind.CLASS || kind == ElementKind.INTERFACE;
}
/**
* 获取注解,支持组合注解
*
* @param elementUtils elementUtils
* @param e Element
* @param annotationFullName annotationFullName
* @return {boolean}
*/
protected AnnotationMirror getAnnotation(Elements elementUtils, Element e, String annotationFullName) {
List<? extends AnnotationMirror> annotationList = elementUtils.getAllAnnotationMirrors(e);
for (AnnotationMirror annotation : annotationList) {
// 如果是对于的注解
if (isAnnotation(annotationFullName, annotation)) {
return annotation;
}
// 处理组合注解
Element element = annotation.getAnnotationType().asElement();
// 如果是 java 元注解,继续循环
if (element.toString().startsWith("java.lang")) {
continue;
}
// 递归处理 组合注解
return getAnnotation(elementUtils, element, annotationFullName);
}
return null;
}
/**
* 判断是相同的注解,支持组合注解
*
* @param elementUtils elementUtils
* @param e Element
* @param annotationFullName annotationFullName
* @return {boolean}
*/
protected 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;
}
/**
* 判断是否同一个注解
*
* @param annotationFullName annotationFullName
* @param annotation AnnotationMirror
* @return {boolean}
*/
protected boolean isAnnotation(String annotationFullName, AnnotationMirror annotation) {
return annotationFullName.equals(annotation.getAnnotationType().toString());
}
/**
* 获取属性的名称
*
* @param element Element
* @return {String}
*/
protected String getQualifiedName(Element element) {
if (element instanceof QualifiedNameable) {
return ((QualifiedNameable) element).getQualifiedName().toString();
}
return element.toString();
}
protected void log(String msg) {
if (processingEnv.getOptions().containsKey("debug")) {
processingEnv.getMessager().printMessage(Kind.NOTE, msg);
}
}
protected void error(String msg, Element element) {
processingEnv.getMessager().printMessage(Kind.ERROR, msg, element);
}
protected void error(String msg, Element element, AnnotationMirror annotation) {
processingEnv.getMessager().printMessage(Kind.ERROR, msg, element, annotation);
}
......
......@@ -16,8 +16,8 @@
package net.dreamlu.mica.auto.common;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.dreamlu.mica.auto.annotation.AutoContextInitializer;
import net.dreamlu.mica.auto.annotation.AutoFailureAnalyzer;
import net.dreamlu.mica.auto.annotation.AutoListener;
......@@ -28,7 +28,7 @@ import net.dreamlu.mica.auto.annotation.AutoListener;
* @author L.cm
*/
@Getter
@AllArgsConstructor
@RequiredArgsConstructor
public enum BootAutoType {
/**
* 注解处理的类型
......
......@@ -22,7 +22,6 @@ import net.dreamlu.mica.auto.common.BootAutoType;
import net.dreamlu.mica.auto.common.MultiSetMap;
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;
......@@ -34,7 +33,6 @@ 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;
......@@ -163,34 +161,4 @@ public class AutoFactoriesProcessor extends AbstractMicaProcessor {
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());
}
}
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.dreamlu.mica.auto.service;
import com.google.auto.service.AutoService;
import net.dreamlu.mica.auto.common.AbstractMicaProcessor;
import net.dreamlu.mica.auto.common.MultiSetMap;
import javax.annotation.processing.*;
import javax.lang.model.element.*;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
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.util.*;
import java.util.stream.Collectors;
/**
* java spi 服务自动处理器 参考:google auto
*
* @author L.cm
*/
@SupportedOptions("debug")
@AutoService(Processor.class)
public class AutoServiceProcessor extends AbstractMicaProcessor {
/**
* AutoService 注解名
*/
private static final String AUTO_SERVICE_NAME = net.dreamlu.mica.auto.annotation.AutoService.class.getName();
/**
* spi 服务集合,key 接口 -> value 实现列表
*/
private MultiSetMap<String, String> providers = new MultiSetMap<>();
/**
* 元素辅助类
*/
private Elements elementUtils;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
elementUtils = processingEnv.getElementUtils();
}
@Override
public Set<String> getSupportedAnnotationTypes() {
return Collections.singleton(AUTO_SERVICE_NAME);
}
@Override
protected boolean processImpl(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (roundEnv.processingOver()) {
generateConfigFiles();
} else {
processAnnotations(annotations, roundEnv);
}
return false;
}
private void processAnnotations(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
TypeElement autoService = elementUtils.getTypeElement(AUTO_SERVICE_NAME);
Set<? extends Element> elementSet = roundEnv.getElementsAnnotatedWith(autoService);
// 过滤 TypeElement
Set<TypeElement> typeElementSet = elementSet.stream()
.filter(this::isClass)
.filter(e -> e instanceof TypeElement)
.map(e -> (TypeElement) e)
.collect(Collectors.toSet());
// 如果为空直接跳出
if (typeElementSet.isEmpty()) {
log("Annotations elementSet is isEmpty");
return;
}
log(annotations.toString());
log(typeElementSet.toString());
for (TypeElement typeElement : typeElementSet) {
AnnotationMirror annotationMirror = getAnnotation(elementUtils, typeElement, AUTO_SERVICE_NAME);
if (annotationMirror == null) {
continue;
}
Set<TypeMirror> typeMirrors = getValueFieldOfClasses(annotationMirror);
if (typeMirrors.isEmpty()) {
error("No service interfaces provided for element!", typeElement, annotationMirror);
continue;
}
// 接口的名称
String providerImplementerName = getQualifiedName(typeElement);
for (TypeMirror typeMirror : typeMirrors) {
String providerInterfaceName = getType(typeMirror);
log("provider interface: " + providerInterfaceName);
log("provider implementer: " + providerImplementerName);
if (checkImplementer(typeElement, typeMirror)) {
providers.put(providerInterfaceName, getQualifiedName(typeElement));
} else {
String message = "ServiceProviders must implement their service provider interface. "
+ providerImplementerName + " does not implement " + providerInterfaceName;
error(message, typeElement, 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(Element 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) {
return Collections.singleton(typeMirror);
}
@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);
}
public AnnotationValue getAnnotationValue(AnnotationMirror annotationMirror, String elementName) {
Objects.requireNonNull(annotationMirror);
Objects.requireNonNull(elementName);
for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : elementUtils.getElementValuesWithDefaults(annotationMirror).entrySet()) {
if (entry.getKey().getSimpleName().contentEquals(elementName)) {
return entry.getValue();
}
}
String annotationName = annotationMirror.getAnnotationType().toString();
throw new IllegalArgumentException(String.format("@%s does not define an element %s()", annotationName, elementName));
}
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();
}
}
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.dreamlu.mica.auto.service;
import lombok.experimental.UtilityClass;
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
*/
@UtilityClass
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();
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册