SpringBoot原理分析

释放双眼,带上耳机,听听看~!

本文采用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

SpringBoot原理分析

每一个*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源码深度解析》

给TA打赏
共{{data.count}}人
人已打赏
安全技术

c++ list, vector, map, set 区别与用法比较

2022-1-11 12:36:11

安全运维

Linux从用户层到内核层系列 - TCP/IP协议栈部分系列11: 再话Linux系统调用

2021-8-18 16:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索