使用SpringBoot的最大好处就是简化配置,它实现了自动化配置。
这里以SpringBoot 2.1.4.RELEASE版本和Spring 5.1.6.RELEASE版本为例。
API文档:https://docs.spring.io/spring-boot/docs/current/api/
自动化配置的原理如下:
一个SpringBoot构建的项目都会有一个入口启动类,其中有个最重要的注解就是@SpringBootApplication,其源码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
({ElementType.TYPE})
@Retention
(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
(
excludeFilters = {
@Filter
(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.
class
}
),
@Filter
(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.
class
}
)}
)
public
@interface
SpringBootApplication {
@AliasFor
(
annotation = EnableAutoConfiguration.
class
)
Class>[] exclude()
default
{};
@AliasFor
(
annotation = EnableAutoConfiguration.
class
)
String[] excludeName()
default
{};
@AliasFor
(
annotation = ComponentScan.
class
,
attribute =
"basePackages"
)
String[] scanBasePackages()
default
{};
@AliasFor
(
annotation = ComponentScan.
class
,
attribute =
"basePackageClasses"
)
Class>[] scanBasePackageClasses()
default
{};
}
在SpringBootApplication类上有一个重要的注解@EnableAutoConfiguration,它就是实现自动化配置的核心。当SpringBoot项目启动的时候,就会调用@EnableAutoConfiguration来进一步加载系统所需的一些配置信息,完成自动化配置。
@EnableAutoConfiguration的源码如下:
1
2
3
4
5
6
7
8
9
10
11
@Target
({ElementType.TYPE})
@Retention
(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import
({AutoConfigurationImportSelector.
class
})
public
@interface
EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY =
"spring.boot.enableautoconfiguration"
;
Class>[] exclude()
default
{};
String[] excludeName()
default
{};
}
在EnableAutoConfiguration类中,它使用@Import注解来导入配置类AutoConfigurationImportSelector。
使用@Import注解可以导入三种类型的配置类,如下:
(1)直接导入配置类:@Import({xxxConfiguration.class})
(2)依据条件选择配置类:@Import({xxxSelector.class})
(3)动态注册 Bean:@Import({xxxRegistrar.class})
我们进入查看AutoConfigurationImportSelector类的源码,(由于源码太多,在此不再展示),其中用到了一个重要的类SpringFactoriesLoader,该类位于org.springframework.core.io.support包下,它才是真正加载项目所需要的jar包的类,它主要用于加载 classpath下所有 JAR 文件的 META-INF/spring.factories 文件,并分析出其中定义的工厂类。这些工厂类进而被启动逻辑使用,应用于进一步初始化工作。
SpringFactoriesLoader类是spring框架自己使用的内部工具类,本身被声明为 final,表示不可以被其他类继承。
SpringFactoriesLoader类的源码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package
org.springframework.core.io.support;
import
java.io.IOException;
import
java.net.URL;
import
java.util.ArrayList;
import
java.util.Collections;
import
java.util.Enumeration;
import
java.util.Iterator;
import
java.util.List;
import
java.util.Map;
import
java.util.Properties;
import
java.util.Map.Entry;
import
org.apache.commons.logging.Log;
import
org.apache.commons.logging.LogFactory;
import
org.springframework.core.annotation.AnnotationAwareOrderComparator;
import
org.springframework.core.io.UrlResource;
import
org.springframework.lang.Nullable;
import
org.springframework.util.Assert;
import
org.springframework.util.ClassUtils;
import
org.springframework.util.ConcurrentReferenceHashMap;
import
org.springframework.util.LinkedMultiValueMap;
import
org.springframework.util.MultiValueMap;
import
org.springframework.util.ReflectionUtils;
import
org.springframework.util.StringUtils;
/**
* SpringFactoriesLoader#loadFactories设计用于加载和实例化指定类型的工厂,这些工厂类型的定义
* 来自classpath中多个JAR包内常量FACTORIES_RESOURCE_LOCATION所指定的那些spring.factories文件。
* spring.factories文件的格式必须是属性文件格式,每条属性的key必须是接口或者抽象类的全限定名,
* 而属性值value是一个逗号分割的实现类的名称。
*/
public
final
class
SpringFactoriesLoader {
/*
* 要加载的资源路径,该常量定义了该工具类要从每个jar包中提取的工厂类定义属性文件的相对路径
* 在classpath中的多个JAR中,要扫描的工厂配置文件的在本JAR包中的路径。
* 实际上,Springboot的每个 autoconfigure包都包含spring.factories这个配置文件。
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
//日志
private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
private static final Map> cache = new ConcurrentReferenceHashMap();
private SpringFactoriesLoader() {
}
/**
* @param factoryClass 工厂所属接口/抽象类全限定名称
* @param classLoader 所要使用的类加载器
*
* 该方法会读取classpath上所有的jar包中的所有 META-INF/spring.factories 属性文件,找出其中定义的匹配类型 factoryClass 的工厂类,
* 然后创建每个工厂类的对象/实例,并返回这些工厂类对象/实例的列表
*/
public static List loadFactories(Class factoryClass, @Nullable ClassLoader classLoader) {
Assert.notNull(factoryClass, "'factoryClass' must not be null");
ClassLoader classLoaderToUse = classLoader;
if (classLoader == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
//加载类型为factoryClass的工厂的名称,其实是一个个的全限定类名,使用指定的classloader:classLoaderToUse
List factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
if (logger.isTraceEnabled()) {
logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
}
List result = new ArrayList(factoryNames.size());
Iterator var5 = factoryNames.iterator();
// 实例化所加载的每个工厂类
while(var5.hasNext()) {
String factoryName = (String)var5.next();
result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
}
//对工厂类进行排序
AnnotationAwareOrderComparator.sort(result);
return result;
}
/**
*
* @param factoryClass 工厂所属接口/抽象类全限定名称
* @param classLoader 类加载器
* @return
*
* 该方法会读取classpath上所有的jar包中的所有 META-INF/spring.factories 属性文件,找出其中定义的匹配类型 factoryClass 的工厂类,
* 然后并返回这些工厂类的名字列表,注意是包含包名的全限定名。
*/
public static List loadFactoryNames(Class> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
// 1. 使用指定的classloader扫描classpath上所有的JAR包中的文件META-INF/spring.factories,加载其中的多值工厂属性定义,使用多值Map的形式返回,
// 2. 返回多值Map中key为factoryClassName的工厂名称列表,如果没有相应的entry,返回空列表而不是返回null
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
/**
* @param classLoader 类加载器
*
* 使用指定的classloader扫描classpath上所有的JAR包中的文件META-INF/spring.factories,加载其中的多值
* 工厂属性定义,使用多值Map的形式返回
**/
private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
// 扫描classpath上所有JAR中的文件META-INF/spring.factories
Enumeration urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
while(urls.hasMoreElements()) {
// 找到的每个META-INF/spring.factories文件都是一个Properties文件,将其内容
// 加载到一个 Properties 对象然后处理其中的每个属性
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry, ?> entry = (Entry)var6.next();
// 获取工厂类名称(接口或者抽象类的全限定名)
String factoryClassName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11
String factoryName = var9[var11];
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
/**
* @param instanceClassName 工厂实现类全限定名称
* @param factoryClass 工厂所属接口/抽象类全限定名称
* @param classLoader 所要使用的类加载器
**/
private
static
T instantiateFactory(String instanceClassName, Class factoryClass, ClassLoader classLoader) {
try
{
Class> instanceClass = ClassUtils.forName(instanceClassName, classLoader);
if
(!factoryClass.isAssignableFrom(instanceClass)) {
throw
new
IllegalArgumentException(
"Class ["
+ instanceClassName +
"] is not assignable to ["
+ factoryClass.getName() +
"]"
);
}
else
{
return
ReflectionUtils.accessibleConstructor(instanceClass,
new
Class[
0
]).newInstance();
}
}
catch
(Throwable var4) {
throw
new
IllegalArgumentException(
"Unable to instantiate factory class: "
+ factoryClass.getName(), var4);
}
}
}
一般情况下,springboot提供的一些JAR包里面会带有文件META-INF/spring.factories,然后在Springboot启动的时候,根据启动阶段不同的需求,框架就会多次调用SpringFactoriesLoader加载相应的工厂配置信息。
使用了注解@EnableAutoConfiguration时,就会触发对SpringFactoriesLoader.loadFactoryNames()的调用。
看一下spring.factories所在的位置:
部分内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
以上就是SpringBoot实现自动化配置的原理,或许你看到的源码会与我的不相同,那有可能是jar版本的不一致。
总结一下使用SpringBoot的好处:
(1)简化配置,不需要编写太多的xml配置文件;
(2)基于Spring构建,使开发者快速入门,门槛很低;
(3)SpringBoot可以创建独立运行的应用而不需要依赖于容器;
(4)内置tomcat服务器,不需要打包成war包,可以直接放到tomcat中运行;
(5)提供maven极简配置,以及可视化的相关监控功能,比如性能监控,应用的健康程度等;
(6)为微服务SpringCloud奠定了基础,使得微服务的构建变得简单;
(7)Spring可以整合很多各式各样的框架,并能很好的集成;
(8)活跃的社区与论坛,以及丰富的开发文档;
作者:霜花似雪
链接:http://www.imooc.com/article/287576?block_id=tuijian_wz
来源:慕课网
本文首次发布于慕课网 ,转载请注明出处,谢谢合作