目录
Spring Boot 原理解析和 SpringApplication 执行流程
官方文档:https://docs.spring.io/spring-framework/docs/current/reference/html/

Core Container核心容器包括
AOP:运行定义方法拦截器和切入点。
AspectJ 是一个功能强大且成熟的面向切面编程(AOP)框架。
ORM:对象关系映射(Object Relational Mapping,简称ORM)。
从获取对象的方式来进行比较:
传统的方式:
通过new 关键字主动创建一个对象
IOC方式
对象的生命周期由Spring来管理,直接从Spring那里去获取一个对象。 IOC是控制反转 (Inversion Of Control)的缩写,控制反转是把控制权交出去,交给Spring,通过IOC方式松耦合。
传统方式导致了硬编码耦合。

入门实例:
1.新建一个Java project,新建一个com.cxf.pojo包,Category 类
- package com.cxf.pojo;
-
- public class Category {
- private int id;
- private String name;
-
- public int getId() {
- return id;
- }
-
- public void setId(int id) {
- this.id = id;
-
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
- }
2.新建一个配置文件applicationContext.xml
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xmlns:tx="http://www.springframework.org/schema/tx"
- xmlns:context="http://www.springframework.org/schema/context"
- xsi:schemaLocation="
- http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
- http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
- http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
- http://www.springframework.org/schema/context/spring-context-3.0.xsd">
-
- <bean name="c" class="com.cxf.pojo.Category">
- <property name="name" value="category 1"/>
- </bean>
-
- </beans>
对Category的name属性注入了"category 1"字符串。
3.新建一个测试类TestSpring
- package com.cxf.test;
-
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- import com.cxf.pojo.Category;
-
- public class TestSpring {
-
- public static void main(String[] args) {
- ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"});
- Category c = (Category)context.getBean("c");
- System.out.println(c.getName());
- }
-
- }
DI (Dependency Injection)
IoC是一种设计思想,主要是为了解耦,DI是这种思想的一种实现方式。依赖注入是指在启动Spring容器加载 bean 配置的时候,完成对变量的赋值行为。
依赖注入通常有两种方式:
eg:
(1)XML方式:


(2)Java方式:

Bean的作用域

(这个模块后续再整理)
aspect-oriented programming
(这个模块后续再整理)
将普通类交给 Spring 容器管理通常有2种方法:
(1)使用 @Configuration,@Bean,@Controller,@Service,@Repository,@Component注解,然后启用 @ComponentScan 自动扫描。
(2)使用 @Import 。
Spring Boot 入口类 @SpringBootApplication 和 main 方法中 SpringApplication.run方法的解析:
- @Target({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 {
- // 省略代码
- }
@SpringBootConfiguration 是对 @Configuration 的简单“包装”,它就是Java Config形式的Spring IoC 容器的配置类:
- @Target(ElementType.TYPE)
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- @Configuration
- public @interface SpringBootConfiguration {
-
- }
@EnableAutoConfiguration:
- @Target({ElementType.TYPE})
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- @Inherited
- @AutoConfigurationPackage
- @Import({AutoConfigurationImportSelector.class})
- public @interface EnableAutoConfiguration {
-
- }
@AutoConfigurationPackage:
- @Target({ElementType.TYPE})
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- @Inherited
- @Import({Registrar.class})
- public @interface AutoConfigurationPackage {
- String[] basePackages() default {};
-
- Class<?>[] basePackageClasses() default {};
- }
Registrar:
- @Order(Ordered.HIGHEST_PRECEDENCE)
- static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
-
- @Override
- public void registerBeanDefinitions(AnnotationMetadata metadata,
- BeanDefinitionRegistry registry) {
- register(registry, new PackageImport(metadata).getPackageName());
- }
-
- @Override
- public Set<Object> determineImports(AnnotationMetadata metadata) {
- return Collections.<Object>singleton(new PackageImport(metadata));
- }
-
- }
@Import:
- @Target({ElementType.TYPE})
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- public @interface Import {
- Class<?>[] value();
- }
AutoConfigurationImportSelector:
- protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
- List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
- Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
- return configurations;
- }
从 spring.factories 中可以看到:

@Import 注解的主要作用就是借助 AutoConfigurationImportSelector 将 Spring Boot 应用所有符合条件的 @Configuration 配置都加载到当前 Spring Boot 创建并使用的 IoC 容器中,IoC 容器就是Spring 应用程序上下文 ApplicationContext。
Spring 框架提供了很多 @Enable 开头的注解定义,比如 @EnableScheduling,@EnableCaching,@EnableAsync 等,这些注解的共同点是借助 @Import 收集和注册特定场景相关的 bean定义。
@ComponentScan:启动组件扫描,组件或bean定义能被自动发现并注入Spring应用程序上下文,@Controller,@Service,@Component,@Repository等都可以被扫描到。
SpringApplication.run 的run底层代码是new SpringApplication后再调用run:
- public ConfigurableApplicationContext run(String... args) {
- long startTime = System.nanoTime();
- DefaultBootstrapContext bootstrapContext = createBootstrapContext();
- // 注意是ConfigurableApplicationContext
- ConfigurableApplicationContext context = null;
- configureHeadlessProperty();
- // 开启SpringApplicationRunListeners监听器
- SpringApplicationRunListeners listeners = getRunListeners(args);
- listeners.starting(bootstrapContext, this.mainApplicationClass);
- try {
- ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
- ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
- configureIgnoreBeanInfo(environment);
- Banner printedBanner = printBanner(environment);
- // 创建应用上下文
- context = createApplicationContext();
- context.setApplicationStartup(this.applicationStartup);
- // 准备上下文
- prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
- // 刷新上下文
- refreshContext(context);
- // 刷新后操作
- afterRefresh(context, applicationArguments);
- Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
- if (this.logStartupInfo) {
- new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
- }
- listeners.started(context, timeTakenToStartup);
- callRunners(context, applicationArguments);
- }
- catch (Throwable ex) {
- handleRunFailure(context, ex, listeners);
- throw new IllegalStateException(ex);
- }
- try {
- Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
- listeners.ready(context, timeTakenToReady);
- }
- catch (Throwable ex) {
- handleRunFailure(context, ex, null);
- throw new IllegalStateException(ex);
- }
- return context;
- }
创建、准备、刷新 ConfigurableApplicationContext,通过上下文加载应用所需的类和各种环境配置等,最后启动应用实例。
查询和加载所有的 SpringApplicationRunListener 监听器(通过 SpringFactoriesLoader 加载 META-INF/spring.factories 文件)。
SpringApplicationRunListener 规定了 Spring Boot 的生命周期,在各个生命周期广播响应的事件(ApplicationEvent),实际调用的是 ApplicationListener 类:


ApplicationListener 是 Spring 框架的监听器模式的一种实现。
- @FunctionalInterface
- public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
- void onApplicationEvent(E event);
-
- static <T> ApplicationListener<PayloadApplicationEvent<T>> forPayload(Consumer<T> consumer) {
- return (event) -> {
- consumer.accept(event.getPayload());
- };
- }
- }
prepareContext时:
创建和准备应用要使用的 Environment,包括配置要使用的 PropertySource 和 Profile。


ApplicationContextInitializer 类的 initialize方法对应用程序上下文进行设置和处理。
- @FunctionalInterface
- public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
- void initialize(C applicationContext);
- }
在调用 ApplicationContext 的 refresh 方法使 IoC容器达到可用状态后,查找当前上下文是否注册有 ApplicationRunner 与 CommandLineRunner,从 SpringApplicationRunListener 类的 started 方法的注释中可以看出:

void run(ApplicationArguments args) throws Exception; // ApplicationRunner void run(String... args) throws Exception; // CommandLineRunner ApplicationRunner 和 CommandLineRunner 都是run方法,区别就是入参不一样。
如果想在 Spring Boot 启动时运行一些特定代码,可以实现 implements ApplicationRunner 或 CommandLineRunner 接口。
以上描述的完整流程如下:

另外可借用网上的其他图辅助理解:


起步依赖本质上是一个Maven项目对象模型,定义了对其他库的传递依赖。
举个例子,mybatis-spring-boot-starter的pom.xml是:
- <?xml version="1.0" encoding="UTF-8"?>
- <!--
- Copyright 2015-2017 the original author or authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
- <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.mybatis.spring.boot</groupId>
- <artifactId>mybatis-spring-boot</artifactId>
- <version>1.3.1</version>
- </parent>
- <artifactId>mybatis-spring-boot-starter</artifactId>
- <name>mybatis-spring-boot-starter</name>
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-jdbc</artifactId>
- </dependency>
- <dependency>
- <groupId>org.mybatis.spring.boot</groupId>
- <artifactId>mybatis-spring-boot-autoconfigure</artifactId>
- </dependency>
- <dependency>
- <groupId>org.mybatis</groupId>
- <artifactId>mybatis</artifactId>
- </dependency>
- <dependency>
- <groupId>org.mybatis</groupId>
- <artifactId>mybatis-spring</artifactId>
- </dependency>
- </dependencies>
- </project>

MybatisAutoConfiguration的源代码:
- @Configuration
- @ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
- @ConditionalOnBean({DataSource.class})
- @EnableConfigurationProperties({MybatisProperties.class})
- @AutoConfigureAfter({DataSourceAutoConfiguration.class})
- public class MybatisAutoConfiguration {
- private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class);
- private final MybatisProperties properties;
- private final Interceptor[] interceptors;
- private final ResourceLoader resourceLoader;
- private final DatabaseIdProvider databaseIdProvider;
- private final List<ConfigurationCustomizer> configurationCustomizers;
-
- public MybatisAutoConfiguration(MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider, ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider, ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {
- this.properties = properties;
- this.interceptors = (Interceptor[])interceptorsProvider.getIfAvailable();
- this.resourceLoader = resourceLoader;
- this.databaseIdProvider = (DatabaseIdProvider)databaseIdProvider.getIfAvailable();
- this.configurationCustomizers = (List)configurationCustomizersProvider.getIfAvailable();
- }
-
- @PostConstruct
- public void checkConfigFileExists() {
- if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) {
- Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());
- Assert.state(resource.exists(), "Cannot find config location: " + resource + " (please add config file or check your Mybatis configuration)");
- }
-
- }
-
- @Bean
- @ConditionalOnMissingBean
- public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
- // 省略其他代码
- }
-
- // 省略其他代码
- }

DataSourceProperties 定义了数据源的各个属性,且使用 @ConfigurationProperties 指定了配置文件的前缀:
@ConfigurationProperties 的作用是把yml或properties文件转化为Bean。
Spring Boot 提供的 starter 都是以 spring-boot-starter-xxx 的方式命名的,自定义的 starter 官方建议以 xxx-spring-boot-starter 命名。
New Project => Maven => Create from archetype (maven-archetype-quickstart) =>

pom.xml添加:
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-autoconfigure</artifactId>
- </dependency>
- <dependencyManagement>
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-dependencies</artifactId>
- <version>2.1.6.RELEASE</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
- </dependencies>
- </dependencyManagement>
创建 main/resources/META-INF/spring.factories:
- # Auto Configure
- org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
- com.cxf.config.CxfStarterEnableAutoConfiguration
com.cxf.config.CxfStarterEnableAutoConfiguration:
- package com.cxf.config;
-
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
- import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
- import org.springframework.boot.context.properties.EnableConfigurationProperties;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
-
- @Configuration
- @ConditionalOnClass(HelloService.class)
- @EnableConfigurationProperties(HelloServiceProperties.class)
- public class CxfStarterEnableAutoConfiguration {
- private final HelloServiceProperties helloServiceProperties;
-
- @Autowired
- public CxfStarterEnableAutoConfiguration(HelloServiceProperties helloServiceProperties) {
- this.helloServiceProperties = helloServiceProperties;
- }
-
- @Bean
- @ConditionalOnProperty(prefix = "hello.service", name = "enable", havingValue = "true")
- HelloService helloService() {
- return new HelloService(helloServiceProperties.getPrefix(), helloServiceProperties.getSuffix());
- }
-
- }
HelloServiceProperties:
- package com.cxf.config;
-
- import org.springframework.boot.context.properties.ConfigurationProperties;
-
- @ConfigurationProperties("hello.service")
- public class HelloServiceProperties {
- private String prefix;
- private String suffix;
-
- public String getPrefix() {
- return prefix;
- }
-
- public void setPrefix(String prefix) {
- this.prefix = prefix;
- }
-
- public String getSuffix() {
- return suffix;
- }
-
- public void setSuffix(String suffix) {
- this.suffix = suffix;
- }
- }
HelloService:
- package com.cxf.config;
-
- public class HelloService {
- private String prefix;
- private String suffix;
-
- public HelloService(String prefix, String suffix) {
- this.prefix = prefix;
- this.suffix = suffix;
- }
-
- public String say(String text) {
- return String.format("%s, hi, %s, %s", prefix, text, suffix);
- }
- }
当项目依赖该 starter 且配置文件中包含 hello.service前缀且hello.service.enable为true时,自动生成HelloService的Bean。
在别的 Spring Boot 项目中的pom.xml引入:
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>cxf-spring-boot-starter</artifactId>
- <version>1.0-SNAPSHOT</version>
- </dependency>
在application.properties(或对应的application.yml)中配置:
- hello.service.enable=true
- hello.service.prefix=pre
- hello.service.suffix=suf
Spring 核心代码可参考阅读《SPRING技术内幕:深入解析SPRING架构与设计原理 第2版》(计文柯著)、《Spring源码深度解析 第2版》(郝佳著)、《Spring 5核心原理与30个类手写实战》(谭勇德著)、《Spring Boot编程思想》(小马哥著)等书籍和B站的Spring源码分析视频。
用300行代码手写提炼Spring核心原理和设计思想,并保证功能可用的例子:GitHub - gupaoedu-tom/spring5-samples: 《Spring5核心原理与30个类手写实战》随书代码示例工程
Java配置通过@Configuration和@Bean注解实现:
- @Configuration
- public class AppConfig {
- @Bean
- public MyService myService() {
- return my MyServiceImpl();
- }
- }
与上面相同的XML配置如下:
- <beans>
- <bean id="myService" class="com.cxf.services.MyServiceImpl" />
- </beans>
(这部分后续再整理)