本文采用SpringBoot版本2.2.4.RELEASE
一、父项目
1
2
3
4
5
6
7
8 1 <parent>
2 <groupId>org.springframework.boot</groupId>
3 <artifactId>spring-boot-starter-parent</artifactId>
4 <version>2.2.4.RELEASE</version>
5 <relativePath/>
6 </parent>
7
8
spring-boot-starter-parent的父项目:
1
2
3
4
5
6
7
8 1 <parent>
2 <groupId>org.springframework.boot</groupId>
3 <artifactId>spring-boot-dependencies</artifactId>
4 <version>2.2.4.RELEASE</version>
5 <relativePath>../../spring-boot-dependencies</relativePath>
6 </parent>
7
8
spring-boot-dependencies来真正管理SpringBoot应用里面的所有依赖版本
二、探索SpringApplication启动Spring
1
2
3
4
5
6
7
8
9
10 1@SpringBootApplication//标明是SpringBoot应用,可以开启自动配置的功能
2public class DemoApplication {
3
4 public static void main(String[] args) {
5 SpringApplication.run(DemoApplication.class, args);//启动SpringBoot应用
6 }
7
8}
9
10
1
2
3
4
5 1 public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
2 return run(new Class<?>[] { primarySource }, args);
3 }
4
5
1
2
3
4
5
6 1 public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
2 //创建SpringApplication对象,并执行运行
3 return new SpringApplication(primarySources).run(args);
4 }
5
6
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 1 public ConfigurableApplicationContext run(String... args) {
2 //创建StopWatch对象,并启动。StopWatch主要用于简单统计run启动过程的时长
3 StopWatch stopWatch = new StopWatch();
4 stopWatch.start();
5 ConfigurableApplicationContext context = null;
6 Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
7 configureHeadlessProperty();
8 //获得SpringApplicationRunListener的数组,并启动监听
9 SpringApplicationRunListeners listeners = getRunListeners(args);
10 listeners.starting();
11 try {
12 //创建ApplicationArguments对象
13 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
14 //加载属性配置。执行完成后,所有的environment的属性都会加载进来,包括application.properties和外部的属性配置
15 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
16 configureIgnoreBeanInfo(environment);
17 Banner printedBanner = printBanner(environment);
18 //创建Spring容器
19 context = createApplicationContext();
20 exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
21 new Class[] { ConfigurableApplicationContext.class }, context);
22 //调用所有初始化类的initialize方法
23 prepareContext(context, environment, listeners, applicationArguments, printedBanner);
24 //初始化Spring容器
25 refreshContext(context);
26 //执行Spring容器的初始化的后置逻辑。默认实现为空
27 afterRefresh(context, applicationArguments);
28 //停止StopWatch统计时长
29 stopWatch.stop();
30 if (this.logStartupInfo) {
31 new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
32 }
33 //通知SpringApplicationRunListener的数组,Spring容器启动完成
34 listeners.started(context);
35 //调用ApplicationRunner或者CommandLineRunner的运行方法
36 callRunners(context, applicationArguments);
37 }
38 catch (Throwable ex) {
39 handleRunFailure(context, ex, exceptionReporters, listeners);
40 throw new IllegalStateException(ex);
41 }
42
43 try {
44 //通知SpringApplicationRunListener的数组,Spring容器运行中
45 listeners.running(context);
46 }
47 catch (Throwable ex) {
48 handleRunFailure(context, ex, exceptionReporters, null);
49 throw new IllegalStateException(ex);
50 }
51 return context;
52 }
53
54
createApplicationContext()创建Spring容器:
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 1 protected ConfigurableApplicationContext createApplicationContext() {
2 //根据webApplicationType类型,获得ApplicationContext类型
3 Class<?> contextClass = this.applicationContextClass;
4 if (contextClass == null) {
5 try {
6 switch (this.webApplicationType) {
7 case SERVLET:
8 contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
9 break;
10 case REACTIVE:
11 contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
12 break;
13 default:
14 contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
15 }
16 }
17 catch (ClassNotFoundException ex) {
18 throw new IllegalStateException(
19 "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
20 }
21 }
22 //创建ApplicationContext对象
23 return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
24 }
25
26
prepareContext()调用所有初始化类的initialize方法:
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 1 private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
2 SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
3 //设置context的environment属性
4 context.setEnvironment(environment);
5 //设置context的一些属性
6 postProcessApplicationContext(context);
7 //初始化ApplicationContextInitializer
8 applyInitializers(context);
9 //通知SpringApplicationRunListener的数组,Spring容器准备完成
10 listeners.contextPrepared(context);
11 if (this.logStartupInfo) {
12 logStartupInfo(context.getParent() == null);
13 logStartupProfileInfo(context);
14 }
15 //设置beanFactory的属性
16 ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
17 beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
18 if (printedBanner != null) {
19 beanFactory.registerSingleton("springBootBanner", printedBanner);
20 }
21 if (beanFactory instanceof DefaultListableBeanFactory) {
22 ((DefaultListableBeanFactory) beanFactory)
23 .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
24 }
25 if (this.lazyInitialization) {
26 context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
27 }
28 //加载BeanDefinition
29 Set<Object> sources = getAllSources();
30 Assert.notEmpty(sources, "Sources must not be empty");
31 load(context, sources.toArray(new Object[0]));
32 //通知SpringApplicationRunListener的数组,Spring容器加载完成
33 listeners.contextLoaded(context);
34 }
35
36
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 1 protected void load(ApplicationContext context, Object[] sources) {
2 if (logger.isDebugEnabled()) {
3 logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
4 }
5 //执行BeanDefinition加载
6 BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
7 //设置loader的属性
8 if (this.beanNameGenerator != null) {
9 loader.setBeanNameGenerator(this.beanNameGenerator);
10 }
11 if (this.resourceLoader != null) {
12 loader.setResourceLoader(this.resourceLoader);
13 }
14 if (this.environment != null) {
15 loader.setEnvironment(this.environment);
16 }
17 //执行BeanDefinition加载
18 loader.load();
19 }
20
21
三、Starter自动化配置原理
@SpringBootApplication注解:
1
2
3
4
5
6
7
8
9
10
11 1@Target(ElementType.TYPE)
2@Retention(RetentionPolicy.RUNTIME)
3@Documented
4@Inherited//使用@Inherite注解声明出来的自定义注解,在使用此自定义注解时,如果注解在类上面时,子类会自动继承此注解;否则的话,子类不会继承此注解
5@SpringBootConfiguration//标记这是一个SpringBoot配置类
6@EnableAutoConfiguration//开启自动配置功能
7@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
8 @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
9public @interface SpringBootApplication {
10
11
@EnableAutoConfiguration注解:
1
2
3
4
5
6
7
8
9 1@Target(ElementType.TYPE)
2@Retention(RetentionPolicy.RUNTIME)
3@Documented
4@Inherited
5@AutoConfigurationPackage//自动配置包,它会获取主程序类所在的包路径,并将包路径(包括子包)下的所有组件注册到IOC容器中
6@Import(AutoConfigurationImportSelector.class)//导入自动配置相关的资源
7public @interface EnableAutoConfiguration {
8
9
AutoConfigurationImportSelector类:
getCandidateConfigurations()获得符合条件的配置类的数组:
1
2
3
4
5
6
7
8
9
10 1 protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
2 //加载指定类型EnableAutoConfiguration对应的,在META-INF/spring.factories里的类名的数组
3 List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
4 getBeanClassLoader());
5 Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
6 + "are using a custom packaging, make sure that file is correct.");
7 return configurations;
8 }
9
10
每一个*AutoConfiguration类加入到容器中,用他们来做自动配置
以HttpEncodingAutoConfiguration(Http编码自动配置)为例:
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 1@Configuration(proxyBeanMethods = false)//表明这是一个配置类
2@EnableConfigurationProperties(HttpProperties.class)//启用指定类的ConfigurationProperties功能;将配置文件中对应的值和HttpEncodingProperties绑定起来;并把HttpEncodingProperties加入到IOC容器中
3@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)//Spring底层@ConditionalOn注解,根据不同的条件,如果满足指定的条件,整 个配置类里面的配置才会生效; 判断当前应用是否是web应用,如果是,当前配置类生效
4@ConditionalOnClass(CharacterEncodingFilter.class)//判断当前项目有没有这个类 CharacterEncodingFilter:SpringMVC中进行乱码解决的过滤器
5@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)//判断配置文件中是否存在某个配置spring.http.encoding.enabled;如果不存在判断也是成立的
6//matchIfMissing=true:即使配置文件中不配置spring.http.encoding.enabled=true,也是默认生效的
7public class HttpEncodingAutoConfiguration {
8
9 private final HttpProperties.Encoding properties;
10
11 public HttpEncodingAutoConfiguration(HttpProperties properties) {
12 this.properties = properties.getEncoding();
13 }
14
15 @Bean//给容器中添加一个组件,这个组件的某些值需要从properties中获取
16 @ConditionalOnMissingBean
17 public CharacterEncodingFilter characterEncodingFilter() {
18 CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
19 filter.setEncoding(this.properties.getCharset().name());
20 filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
21 filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
22 return filter;
23 }
24
25
一旦配置类生效,这个配置类就会给容器中添加各种组件,这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的
1
2
3
4 1@ConfigurationProperties(prefix = "spring.http")//从配置文件中获取指定的值和bean的属性进行绑定
2public class HttpProperties {
3
4
四、Conditional机制实现
@Conditional注解根据特定条件来控制bean的创建行为
@ConditionalOnJava
系统的java版本是否符合要求
@ConditionalOnBean
容器中存在指定Bean
@ConditionalOnMissingBean
容器中不存在指定Bean
@ConditionalOnExpression
满足SpEL表达式指定
@ConditionalOnClass
系统中有指定的类
@ConditionalOnMissingClass
系统中没有指定的类
@ConditionalOnSingleCandidate
容器中只有一个指定的Bean,或者这个Bean是首选Bean
@ConditionalOnProperty
系统中指定的属性是否有指定的值
@ConditionalOnResource
类路径下是否存在指定资源文件
@ConditionalOnWebApplication
当前是web环境
@ConditionalOnNotWebApplication
当前不是web环境
@ConditionalOnJndi
JNDI存在指定项
以@ConditionalOnProperty注解为例:
1
2
3
4
5
6
7 1@Retention(RetentionPolicy.RUNTIME)
2@Target({ ElementType.TYPE, ElementType.METHOD })
3@Documented
4@Conditional(OnPropertyCondition.class)
5public @interface ConditionalOnProperty {
6
7
OnPropertyCondition类:
getMatchOutcome()方法获得匹配结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 1 @Override
2 public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
3 //获得@ConditionalOnProperty注解的属性
4 List<AnnotationAttributes> allAnnotationAttributes = annotationAttributesFromMultiValueMap(
5 metadata.getAllAnnotationAttributes(ConditionalOnProperty.class.getName()));
6 //存储匹配和不匹配的结果消息结果
7 List<ConditionMessage> noMatch = new ArrayList<>();
8 List<ConditionMessage> match = new ArrayList<>();
9 //遍历annotationAttributes属性数组,逐个判断是否匹配,并添加到结果
10 for (AnnotationAttributes annotationAttributes : allAnnotationAttributes) {
11 ConditionOutcome outcome = determineOutcome(annotationAttributes, context.getEnvironment());
12 (outcome.isMatch() ? match : noMatch).add(outcome.getConditionMessage());
13 }
14 //如果有不匹配的,则返回不匹配
15 if (!noMatch.isEmpty()) {
16 return ConditionOutcome.noMatch(ConditionMessage.of(noMatch));
17 }
18 //如果都匹配,则返回匹配
19 return ConditionOutcome.match(ConditionMessage.of(match));
20 }
21
22
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 1 private ConditionOutcome determineOutcome(AnnotationAttributes annotationAttributes, PropertyResolver resolver) {
2 //解析成Spec对象
3 Spec spec = new Spec(annotationAttributes);
4 //创建结果数组
5 List<String> missingProperties = new ArrayList<>();
6 List<String> nonMatchingProperties = new ArrayList<>();
7 //收集是否不匹配的信息,到missingProperties、nonMatchingProperties中
8 spec.collectProperties(resolver, missingProperties, nonMatchingProperties);
9 //如果有属性缺失,则返回不匹配
10 if (!missingProperties.isEmpty()) {
11 return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnProperty.class, spec)
12 .didNotFind("property", "properties").items(Style.QUOTE, missingProperties));
13 }
14 //如果有属性不匹配,则返回不匹配
15 if (!nonMatchingProperties.isEmpty()) {
16 return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnProperty.class, spec)
17 .found("different value in property", "different value in properties")
18 .items(Style.QUOTE, nonMatchingProperties));
19 }
20 //返回匹配
21 return ConditionOutcome
22 .match(ConditionMessage.forCondition(ConditionalOnProperty.class, spec).because("matched"));
23 }
24
25
参考:
《Spring源码深度解析》