1. 概述
本文,我们来补充 《精尽 Spring Boot 源码分析 —— 自动配置》 文章,并未详细解析的 AutoConfigurationMetadataLoader 。在 SpringApplication 中,我们可以看到 AutoConfigurationImportSelector.AutoConfigurationGroup#loadMetadata(ClassLoader classLoader, String path)
方法中,加载自动配置类(AutoConfiguration)的元数据,是如下一段代码:
// AutoConfigurationImportSelector.AutoConfigurationGroup.java |
- 在内部,会调用
AutoConfigurationMetadataLoader#loadMetadata(ClassLoader classLoader)
方法,加载 AutoConfigurationMetadata 对象。 - 而我们知道,后续会基于返回的 AutoConfigurationMetadata 对象,进行 AutoConfiguration 类的过滤,从而避免不符合条件的 AutoConfiguration 类的字节码,加载到 JVM 中。那么是怎么做到的呢?我们接着来看 「2. AutoConfigurationMetadataLoader」 。
2. AutoConfigurationMetadataLoader
org.springframework.boot.autoconfigure.AutoConfigurationMetadataLoader
,AutoConfigurationMetadata 加载器。其类上的注释如下:
/** |
2.1 loadMetadata
#loadMetadata(ClassLoader classLoader)
静态方法,加载 AutoConfigurationMetadata 。代码如下:
// AutoConfigurationMetadataLoader.java |
<1>
处,获得PATH
对应的 URL 们,而PATH
就是"META-INF/spring-autoconfigure-metadata.properties"
文件。这样,我们就可以避免去 AutoConfiguration 类上,读取其 Condition 条件了,从而避免将不符合条件的 AutoConfiguration 类的字节码,加载到 JVM 中。那么,此时就会有一个疑问,"META-INF/spring-autoconfigure-metadata.properties"
是怎么来的呢?答案我们在 「3. AutoConfigureAnnotationProcessor」 中说。<2>
处,遍历 URL 数组,读取到properties
中。<3>
处,调用#loadMetadata(Properties properties)
方法,将properties
转换成 PropertiesAutoConfigurationMetadata 对象。代码如下:// AutoConfigurationMetadataLoader.java
static AutoConfigurationMetadata loadMetadata(Properties properties) {
return new PropertiesAutoConfigurationMetadata(properties);
}- 关于 PropertiesAutoConfigurationMetadata 类,在 「2.2 」 中看。
2.2 PropertiesAutoConfigurationMetadata
PropertiesAutoConfigurationMetadata ,是 AutoConfigurationMetadataLoader 的内部静态类,实现 AutoConfigurationMetadata 接口,代码如下:
// AutoConfigurationMetadataLoader#PropertiesAutoConfigurationMetadata.java |
3. AutoConfigureAnnotationProcessor
在 Spring Boot 的源码中,我们如果去检索 "spring-autoconfigure-metadata.properties"
文件,然而并找不到。是不是感觉很奇怪。于是乎,艿艿在搜索了一些网络的上的资料,原来是需要引入 spring-boot-autoconfigure-processor
依赖。这样,它的 AutoConfigureAnnotationProcessor 类,就会自动根据 AutoConfiguration 类的条件,生成 "META-INF/spring-autoconfigure-metadata.properties"
文件。
那么,此时又会有一个疑惑,那是什么时候生成呢?AutoConfigureAnnotationProcessor 继承自 javax.annotation.processing.AbstractProcessor
类,它可以在编译时,扫描和处理注解(Annotation),从而生成 "META-INF/spring-autoconfigure-metadata.properties"
文件。😈 很有意思。
FROM 《Java 注解处理器》
注解处理器(Annotation Processor)是javac的一个工具,它用来在编译时扫描和处理注解(Annotation)。你可以对自定义注解,并注册相应的注解处理器。到这里,我假设你已经知道什么是注解,并且知道怎么申明的一个注解。如果你不熟悉注解,你可以在这官方文档中得到更多信息。注解处理器在Java 5开始就有了,但是从Java 6(2006年12月发布)开始才有可用的API。过了一些时间,Java世界才意识到注解处理器的强大作用,所以它到最近几年才流行起来。
那么,我们开始撸撸 AutoConfigureAnnotationProcessor 的源码吧。
org.springframework.boot.autoconfigureprocessor.AutoConfigureAnnotationProcessor
,继承 AbstractProcessor 抽象类,根据 AutoConfiguration 类的条件,生成 "META-INF/spring-autoconfigure-metadata.properties"
文件。其类上注释如下:
// AutoConfigureAnnotationProcessor.java |
3.1 构造方法
// AutoConfigureAnnotationProcessor.java |
annotations
属性,注解名和全类名的映射。在<1>
处,调用#addAnnotations(Map<String, String> annotations)
方法,进行初始化。代码如下:// AutoConfigureAnnotationProcessor.java
protected void addAnnotations(Map<String, String> annotations) {
// 条件
annotations.put("Configuration", "org.springframework.context.annotation.Configuration");
annotations.put("ConditionalOnClass", "org.springframework.boot.autoconfigure.condition.ConditionalOnClass");
annotations.put("ConditionalOnBean", "org.springframework.boot.autoconfigure.condition.ConditionalOnBean");
annotations.put("ConditionalOnSingleCandidate", "org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate");
annotations.put("ConditionalOnWebApplication", "org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication");
// 排序
annotations.put("AutoConfigureBefore", "org.springframework.boot.autoconfigure.AutoConfigureBefore");
annotations.put("AutoConfigureAfter", "org.springframework.boot.autoconfigure.AutoConfigureAfter");
annotations.put("AutoConfigureOrder", "org.springframework.boot.autoconfigure.AutoConfigureOrder");
}
valueExtractors
属性,注解名和 ValueExtractor 的映射。在<2>
处,调用#addValueExtractors(Map<String, ValueExtractor> attributes)
方法,进行初始化。代码如下:// AutoConfigureAnnotationProcessor.java
private void addValueExtractors(Map<String, ValueExtractor> attributes) {
attributes.put("Configuration", ValueExtractor.allFrom("value"));
attributes.put("ConditionalOnClass", new OnClassConditionValueExtractor());
attributes.put("ConditionalOnBean", new OnBeanConditionValueExtractor());
attributes.put("ConditionalOnSingleCandidate", new OnBeanConditionValueExtractor());
attributes.put("ConditionalOnWebApplication", ValueExtractor.allFrom("type"));
attributes.put("AutoConfigureBefore", ValueExtractor.allFrom("value", "name"));
attributes.put("AutoConfigureAfter", ValueExtractor.allFrom("value", "name"));
attributes.put("AutoConfigureOrder", ValueExtractor.allFrom("value"));
}
properties
属性,扫描和处理注解(Annotation),生成的 Properties 对象。
3.2 ValueExtractor
ValueExtractor ,是 AutoConfigureAnnotationProcessor 的内部接口,值提取器接口。代码如下:
// AutoConfigureAnnotationProcessor#ValueExtractor.java |
3.2.1 AbstractValueExtractor
AbstractValueExtractor ,是 AutoConfigureAnnotationProcessor 的内部类,实现 ValueExtractor 接口,ValueExtractor 抽象实现类。代码如下:
// AutoConfigureAnnotationProcessor#AbstractValueExtractor.java |
- 提供了从 AnnotationValue 读取值的公用方法。
3.2.2 NamedValuesExtractor
NamedValuesExtractor ,是 AutoConfigureAnnotationProcessor 的内部类,继承 AbstractValueExtractor 抽象类,读取 names
的 ValueExtractor 实现类。代码如下:
// AutoConfigureAnnotationProcessor#NamedValuesExtractor.java |
3.2.3 OnBeanConditionValueExtractor
OnBeanConditionValueExtractor ,是 AutoConfigureAnnotationProcessor 的内部类,继承 AbstractValueExtractor 抽象类,读取 @ConditionalOnBean
和 @ConditionalOnSingleCandidate
注解的 ValueExtractor 实现类。代码如下:
// AutoConfigureAnnotationProcessor#OnBeanConditionValueExtractor.java |
3.2.4 OnClassConditionValueExtractor
OnClassConditionValueExtractor ,是 AutoConfigureAnnotationProcessor 的内部类,继承 NamedValuesExtractor 类,读取 @OnClassConditionValueExtractor
注解的 ValueExtractor 实现类。代码如下:
// AutoConfigureAnnotationProcessor#OnClassConditionValueExtractor.java |
3.3 process
实现 #process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)
方法,进行处理。代码如下:
// AutoConfigureAnnotationProcessor.java |
<1>
处,调用#process(RoundEnvironment roundEnv, String propertyKey, String annotationName)
方法,遍历annotations
集合,逐个处理,添加到properties
中。代码如下:// AutoConfigureAnnotationProcessor.java
private void process(RoundEnvironment roundEnv, String propertyKey, String annotationName) {
TypeElement annotationType = this.processingEnv.getElementUtils().getTypeElement(annotationName);
if (annotationType != null) {
for (Element element : roundEnv.getElementsAnnotatedWith(annotationType)) {
Element enclosingElement = element.getEnclosingElement();
if (enclosingElement != null
&& enclosingElement.getKind() == ElementKind.PACKAGE) {
processElement(element, propertyKey, annotationName);
}
}
}
}
// propertyKey=注解名
// annotationName=全类名
private void processElement(Element element, String propertyKey, String annotationName) {
try {
// 获得自动配置类的全类名。例如说:org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration
String qualifiedName = Elements.getQualifiedName(element);
// 获得 AnnotationMirror 对象
AnnotationMirror annotation = getAnnotation(element, annotationName);
if (qualifiedName != null && annotation != null) {
// 获得值
List<Object> values = getValues(propertyKey, annotation);
// 添加到 properties 中
this.properties.put(qualifiedName + "." + propertyKey, toCommaDelimitedString(values));
// 添加到 properties 中
this.properties.put(qualifiedName, "");
}
} catch (Exception ex) {
throw new IllegalStateException("Error processing configuration meta-data on " + element, ex);
}
}
// 获得 AnnotationMirror 对象
private AnnotationMirror getAnnotation(Element element, String type) {
if (element != null) {
for (AnnotationMirror annotation : element.getAnnotationMirrors()) {
if (type.equals(annotation.getAnnotationType().toString())) {
return annotation;
}
}
}
return null;
}
// 获得值
private List<Object> getValues(String propertyKey, AnnotationMirror annotation) {
ValueExtractor extractor = this.valueExtractors.get(propertyKey);
if (extractor == null) {
return Collections.emptyList();
}
return extractor.getValues(annotation);
}
// 拼接值
private String toCommaDelimitedString(List<Object> list) {
StringBuilder result = new StringBuilder();
for (Object item : list) {
result.append((result.length() != 0) ? "," : "");
result.append(item);
}
return result.toString();
}- 胖友简单瞅两眼即可,不是很重要哈~
<2>
处,调用#writeProperties()
方法,处理完成,写到文件PROPERTIES_PATH
中。代码如下:// AutoConfigureAnnotationProcessor.java
protected static final String PROPERTIES_PATH = "META-INF/" + "spring-autoconfigure-metadata.properties";
private void writeProperties() throws IOException {
if (!this.properties.isEmpty()) {
// 创建 FileObject 对象
FileObject file = this.processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", PROPERTIES_PATH);
// 写入 properties 到文件
try (OutputStream outputStream = file.openOutputStream()) {
this.properties.store(outputStream, null);
}
}
}- 这不,和 「2. AutoConfigurationMetadataLoader」 就对上列。
666. 彩蛋
😈 简单小文一篇~在 spring-boot-autoconfigure-processor
的寻找上,花了一些些时间。HOHO ~