在项目中继承 spring-boot-starter-parent 即可。
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>${springboot.version}version>
parent>
在 spring-boot-starter-parent 项目的内部还继承了另外一个父项目 spring-boot-dependencies 。

通过依赖管理的方式,引入Springboot的依赖 spring-boot-dependencies。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenciesartifactId>
<version>${springboot.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
在 spring-boot-starter-parent 的父项目 spring-boot-dependencies 中声明几乎所有开发中常用依赖的版本号。所以在Springboot项目中无需关注版本号,自动版本匹配。
<properties>
<activemq.version>5.16.5activemq.version>
<antlr2.version>2.7.7antlr2.version>
<appengine-sdk.version>1.9.97appengine-sdk.version>
<artemis.version>2.19.1artemis.version>
<aspectj.version>1.9.7aspectj.version>
<assertj.version>3.21.0assertj.version>
<atomikos.version>4.0.6atomikos.version>
<awaitility.version>4.1.1awaitility.version>
<build-helper-maven-plugin.version>3.2.0build-helper-maven-plugin.version>
<byte-buddy.version>1.11.22byte-buddy.version>
<caffeine.version>2.9.3caffeine.version>
<cassandra-driver.version>4.13.0cassandra-driver.version>
<classmate.version>1.5.1classmate.version>
<commons-codec.version>1.15commons-codec.version>
<commons-dbcp2.version>2.9.0commons-dbcp2.version>
<commons-lang3.version>3.12.0commons-lang3.version>
<commons-pool.version>1.6commons-pool.version>
<commons-pool2.version>2.11.1commons-pool2.version>
<couchbase-client.version>3.2.7couchbase-client.version>
<db2-jdbc.version>11.5.7.0db2-jdbc.version>
<dependency-management-plugin.version>1.0.11.RELEASEdependency-management-plugin.version>
<derby.version>10.14.2.0derby.version>
<dropwizard-metrics.version>4.2.10dropwizard-metrics.version>
<ehcache.version>2.10.9.2ehcache.version>
<ehcache3.version>3.9.9ehcache3.version>
<elasticsearch.version>7.15.2elasticsearch.version>
<embedded-mongo.version>3.0.0embedded-mongo.version>
<flyway.version>8.0.5flyway.version>
<freemarker.version>2.3.31freemarker.version>
<git-commit-id-plugin.version>4.9.10git-commit-id-plugin.version>
<glassfish-el.version>3.0.4glassfish-el.version>
<glassfish-jaxb.version>2.3.6glassfish-jaxb.version>
<glassfish-jstl.version>1.2.6glassfish-jstl.version>
<groovy.version>3.0.11groovy.version>
<gson.version>2.8.9gson.version>
<h2.version>1.4.200h2.version>
<hamcrest.version>2.2hamcrest.version>
<hazelcast.version>4.2.5hazelcast.version>
<hazelcast-hibernate5.version>2.2.1hazelcast-hibernate5.version>
<hibernate.version>5.6.9.Finalhibernate.version>
<hibernate-validator.version>6.2.3.Finalhibernate-validator.version>
<hikaricp.version>4.0.3hikaricp.version>
<hsqldb.version>2.5.2hsqldb.version>
<htmlunit.version>2.54.0htmlunit.version>
<httpasyncclient.version>4.1.5httpasyncclient.version>
<httpclient.version>4.5.13httpclient.version>
<httpclient5.version>5.1.3httpclient5.version>
<httpcore.version>4.4.15httpcore.version>
<httpcore5.version>5.1.3httpcore5.version>
<infinispan.version>12.1.12.Finalinfinispan.version>
<influxdb-java.version>2.22influxdb-java.version>
<jackson-bom.version>2.13.3jackson-bom.version>
<jakarta-activation.version>1.2.2jakarta-activation.version>
<jakarta-annotation.version>1.3.5jakarta-annotation.version>
<jakarta-jms.version>2.0.3jakarta-jms.version>
<jakarta-json.version>1.1.6jakarta-json.version>
<jakarta-json-bind.version>1.0.2jakarta-json-bind.version>
<jakarta-mail.version>1.6.7jakarta-mail.version>
<jakarta-management.version>1.1.4jakarta-management.version>
<jakarta-persistence.version>2.2.3jakarta-persistence.version>
<jakarta-servlet.version>4.0.4jakarta-servlet.version>
<jakarta-servlet-jsp-jstl.version>1.2.7jakarta-servlet-jsp-jstl.version>
<jakarta-transaction.version>1.3.3jakarta-transaction.version>
<jakarta-validation.version>2.0.2jakarta-validation.version>
<jakarta-websocket.version>1.1.2jakarta-websocket.version>
<jakarta-ws-rs.version>2.1.6jakarta-ws-rs.version>
<jakarta-xml-bind.version>2.3.3jakarta-xml-bind.version>
<jakarta-xml-soap.version>1.4.2jakarta-xml-soap.version>
<jakarta-xml-ws.version>2.3.3jakarta-xml-ws.version>
<janino.version>3.1.7janino.version>
<javax-activation.version>1.2.0javax-activation.version>
<javax-annotation.version>1.3.2javax-annotation.version>
<javax-cache.version>1.1.1javax-cache.version>
<javax-jaxb.version>2.3.1javax-jaxb.version>
<javax-jaxws.version>2.3.1javax-jaxws.version>
<javax-jms.version>2.0.1javax-jms.version>
<javax-json.version>1.1.4javax-json.version>
<javax-jsonb.version>1.0javax-jsonb.version>
<javax-mail.version>1.6.2javax-mail.version>
<javax-money.version>1.1javax-money.version>
<javax-persistence.version>2.2javax-persistence.version>
<javax-transaction.version>1.3javax-transaction.version>
<javax-validation.version>2.0.1.Finaljavax-validation.version>
<javax-websocket.version>1.1javax-websocket.version>
<jaxen.version>1.2.0jaxen.version>
<jaybird.version>4.0.6.java8jaybird.version>
<jboss-logging.version>3.4.3.Finaljboss-logging.version>
<jdom2.version>2.0.6.1jdom2.version>
<jedis.version>3.7.1jedis.version>
<jersey.version>2.35jersey.version>
<jetty-el.version>9.0.52jetty-el.version>
<jetty-jsp.version>2.2.0.v201112011158jetty-jsp.version>
<jetty-reactive-httpclient.version>1.1.11jetty-reactive-httpclient.version>
<jetty.version>9.4.48.v20220622jetty.version>
<jmustache.version>1.15jmustache.version>
<johnzon.version>1.2.18johnzon.version>
<jolokia.version>1.7.1jolokia.version>
<jooq.version>3.14.16jooq.version>
<json-path.version>2.6.0json-path.version>
<json-smart.version>2.4.8json-smart.version>
<jsonassert.version>1.5.0jsonassert.version>
<jstl.version>1.2jstl.version>
<jtds.version>1.3.1jtds.version>
<junit.version>4.13.2junit.version>
<junit-jupiter.version>5.8.2junit-jupiter.version>
<kafka.version>3.0.1kafka.version>
<kotlin.version>1.6.21kotlin.version>
<kotlin-coroutines.version>1.5.2kotlin-coroutines.version>
<lettuce.version>6.1.8.RELEASElettuce.version>
<liquibase.version>4.5.0liquibase.version>
<log4j2.version>2.17.2log4j2.version>
<logback.version>1.2.11logback.version>
<lombok.version>1.18.24lombok.version>
<mariadb.version>2.7.5mariadb.version>
<maven-antrun-plugin.version>3.0.0maven-antrun-plugin.version>
<maven-assembly-plugin.version>3.3.0maven-assembly-plugin.version>
<maven-clean-plugin.version>3.1.0maven-clean-plugin.version>
<maven-compiler-plugin.version>3.8.1maven-compiler-plugin.version>
<maven-dependency-plugin.version>3.2.0maven-dependency-plugin.version>
<maven-deploy-plugin.version>2.8.2maven-deploy-plugin.version>
<maven-enforcer-plugin.version>3.0.0maven-enforcer-plugin.version>
<maven-failsafe-plugin.version>2.22.2maven-failsafe-plugin.version>
<maven-help-plugin.version>3.2.0maven-help-plugin.version>
<maven-install-plugin.version>2.5.2maven-install-plugin.version>
<maven-invoker-plugin.version>3.2.2maven-invoker-plugin.version>
<maven-jar-plugin.version>3.2.2maven-jar-plugin.version>
<maven-javadoc-plugin.version>3.3.2maven-javadoc-plugin.version>
<maven-resources-plugin.version>3.2.0maven-resources-plugin.version>
<maven-shade-plugin.version>3.2.4maven-shade-plugin.version>
<maven-source-plugin.version>3.2.1maven-source-plugin.version>
<maven-surefire-plugin.version>2.22.2maven-surefire-plugin.version>
<maven-war-plugin.version>3.3.2maven-war-plugin.version>
<micrometer.version>1.8.7micrometer.version>
<mimepull.version>1.9.15mimepull.version>
<mockito.version>4.0.0mockito.version>
<mongodb.version>4.4.2mongodb.version>
<mssql-jdbc.version>9.4.1.jre8mssql-jdbc.version>
<mysql.version>8.0.29mysql.version>
<nekohtml.version>1.9.22nekohtml.version>
<neo4j-java-driver.version>4.4.6neo4j-java-driver.version>
<netty.version>4.1.78.Finalnetty.version>
<netty-tcnative.version>2.0.53.Finalnetty-tcnative.version>
<okhttp3.version>3.14.9okhttp3.version>
<oracle-database.version>21.3.0.0oracle-database.version>
<pooled-jms.version>1.2.4pooled-jms.version>
<postgresql.version>42.3.6postgresql.version>
<prometheus-client.version>0.12.0prometheus-client.version>
<quartz.version>2.3.2quartz.version>
<querydsl.version>5.0.0querydsl.version>
<r2dbc-bom.version>Arabba-SR13r2dbc-bom.version>
<rabbit-amqp-client.version>5.13.1rabbit-amqp-client.version>
<rabbit-stream-client.version>0.4.0rabbit-stream-client.version>
<reactive-streams.version>1.0.4reactive-streams.version>
<reactor-bom.version>2020.0.20reactor-bom.version>
<rest-assured.version>4.4.0rest-assured.version>
<rsocket.version>1.1.2rsocket.version>
<rxjava.version>1.3.8rxjava.version>
<rxjava-adapter.version>1.2.1rxjava-adapter.version>
<rxjava2.version>2.2.21rxjava2.version>
<saaj-impl.version>1.5.3saaj-impl.version>
<selenium.version>3.141.59selenium.version>
<selenium-htmlunit.version>2.54.0selenium-htmlunit.version>
<sendgrid.version>4.7.6sendgrid.version>
<servlet-api.version>4.0.1servlet-api.version>
<slf4j.version>1.7.36slf4j.version>
<snakeyaml.version>1.29snakeyaml.version>
<solr.version>8.8.2solr.version>
<spring-amqp.version>2.4.6spring-amqp.version>
<spring-batch.version>4.3.6spring-batch.version>
<spring-data-bom.version>2021.1.5spring-data-bom.version>
<spring-framework.version>5.3.21spring-framework.version>
<spring-hateoas.version>1.4.4spring-hateoas.version>
<spring-integration.version>5.5.13spring-integration.version>
<spring-kafka.version>2.8.7spring-kafka.version>
<spring-ldap.version>2.3.8.RELEASEspring-ldap.version>
<spring-restdocs.version>2.0.6.RELEASEspring-restdocs.version>
<spring-retry.version>1.3.3spring-retry.version>
<spring-security.version>5.6.6spring-security.version>
<spring-session-bom.version>2021.1.3spring-session-bom.version>
<spring-ws.version>3.1.3spring-ws.version>
<sqlite-jdbc.version>3.36.0.3sqlite-jdbc.version>
<sun-mail.version>1.6.7sun-mail.version>
<thymeleaf.version>3.0.15.RELEASEthymeleaf.version>
<thymeleaf-extras-data-attribute.version>2.0.1thymeleaf-extras-data-attribute.version>
<thymeleaf-extras-java8time.version>3.0.4.RELEASEthymeleaf-extras-java8time.version>
<thymeleaf-extras-springsecurity.version>3.0.4.RELEASEthymeleaf-extras-springsecurity.version>
<thymeleaf-layout-dialect.version>3.0.0thymeleaf-layout-dialect.version>
<tomcat.version>9.0.64tomcat.version>
<unboundid-ldapsdk.version>4.0.14unboundid-ldapsdk.version>
<undertow.version>2.2.18.Finalundertow.version>
<versions-maven-plugin.version>2.8.1versions-maven-plugin.version>
<webjars-locator-core.version>0.48webjars-locator-core.version>
<wsdl4j.version>1.6.3wsdl4j.version>
<xml-maven-plugin.version>1.0.2xml-maven-plugin.version>
<xmlunit2.version>2.8.4xmlunit2.version>
properties>
所以由上面的pom文件的依赖关系可以得出结论:
在
dependencies中存在的依赖,我们导入项目时,依赖默认是不需要写版本号的;而没有在dependencies里面管理的依赖自然需要声明版本号。
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
dependencies>

在当前项目中引入如下配置覆盖父项目中配置,即可修改mysql的版本号。
<properties>
<mysql.version>5.1.49mysql.version>
properties>

什么是Springboot 项目的Starters?
**Springboot将所有的功能场景都抽取出来,做成一个个的Starters(启动器),只需要在项目里面引入这些Starters相关场景的所有依赖都会导入进来。**要用什么功能就导入什么场景的启动器。
官方的所有 starter 都遵循类似的命名规则:spring-boot-starter-*,其中 * 是特定类型的应用。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
spring-boot-starter-web:帮我们导入了web模块正常运行所依赖的组件。
第三方的 starter 命名不应该以 spring-boot 开头,因为它是官方 Spring Boot 构件所保留的规则。例如,有一个第三方 starter 项目叫做 thirdpartyproject,它通常会命名为 thirdpartyproject-spring-boot-starter。
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>1.1.10version>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.2.2version>
dependency>
所有场景启动器最底层的依赖 spring-boot-starter 。

启动器包含两大核心:
自动化配置是Spring boot的核心功能之一,正是它消除或者说减少了我们在开发spring应用时所需要的一大堆和业务无关的配置。
而它背后的理念并不新鲜,它使用的是叫做约定优于配置(convention over configuration)的原则,它预先做出一些合理的假设(也就是约定),只要你遵循它的约定,就不需要做出额外的配置,便可以直接使用它提供的功能,从而消除了显式的配置。例如maven会假设源代码目录默认就是项目根目录下的src/main/java目录,如果你遵循这个目录结构,便不需要告诉maven源代码目录是什么。这个原则经常被各种框架使用,因为框架往往根据这些约定做出了相应的配置,如果这些配置符合我们的需求,我们就什么也不用做,只需要欣然的接受并使用就可以了,只有当这些配置不符合我们的需求时,才需要我们做出调整。
接下来以spring-boot-starter-web为例,来看一下都做了哪些自动化配置。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class Springboot02BootApplication {
public static void main(String[] args) {
//获取SpringIOC容器,ConfigurableApplicationContext是ApplicationContext接口的子接口
ConfigurableApplicationContext context = SpringApplication.run(Springboot02BootApplication.class, args);
//获取Spring容器中所有Bean的名称
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String name : beanDefinitionNames) {
System.out.println(name);
}
}
}
无需配置组件扫描,默认的组件扫描包为启动类所在包,表示启动类所在包及其下面的所有子包里面的组件都会被默认扫描进来。
如果需要改变扫描包,在启动类@SpringBootApplication注解中添加scanBasePackages配置项即可,或者通过@ComponentScan注解指定扫描路径。
@SpringBootApplication(scanBasePackages = "com.newcapec")
默认配置最终都是映射到某个类上,如:DataSourceProperties。
Springboot中有很多的启动器starter,每个启动器中都带有自动配置项,所有的自动配置功能都在 spring-boot-autoconfigure 包里面。

注: Springboot启动时并不是加载所有自动配置项,而是会根据引入的启动器开启相应的自动配置项。
在Springboot项目中通过@Configuration和@Bean注解进行Bean的配置。
Customers类:
public class Customers {
private String name;
private Integer age;
public Customers() {
}
public Customers(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Customers{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Orders类:
public class Orders {
private String name;
private Customers customers;
public Orders() {
}
public Orders(String name) {
this.name = name;
}
public Orders(String name, Customers customers) {
this.name = name;
this.customers = customers;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Customers getCustomers() {
return customers;
}
public void setCustomers(Customers customers) {
this.customers = customers;
}
@Override
public String toString() {
return "Orders{" +
"name='" + name + '\'' +
", customers=" + customers +
'}';
}
}
@Configuration
public class MyConfig {
@Bean
public Customers getCustomers(){
return new Customers("Tom", 20);
}
@Bean
public Orders getOrders(){
//在@Configuration注解的配置类中,如果直接调用@Bean标注的方法,相当于从IOC容器中获取该bean并依赖注入
return new Orders("Tom's Order", getCustomers());
}
}
public static void main(String[] args) {
//获取SpringIOC容器,ConfigurableApplicationContext是ApplicationContext接口的子接口
ConfigurableApplicationContext context = SpringApplication.run(Springboot02BootApplication.class, args);
//获取Spring中加载的Bean
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String name : beanDefinitionNames) {
System.out.println(name);
}
Customers customers = context.getBean(Customers.class);
System.out.println(customers);
Orders orders = context.getBean(Orders.class);
System.out.println(orders);
System.out.println("是否单例:" + (customers == orders.getCustomers()));
}

直接填写class数组,Class数组可以有0到多个。
Bean类:
public class Dog {
}
public class Cat {
}
在配置类中导入:
@Configuration
@Import({Dog.class, Cat.class})
public class MyConfig {
@Bean
public Customers getCustomers(){
return new Customers("Tom", 20);
}
@Bean
public Orders getOrders(){
return new Orders("Tom's Order", getCustomers());
}
}

对应的import的bean都将加入到spring容器中,这些在容器中bean名称是该类的全类名。
通过@Import注解导入的是ImportSelector接口的实现类,在其抽象方法selectImports中定义需要注册的Bean对象。
Bean类:
public class Student {
}
public class Teacher {
}
ImportSelector接口的实现类:
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.newcapec.bean.Student", "com.newcapec.bean.Teacher"};
}
}
ImportSelector接口的selectImports方法:
在配置类中导入:
@Configuration
@Import({Dog.class, Cat.class, MyImportSelector.class})
public class MyConfig {
@Bean
public Customers getCustomers(){
return new Customers("Tom", 20);
}
@Bean
public Orders getOrders(){
return new Orders("Tom's Order", getCustomers());
}
}

需要注意的是selectImports方法可以返回空数组但是不能返回null,否则会报空指针异常。
通过@Import注解导入的是ImportBeanDefinitionRegistrar接口的实现类,在其抽象方法registerBeanDefinitions中定义需要注册的Bean对象。与ImportSelector用法类似,只不过这种用法可自定义注册Bean。
Bean类:
public class Users {
}
ImportBeanDefinitionRegistrar接口的实现类:
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//指定Bean定义信息
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Users.class);
//注册一个Bean指定bean名称
registry.registerBeanDefinition("users", rootBeanDefinition);
}
}
ImportBeanDefinitionRegistrar接口的registerBeanDefinitions方法:
在配置类中导入:
@Configuration
@Import({Dog.class, Cat.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
public class MyConfig {
@Bean
public Customers getCustomers(){
return new Customers("Tom", 20);
}
@Bean
public Orders getOrders(){
return new Orders("Tom's Order", getCustomers());
}
}
@Conditional注解可以用在任何类型或者方法上面,通过@Conditional注解可以配置一些条件判断,当所有条件都满足的时候,被@Conditional标注的目标才会被spring容器处理。

| @Conditional派生注解 | 作用(都是判断是否符合指定的条件) |
|---|---|
| @ConditionalOnJava | 系统的java版本是否符合要求 |
| @ConditionalOnBean | 有指定的Bean类 |
| @ConditionalOnMissingBean | 没有指定的bean类 |
| @ConditionalOnExpression | 符合指定的SpEL表达式 |
| @ConditionalOnClass | 有指定的类 |
| @ConditionalOnMissingClass | 没有指定的类 |
| @ConditionalOnSingleCandidate | 容器只有一个指定的bean,或者这个bean是首选bean |
| @ConditionalOnProperty | 指定的property属性有指定的值 |
| @ConditionalOnResource | 路径下存在指定的资源 |
| @ConditionalOnWebApplication | 系统环境是web环境 |
| @ConditionalOnNotWebApplication | 系统环境不是web环境 |
| @ConditionalOnjndi | JNDI存在指定的项 |
Bean类:
public class Color {
}
public class Red {
}
public class Green {
}
配置类:
@Configuration
@Import({Dog.class, Cat.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
public class MyConfig {
@Bean
public Customers getCustomers(){
return new Customers("Tom", 20);
}
@Bean
public Orders getOrders(){
return new Orders("Tom's Order", getCustomers());
}
@Bean("color")
public Color getColor(){
return new Color();
}
@ConditionalOnBean(name = "color")
@Bean("red")
public Red getRed(){
return new Red();
}
@ConditionalOnMissingBean(name = "color")
@Bean("green")
public Green getGreen(){
return new Green();
}
}
测试:
public static void main(String[] args) {
//获取SpringIOC容器,ConfigurableApplicationContext是ApplicationContext接口的子接口
ConfigurableApplicationContext context = SpringApplication.run(Springboot02BootApplication.class, args);
//获取Spring中加载的Bean
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String name : beanDefinitionNames) {
System.out.println(name);
}
Customers customers = context.getBean(Customers.class);
System.out.println(customers);
Orders orders = context.getBean(Orders.class);
System.out.println(orders);
System.out.println("是否单例:" + (customers == orders.getCustomers()));
boolean color = context.containsBean("color");
System.out.println("容器中是否包含color组件:" + color);
boolean red = context.containsBean("red");
System.out.println("容器中是否包含red组件:" + red);
boolean green = context.containsBean("green");
System.out.println("容器中是否包含green组件:" + green);
}
color Bean存在时:
容器中是否包含color组件:true
容器中是否包含red组件:true
容器中是否包含green组件:false
color Bean不存在时:
容器中是否包含color组件:false
容器中是否包含red组件:false
容器中是否包含green组件:true
在一个普通的Java类上面添加一个注解@SpringBootApplication,将自动识别为一个Springboot的应用程序,该类中的main方法也是Springboot程序的入口方法。
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
SpringBoot程序的主程序注解,当前类也是Springboot的主配置类。
@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 {
}
这里又涉及到三个注解@SpringBootConfiguration、@ComponentScan和@EnableAutoConfiguration。
@SpringBootConfiguration是Springboot程序的配置类注解。
@SpringBootConfiguration注解的源码:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
}
@Configuration注解的源码:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
}
分析:
@ComponentScan注解的源码:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
}
分析:
开启自动配置功能。
之前Spring的时代,需要配置的xml内容;现在Springboot帮我们自动配置,@EnableAutoConfiguration告诉SpringBoot开启自动配置功能。
@EnableAutoConfiguration注解的源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
这里又涉及到两个注解@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class)。
自动配置包,指定了默认的包规则。
@AutoConfigurationPackage注解的源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
这里又涉及到@Import(AutoConfigurationPackages.Registrar.class)注解。
使用@Import注解导入一个AutoConfigurationPackages.Registrar的类,给容器中注册一系列组件。
AutoConfigurationPackages.Registrar的源码:
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
利用断点调试功能,可得知使用@SpringBootApplication注解标识的类所在的包以及所在子包下的所有类,自动扫描注册到Spring容器中。跟之前学习Spring时,使用的xml文件中,组件扫描基础包的功能一样。

使用@Import注解导入一个AutoConfigurationImportSelector的类,给容器中注册一系列组件。
AutoConfigurationImportSelector的部分源码:
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(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 = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
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;
}
}
在源码99行,利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件。
在源码123行,调用List获取到所有准备导入到容器中的组件。这些组件就是自动配置类,就是给容器中导入某个场景(Starter)需要的所有组件,并配置好这些组件。有了自动配置类,免去了我们手动编写配置注入功能组件等的工作。

在源码178行,利用工厂加载SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());获取到所有组件。
SpringFactoriesLoader类中的loadFactoryNames()方法源码:
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoader == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = (Map)cache.get(classLoader);
if (result != null) {
return result;
} else {
HashMap result = new HashMap();
try {
Enumeration urls = classLoader.getResources("META-INF/spring.factories");
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
String[] var10 = factoryImplementationNames;
int var11 = factoryImplementationNames.length;
for(int var12 = 0; var12 < var11; ++var12) {
String factoryImplementationName = var10[var12];
((List)result.computeIfAbsent(factoryTypeName, (key) -> {
return new ArrayList();
})).add(factoryImplementationName.trim());
}
}
}
result.replaceAll((factoryType, implementations) -> {
return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
});
cache.put(classLoader, result);
return result;
} catch (IOException var14) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
}
}
}
查看源码得知Springboot默认加载了类路径下的META-INF/spring.factories文件。

在spring-boot-autoconfigure-2.6.9.jar包中找到META-INF/spring.factories文件。
从25行到158行总共133个准备导入的组件,与调试中展示的个数相符。

虽然默认加载130个自动配置,但Springboot仍会按照条件装配规则@Conditional,最终按需配置。当代码执行到129行时,我们可以看到需要导入的自动配置类仅剩24个。

@Bean
@ConditionalOnBean(MultipartResolver.class)
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
public MultipartResolver multipartResolver(MultipartResolver resolver) {
// Detect if the user has created a MultipartResolver but named it incorrectly
return resolver;
}
随便找一个看看,这里看的是RedisAutoConfiguration,看看redis是如何配置的
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
分析:
这里看的是DispatcherServletAutoConfiguration,看看DispatcherServlet是如何配置的
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
/**
* The bean name for a DispatcherServlet that will be mapped to the root URL "/".
*/
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
/**
* The bean name for a ServletRegistrationBean for the DispatcherServlet "/".
*/
public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";
@Configuration(proxyBeanMethods = false)
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
protected static class DispatcherServletConfiguration {
}
@Configuration(proxyBeanMethods = false)
@Conditional(DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {
}
@Order(Ordered.LOWEST_PRECEDENCE - 10)
private static class DefaultDispatcherServletCondition extends SpringBootCondition {
}
@Order(Ordered.LOWEST_PRECEDENCE - 10)
private static class DispatcherServletRegistrationCondition extends SpringBootCondition {
}
}
分析:
@Configuration配置类它也是一个Bean,但对于配置类来说,某些场景下的执行顺序是必须的,是需要得到保证的。Spring Boot下对自动配置的管理对比于Spring它就是黑盒,它会根据当前容器内的情况来动态的判断自动配置类的载入与否、以及载入的顺序,所以可以说:Spring Boot的自动配置它对顺序是有强要求的。需求驱使,Spring Boot给我们提供了@AutoConfigureBefore、@AutoConfigureAfter、@AutoConfigureOrder(下面统称这三个注解为“三大注解”)这三个注解来帮我们解决这种诉求。@AutoConfigureOrder属于1.3.0版本新增,表示绝对顺序(数字越小,优先顺序越高)。@AutoConfigureBefore、@AutoConfigureAfter来控制配置的顺序,表示在某种配置之后或之前载入;DispatcherServletAutoConfiguration被载入的前提是:ServletWebServerFactoryAutoConfiguration已经完成初始化。总结: