Skip to content

Commit 63c28e2

Browse files
committed
springboot source code analysis
1 parent fa967a3 commit 63c28e2

13 files changed

+7124
-0
lines changed

docs/Spring全家桶/SpringBoot/springboot打包与启动.md

Lines changed: 1388 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 382 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,382 @@
1+
springboot 在启动类上会标注一个注解:`@SpringBootApplication`,本人将从源码解析分析这 个注解的作用。
2+
3+
`@SpringBootApplication` 代码如下:
4+
5+
```
6+
@Target(ElementType.TYPE)
7+
@Retention(RetentionPolicy.RUNTIME)
8+
@Documented
9+
@Inherited
10+
@SpringBootConfiguration
11+
@EnableAutoConfiguration
12+
@ComponentScan(excludeFilters = {
13+
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
14+
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
15+
public @interface SpringBootApplication {
16+
17+
/**
18+
* 自动装配要排除的类,功能来自于 @EnableAutoConfiguration
19+
*/
20+
@AliasFor(annotation = EnableAutoConfiguration.class)
21+
Class<?>[] exclude() default {};
22+
23+
/**
24+
* 自动装配要排除的类名,功能来自于 @EnableAutoConfiguration
25+
*/
26+
@AliasFor(annotation = EnableAutoConfiguration.class)
27+
String[] excludeName() default {};
28+
29+
/**
30+
* 配置扫描的包,功能来自于 @ComponentScan
31+
*/
32+
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
33+
String[] scanBasePackages() default {};
34+
35+
/**
36+
* 配置扫描的class,该class所在的包都会被扫描,功能来自于 @ComponentScan
37+
*/
38+
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
39+
Class<?>[] scanBasePackageClasses() default {};
40+
41+
/**
42+
* 是否启用 @Bean 方法代理,功能来自于 @Configuration
43+
*/
44+
@AliasFor(annotation = Configuration.class)
45+
boolean proxyBeanMethods() default true;
46+
47+
}
48+
49+
```
50+
51+
1. `@SpringBootApplication` 是一个组合注解,包含了 `@SpringBootConfiguration``@EnableAutoConfiguration``@ComponentScan` 三个注解的功能;
52+
2. `@SpringBootApplication` 中也提供了一些配置属性,而这些属性来自于以上三个注解。
53+
54+
接下来我们来看看这三个注解的作用分别是什么。
55+
56+
### 1. `@SpringBootConfiguration`
57+
58+
进入 `@SpringBootConfiguration`,代码如下:
59+
60+
```
61+
@Target(ElementType.TYPE)
62+
@Retention(RetentionPolicy.RUNTIME)
63+
@Documented
64+
@Configuration
65+
public @interface SpringBootConfiguration {
66+
67+
@AliasFor(annotation = Configuration.class)
68+
boolean proxyBeanMethods() default true;
69+
70+
}
71+
72+
```
73+
74+
这个注解比较简单,上面标记了 `@Configuration`,然后是一个属性 `proxyBeanMethods()`,它来自于 `@Configuration`。因此,`@SpringBootConfiguration` 并没有做什么,仅仅只是将 `@Configuration` 使用了 `@Configuration` 的功能。
75+
76+
关于 `@Configuration`,它来自于 spring,能被 spring 识别为 `Component`,且 `proxyBeanMethods != false` 时,会被 spring 标记为 `Full` 配置类,在后续对其中的 `@Bean` 方法处理时,会进行 cglib 代理,关于这方面的内容,可参考 [ConfigurationClassPostProcessor(二):处理 @Bean 注解](https://my.oschina.net/funcy/blog/4492878).
77+
78+
### 2. `@EnableAutoConfiguration`
79+
80+
`@EnableAutoConfiguration` 主要 用来开启自动装配功能,代码如下:
81+
82+
```
83+
@Target(ElementType.TYPE)
84+
@Retention(RetentionPolicy.RUNTIME)
85+
@Documented
86+
@Inherited
87+
// 自动装配的包
88+
@AutoConfigurationPackage
89+
// 引入的自动装配类
90+
@Import(AutoConfigurationImportSelector.class)
91+
public @interface EnableAutoConfiguration {
92+
93+
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
94+
95+
/**
96+
* 可自行定义排除自动装配的类
97+
*/
98+
Class<?>[] exclude() default {};
99+
100+
/**
101+
* 可自行定义排除自动装配的类名
102+
*/
103+
String[] excludeName() default {};
104+
105+
}
106+
107+
```
108+
109+
从代码中可以看到,
110+
111+
1. 该注解组合了 `@AutoConfigurationPackage` 注解的功能,该注解用来指定自动装配的包;
112+
2. 该注解通过 `@Import` 注解引入了一个类 `AutoConfigurationImportSelector`,这个类是自动装配的关键;
113+
3. 该注解提供了两个配置,用来排除指定的自动装配类,可以根据类来排除 (`Class` 对象),也可以根据类名 (`包名.类名`) 排除。
114+
115+
接下来我们来关注 `@AutoConfigurationPackage` 及引入的 `AutoConfigurationImportSelector`
116+
117+
#### 2.1 `@AutoConfigurationPackage`
118+
119+
`@AutoConfigurationPackage` 指定了自动装配的包,代码如下:
120+
121+
```
122+
@Target(ElementType.TYPE)
123+
@Retention(RetentionPolicy.RUNTIME)
124+
@Documented
125+
@Inherited
126+
@Import(AutoConfigurationPackages.Registrar.class)
127+
public @interface AutoConfigurationPackage {
128+
129+
}
130+
131+
```
132+
133+
这个注解的内容非常简单,仅使用 `@Import` 注解引入了 `AutoConfigurationPackages.Registrar`,我们来看下它的内容:
134+
135+
```
136+
public abstract class AutoConfigurationPackages {
137+
138+
private static final String BEAN = AutoConfigurationPackages.class.getName();
139+
140+
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
141+
142+
/**
143+
* 根据 ImportBeanDefinitionRegistrar 的处理,spring将调用 registerBeanDefinitions() 注册内容
144+
*/
145+
@Override
146+
public void registerBeanDefinitions(AnnotationMetadata metadata,
147+
BeanDefinitionRegistry registry) {
148+
register(registry, new PackageImport(metadata).getPackageName());
149+
}
150+
151+
@Override
152+
public Set<Object> determineImports(AnnotationMetadata metadata) {
153+
return Collections.singleton(new PackageImport(metadata));
154+
}
155+
156+
}
157+
158+
/**
159+
* 处理具体的注册操作
160+
* 1\. 如果 beanFacotry 中包含 BEAN,则将传入的包名添加到 BEAN 对应的 BeanDefinition 的构造方法参数值上;
161+
* 2\. 如果 beanFacotry 中不包含 BEAN,则创建 beanDefinition,设置参数值,然后将其注册到 beanFacotry。
162+
* 注册到beanFacotry中的bean为BasePackages
163+
*/
164+
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
165+
if (registry.containsBeanDefinition(BEAN)) {
166+
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
167+
// bean 是 BasePackages,构造方法是 BasePackages(String... names),这里获取原本的构造参数的值
168+
ConstructorArgumentValues constructorArguments
169+
= beanDefinition.getConstructorArgumentValues();
170+
// 将原本的构造参数值,以及传入的 packageNames 统一添加到构造方法的第0个参数值上
171+
constructorArguments.addIndexedArgumentValue(0,
172+
addBasePackages(constructorArguments, packageNames));
173+
}
174+
else {
175+
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
176+
// 设置BeanClass为BasePackages.class
177+
beanDefinition.setBeanClass(BasePackages.class);
178+
// 设置构造方法的参数值
179+
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
180+
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
181+
registry.registerBeanDefinition(BEAN, beanDefinition);
182+
}
183+
}
184+
185+
/**
186+
* packageName 的包装类
187+
* packageName 是传入类所在的包名,在PackageImport的构造方法中获取
188+
*/
189+
private static final class PackageImport {
190+
191+
private final String packageName;
192+
193+
PackageImport(AnnotationMetadata metadata) {
194+
// 获取传入类所在包名
195+
this.packageName = ClassUtils.getPackageName(metadata.getClassName());
196+
}
197+
198+
String getPackageName() {
199+
return this.packageName;
200+
}
201+
202+
// 省略 equals/toString/hashCode 方法
203+
...
204+
205+
}
206+
207+
/**
208+
* 注解到 beanFactory 中的类
209+
* 该类中有一个List结构,用来保存包扫描路径
210+
*/
211+
static final class BasePackages {
212+
// 包扫描路径在这里保存
213+
private final List<String> packages;
214+
215+
private boolean loggedBasePackageInfo;
216+
217+
BasePackages(String... names) {
218+
List<String> packages = new ArrayList<>();
219+
for (String name : names) {
220+
if (StringUtils.hasText(name)) {
221+
packages.add(name);
222+
}
223+
}
224+
this.packages = packages;
225+
}
226+
227+
// 省略了一些代码
228+
...
229+
}
230+
231+
}
232+
233+
```
234+
235+
代码有点长,但逻辑并不复杂,流程如下:
236+
237+
1. `AutoConfigurationPackages.Registrar` 实现了 `ImportBeanDefinitionRegistrar``registerBeanDefinitions(...)` 方法向 spring 中注册了 `BasePackages`,注册逻辑在 `AutoConfigurationPackages#register` 方法中;
238+
2. `AutoConfigurationPackages#register` 方法的注册逻辑为,先判断是否已注册了 `BasePackages`,如果注册了,就将当前类所在的包添加到 `BasePackages` 的构造方法参数值中,否则就创建 `BeanDefinition`,设置构造方法的参数值,然后注册到 spring 中;
239+
240+
#### 2.2 `AutoConfigurationImportSelector`
241+
242+
`AutoConfigurationImportSelector` 是处理自动配置的关键,代码如下:
243+
244+
```
245+
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
246+
247+
...
248+
249+
}
250+
251+
```
252+
253+
`AutoConfigurationImportSelector` 实现了 `DeferredImportSelector`,这是一个 `ImportSelector` 类,但处理的优先级最低 (在 `@ComponentScan``@Component``@Bean``@Configuration` 及其他 `@Import` 注解处理完之后再处理),在 `AutoConfigurationImportSelector` 类中会处理自动配置类的加载流程,正是通过这种方式,将自动配置类引入了 spring 容器中。
254+
255+
关于 spring 对 `@Import` 的处理,可以参考 [ConfigurationClassPostProcessor 之处理 @Import 注解](https://my.oschina.net/funcy/blog/4678152).
256+
257+
关于 `AutoConfigurationImportSelector` 获取自动配置类的流程,将在后面的文章中具体分析,本文就不展开了。
258+
259+
### 3. `@ComponentScan`
260+
261+
这个注解想必大家已经很熟悉了,它指定了包扫描路径,如果不指定,就扫描所在类的包,关于这些,在 [ConfigurationClassPostProcessor 之处理 @ComponentScan 注解](https://my.oschina.net/funcy/blog/4836178)一文中已经详细分析过了,就不再分析了。
262+
263+
本文我们来分析这个注解属性中使用的 2 个类:
264+
265+
![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/up-19d14d3d8262eead434d5ca09369e1789d5.png)
266+
267+
#### 3.1 `TypeExcludeFilter`
268+
269+
这个类表示在进行包扫描时,可以排除一些类,代码如下:
270+
271+
```
272+
public class TypeExcludeFilter implements TypeFilter, BeanFactoryAware {
273+
274+
private BeanFactory beanFactory;
275+
276+
private Collection<TypeExcludeFilter> delegates;
277+
278+
@Override
279+
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
280+
this.beanFactory = beanFactory;
281+
}
282+
283+
@Override
284+
public boolean match(MetadataReader metadataReader,
285+
MetadataReaderFactory metadataReaderFactory) throws IOException {
286+
if (this.beanFactory instanceof ListableBeanFactory
287+
&& getClass() == TypeExcludeFilter.class) {
288+
// getDelegates() 获取当前容器中所有的 TypeExcludeFilter 实例
289+
// 可以自主继承 TypeExcludeFilter,自定义匹配规则
290+
for (TypeExcludeFilter delegate : getDelegates()) {
291+
if (delegate.match(metadataReader, metadataReaderFactory)) {
292+
return true;
293+
}
294+
}
295+
}
296+
return false;
297+
}
298+
299+
private Collection<TypeExcludeFilter> getDelegates() {
300+
Collection<TypeExcludeFilter> delegates = this.delegates;
301+
if (delegates == null) {
302+
delegates = ((ListableBeanFactory) this.beanFactory)
303+
.getBeansOfType(TypeExcludeFilter.class).values();
304+
this.delegates = delegates;
305+
}
306+
return delegates;
307+
}
308+
309+
....
310+
311+
```
312+
313+
从代码上来看,如果要排除一些 类,我们可以自主继承 `TypeExcludeFilter` 类,然后重写 `match(...)` 方法,在其中定义匹配逻辑。
314+
315+
#### 3.1 `AutoConfigurationExcludeFilter`
316+
317+
`AutoConfigurationExcludeFilter` 用来排除自动配置类,也就是说,spring 在进行包扫描时,不会扫描自动配置类,代码如下:
318+
319+
```
320+
public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoaderAware {
321+
322+
private ClassLoader beanClassLoader;
323+
324+
private volatile List<String> autoConfigurations;
325+
326+
@Override
327+
public void setBeanClassLoader(ClassLoader beanClassLoader) {
328+
this.beanClassLoader = beanClassLoader;
329+
}
330+
331+
@Override
332+
public boolean match(MetadataReader metadataReader,
333+
MetadataReaderFactory metadataReaderFactory) throws IOException {
334+
// isConfiguration(...):当前类是否被 @Configuration 标记
335+
// isAutoConfiguration(...):当前类是否为自动配置类
336+
return isConfiguration(metadataReader) && isAutoConfiguration(metadataReader);
337+
}
338+
339+
private boolean isConfiguration(MetadataReader metadataReader) {
340+
return metadataReader.getAnnotationMetadata().isAnnotated(Configuration.class.getName());
341+
}
342+
343+
private boolean isAutoConfiguration(MetadataReader metadataReader) {
344+
// 获取所有的自动配置类,然后判断当前类是否存在于其中
345+
return getAutoConfigurations().contains(metadataReader.getClassMetadata().getClassName());
346+
}
347+
348+
protected List<String> getAutoConfigurations() {
349+
if (this.autoConfigurations == null) {
350+
this.autoConfigurations = SpringFactoriesLoader
351+
.loadFactoryNames(EnableAutoConfiguration.class, this.beanClassLoader);
352+
}
353+
return this.autoConfigurations;
354+
}
355+
356+
}
357+
358+
```
359+
360+
我们主要看 `match(...)` 方法,它的匹配的类为:
361+
362+
1.`@Configuration` 标记;
363+
2. 是自动配置类。
364+
365+
满足以上两个条件,spring 就不会对其进行扫描处理。
366+
367+
那什么是自动配置类呢?从 `isAutoConfiguration(...)` 可以看到,在判断是否为自动配置类上,springboot 先使用 `SpringFactoriesLoader` 加载所有配置类,然后再判断传入的类是否为其中之一。从这里可以看出,自动配置类并不进行包扫描操作。
368+
369+
关于 `SpringFactoriesLoader` 如何加载配置类,后面的文章会详细分析。
370+
371+
### 4\. 总结
372+
373+
本文主要分析 `@SpringBootApplication` 的功能,总结如下:
374+
375+
1. `@SpringBootApplication` 是一个组合注解,包含了 `@SpringBootConfiguration``@EnableAutoConfiguration``@ComponentScan` 三个注解的功能,同时提供了一些属性配置,也是来自于以上 3 个注解;
376+
2. `@SpringBootConfiguration` 包含了 `Configuration` 注解的功能;
377+
3. `@EnableAutoConfiguration` 是开启自动装配的关键注解,其中标记了 `@AutoConfigurationPackage`,会将被 `@SpringBootApplication` 标记的类所在的包,包装成 `BasePackages`,然后注册到 spring 容器中;`@EnableAutoConfiguration` 还通过 `@Import` 注解向容器中引入了 `AutoConfigurationImportSelector`,该类会将当前项目支持的自动配置类添加到 spring 容器中;
378+
4. `@ComponentScan` 定义了包扫描路径,其 `excludeFilters` 值可以用来排除类的扫描,springboot 指定了 `TypeExcludeFilter`,表明我们可以继承该类来自主定义排除的类 ;同时也指定了 `AutoConfigurationExcludeFilter` ,该 `Filter` 可以用来排除自动配置类,也就是说,自动配置类不会进行包描述操作。
379+
380+
* * *
381+
382+
_本文原文链接:[https://my.oschina.net/funcy/blog/4870882](https://my.oschina.net/funcy/blog/4870882) ,限于作者个人水平,文中难免有错误之处,欢迎指正!原创不易,商业转载请联系作者获得授权,非商业转载请注明出处。_

0 commit comments

Comments
 (0)