说明:

本系列基于SpringBoot 2.2.9.RELEASE 版本,对SpringBoot的原理进行分析,一共分为四节:

SpringBoot原理发现(一):创建Hello World,对pom依赖以及@SpringBootApplication注解进行分析

SpringBoot原理发现(二):分析SpringBoot自动配置原理

SpringBoot原理发现(三):通过主配置类main方法分析SpringBoot启动配置原理

SpringBoot原理发现(四):了解SpringBoot启动中的几个重要回调机制

Hello World

创建Hello World,主启动类如下:

@SpringBootApplication
public class DemoApplication { public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
} @RestController
public class HelloController{ @GetMapping("/")
public String helloWorld(){
return "hello world";
}
}
}

1. POM文件

 1.1 父POM

<!-- 当前项目pom -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.9.RELEASE</version>
<relativePath/>
</parent> <!-- spring-boot-starter-parent 的父pom -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.9.RELEASE</version>
<relativePath>../../spring-boot-dependencies</relativePath>
</parent>

  可以看出当前项目pom中的parent 依赖了 spring-boot-dependencies,而spring-boot-dependencies中管理的就是SpringBoot应用里面的所有依赖版本。(如果其中没包含,需要自定义在父pom中进行依赖)

<!-- spring-boot-dependencies 部分代码 -->
<properties>
<activemq.version>5.15.13</activemq.version>
<antlr2.version>2.7.7</antlr2.version>
<appengine-sdk.version>1.9.81</appengine-sdk.version>
<artemis.version>2.10.1</artemis.version>
<aspectj.version>1.9.6</aspectj.version>
<assertj.version>3.13.2</assertj.version>
<atomikos.version>4.0.6</atomikos.version>
<awaitility.version>4.0.3</awaitility.version>
<bitronix.version>2.1.4</bitronix.version>

  .......

 1.2  启动器

    SpringBoot将所有的功能场景都抽取成一个个的starter,只需要依赖某个starter,便有了相关依赖信息及功能,这样的starter称之为场景启动器。

  可以通过点击官网进行查看包含哪些starter,如下图:

  

  如开发web应用,只需要引入spring-boot-starter-web便有了web模块的相关功能

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency> ......

  spring-boot-starter-web:管理了web模块需要引用的依赖文件,如下图:

<!-- spring-boot-starter-web.pom部分依赖-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.2.9.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>2.2.9.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.2.9.RELEASE</version>
<scope>compile</scope>
</dependency>

2.主程序分析

@SpringBootApplication
public class DemoApplication { public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
} @RestController
public class HelloController{ @GetMapping("/")
public String helloWorld(){
return "hello world";
}
}
}

  2.1 @SpringBootApplication

   该注解标注在某个类上,说明这个类是SpringBoot的主配置类,SpringBoot应用应该使用这个类的main方法进行启动。

   进入该注解,可以看到@SpringBootApplication是一个组合注解,代码如下:

@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 {

   2. 1.1 @SpringBootConfiguration

    SpringBoot的配置类。进去会发现其实就是一个Spring的配置类@Configuration,而@Configuration其实就是一个组件@Component。

    @SpringBootConfiguration是SpringBoot提供的,而@Configuration是Spring提供的

    代码如下:

/**
* SpringBootConfiguration
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration { ..... /**
* Configuration
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {

  2 .2 @EnableAutoConfiguration

    作用:开启自动配置功能

    所谓开启自动配置:以前需要提供各种配置文件,如包扫描等,现在使用@EnableAutoConfiguration标注,SpringBoot就会开启自动配置功能。

    点开这个注解,会发现它同样是组合注解,如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

    2.2.1 @AutoConfigurationPackage

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage { }
@Import(AutoConfigurationPackages.Registrar.class):向容器中导入一个组件,组件由@Import后面传入的类指定
AutoConfigurationPackages.Registrar.class 关键代码如下:
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

        @Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}

  通过debug可以查看该注解是标注是在主启动类上,获取主启动类所在的包位置路径,将该路径下面的所有包扫描注入到容器中。类似spring配置文件xml中 <context:component-scan base-package="xx.yyy" />功能。

@AutoConfigurationPackage 其实就是自动获取主配置类下面的所有包并扫描注入进IOC容器中。

    2.2.2 @Import(AutoConfigurationImportSelector.class)

    同样是往IOC容器中导入AutoConfigurationImportSelector.class中指定的组件。

    AutoConfigurationImportSelector主要代码:

protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}

  通过debug可以看出,它会返回各种XXXAutoConfiguration。而有了这些XXXAutoConfiguration,就不需要我们在手动去编写相关场景的注入功能组件等工作,如下图:

这些XXXAutoConfiguration又是从何而来呢?
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
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;
}

SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader())
 第一个参数:EnableAutoConfiguration.class
 第二个参数:ClassLoader

进入loadFactoryNames方法,SpringBoot会扫描类路径下的META-INF/spring.factories 文件中定义的数据,并返回第一个参数EnableAutoConfiguration指定的值,并将这些值作为自动配置类导入到容器中,
 在后续该方法会经常出现,用于扫描spring.factories中定义的值,如下图:

  

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\ ......

  至此基本上可以看出SpringBoot是如何进行加载且工作的。说到底spring的配置文件一个也没少,只是SpringBoot底层将我们的配置文件进行自动配置到容器中了,少了手动编写大量的组件配置。

SpringBoot原理发现(一)的更多相关文章

  1. 深入springboot原理——动手封装一个starter

    从上一篇文章<深入springboot原理——一步步分析springboot启动机制(starter机制)> 我们已经知道springboot的起步依赖与自动配置的机制.spring-bo ...

  2. 深入springboot原理——一步步分析springboot启动机制(starter机制)

    前言 使用过springboot的同学应该已经知道,springboot通过默认配置了很多框架的使用方式帮我们大大简化了项目初始搭建以及开发过程.本文的目的就是一步步分析springboot的启动过程 ...

  3. Spring-boot原理(附带实现一个spring-boot-starter实例和代码下载)

    ​ (我就是个封面) Spring-boot自出现后,到现在火的很,大家貌似都在用,连招聘里面也要求会这个.但是说实话,spring-boot无外乎想实现一种可插拔的编程方式,说是简化配置,其实并没有 ...

  4. SpringBoot原理讲解

    一.问题的引入 首先我们来看一个最简单的例子. 我们先创建一个SpringBoot的工程,如何创建一个SpringBoot工程就不说了,不会请自行解决.然后写一个controller类,通过请求路径, ...

  5. SpringBoot原理分析与配置

    1.1 起步依赖原理分析 1.1.1 分析spring-boot-starter-parent 按住Ctrl点击pom.xml中的spring-boot-starter-parent,跳转到了spri ...

  6. SpringBoot原理—分析SpringBoot启动机制(starter机制)

    一:前言使用过springboot的同学应该已经知道,springboot通过默认配置了很多框架的使用方式帮我们大大简化了项目初始搭建以及开发过程.本文的目的就是一步步分析springboot的启动过 ...

  7. SpringBoot原理深入及源码剖析(一) 依赖管理及自动配置

    前言 传统的Spring框架实现一个Web服务需要导入各种依赖jar包,然后编写对应的XML配置文件等,相较而言,SpringBoot显得更加方便.快捷和高效.那么,SpringBoot究竟是如何做到 ...

  8. Springboot原理

    1. SpringBoot特点 一个starter导入所有 依赖管理 父项目做依赖管理:声明了所需依赖的版本号 依赖管理 <parent> <groupId>org.sprin ...

  9. Spring-Boot原理及应用布署

    一.Spring Boot的理念 从最根本上来讲,Spring Boot就是一些库的集合,它能够被任意项目的构建系统所使用.简便起见,该框架也提供了命令行界面,它可以用来运行和测试Boot应用.框架的 ...

随机推荐

  1. PHP_加密解密字符串

    PHP_加密解密字符串.php <?php //加解密字符串函数,可以加密中文 /* //加密 echo $encode = authcode('爱迪生', 'ENCODE', '3'); // ...

  2. Spring Boot第四弹,一文教你如何无感知切换日志框架?

    持续原创输出,点击上方蓝字关注我吧 目录 前言 Spring Boot 版本 什么是日志门面? 如何做到无感知切换? 如何切换? 引入依赖 指定配置文件 日志如何配置? 总结 前言 首先要感谢一下读者 ...

  3. PageObject课程培训记录

    前言 昨晚的培训课程讲了PO设计模式,对于PO模式我们需要去了解关于为什么要使用PO,而不使用PO是什么情况?什么是PO模式?PO怎么去使用? 第一,为什么要使用PO,而不使用PO是什么情况? 我们先 ...

  4. # mac使用homebrew安装jdk和tomcat

    homebrew安装jdk 前提是已经安装homebrew //直接命令行安装 brew cask install homebrew/cask-versions/adoptopenjdk8 homeb ...

  5. MySQL触发器初试:当A表插入新记录,自动在B表中插入相同ID的记录

    今天第一次用MySQL的触发器,怕忘了,赶紧写篇博客记录一下. 废话不说,先上语法: 1 CREATE TRIGGER trigger_name 2 { BEFORE | AFTER } { INSE ...

  6. java泛型之通配符?

    一.在说泛型通配符" ?" 之前先讲几个概念 1.里氏替换原则(Liskov Substitution Principle, LSP): 定义:所有引用基类(父类)的地方必须能透明 ...

  7. go分库分表 主从分离例子

    网上有很多介绍分库分表的文章,方法很多: 分区表切分 垂直切分 水平切分 区间切分 取模切分 这里不细说 分库分表简单,但后期会带来一系列的难题: 事务 Join 分页 数据库: master和sla ...

  8. 《C++primerplus》第8章练习题

    1.(简单用一下引用变量,没有采用书中的题目)定义一个替身结构体,存储名字(char[])和力量值(int).使用结构体引用作为形参写两个函数,一个不加const,使得能对定义的结构体做修改,另一个加 ...

  9. 怀疑安装MySQL之后,导致OrCAD Capture、Allegro就打不开

    记得在异常出现之前,只安装了MySQL,之后OrCAD Capture.Allegro就打不开了. Capture.exe - 系统错误 allegro.exe - 系统错误 我尝试在Cadence的 ...

  10. java高级项目 jdbc与数据库连接数据库

    //图书管类 public class Book { private Integer id; private String b_name; private double b_price; privat ...