SpringBoot流行之前,程序员大多是用SSM框架整合来进行WEB后端开发。这种方式非常麻烦,需要手动引入大量的包,还要配置很多XML文件,光是搭建环境就需要很久。
随着“约定大于配置”理念的流行,SpringBoot随之兴起,它大大简化了web开发的流程,可以让初学者快速上手。SpringBoot的核心理念大致有3点:
1、帮助开发者快速整合第3方框架,原理是maven依赖封装和自定义的Starter。
2、完全去除XML,采用纯注解的方式。原理是SpringBoot其实是根据Spring的体系原生的注解实现的包装。
3、不需要外部容器,转而使用内嵌的web容器,原理是使用Java语言创建tomcat服务器,然后将本地的class文件交给tomcat来加载。Tomcat tomcat = new Tomcat()。
常见的springboot主程序如下所示:
package org.study.jimmy.springmvctest;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringMvcTestApplication {
public static void main(String[] args) {
SpringApplication.run(SpringMvcTestApplication.class, args);
}
}
关键点有2个,一个是类上的注解@SpringBootApplication,另一个是run()方法。
首先来看一下注解@SpringBootApplication,它是个复合注解,使用的都是spring的原生注解,跟自动配置相关的注解就是@EnableAutoConfiguration。
我们来看看@EnableAutoConfiguration这个注解,这个注解又有2个注解,
1、@AutoConfigurationPackage注解就是将该注解的类所在的包路径作为自动配置的包路径进行管理。
2、@Import注解会引入一个AutoConfigurationImportSelector类。这个类是重点。
通过debug代码,我们看到,真正启动自动配置的方法是:ConfigurationClassParser.getImports(),其中process()方法对传入的AutoConfigurationImportSelector下这个类selectImports()方法做选择,选择一些符合条件的自动配置类,过滤掉一些不符合条件的自动配置类。
// ConfigurationClassParser.java
public Iterable<Group.Entry> getImports() {
// 遍历DeferredImportSelectorHolder对象集合deferredImports,deferredImports集合装了各种ImportSelector,当然这里装的是AutoConfigurationImportSelector
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
// 【1】,利用AutoConfigurationGroup的process方法来处理自动配置的相关逻辑,决定导入哪些配置类(这个是我们分析的重点,自动配置的逻辑全在这了)
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
// 【2】,经过上面的处理后,然后再进行选择导入哪些配置类
return this.group.selectImports();
}
上面的方法【1】处是用来获取自动配置类,承担了自动配置的主要逻辑。它的主要作用是去jar包中的spring.factories文件读取EnableAutoConfiguration类的子类,将符合条件的自动配置类封装进AutoConfigurationEntry对象,并返回。
那么,SpringBoot是怎么样有选择的导入自动配置类的?是使用AutoConfigurationImportFilter接口的match()方法。
@FunctionalInterface
public interface AutoConfigurationImportFilter {
boolean[] match(String[] autoConfigurationClasses,
AutoConfigurationMetadata autoConfigurationMetadata);
}
可以看到,AutoConfigurationImportFilter接口有一个具体的实现类FilteringSpringBootCondition,FilteringSpringBootCondition又有三个具体的子类:OnClassCondition,OnBeanCondtition和OnWebApplicationCondition。
对于每一个候选的自动配置类,都会经过match方法,会经过三个具体的子类:OnClassCondition,OnBeanCondtition和OnWebApplicationCondition的match方法,判断是否过滤。
最后我们来看个例子:我们来看2个由上面这个文件引入的配置类,第一个是ServletWebServerFactoryAutoConfiguration。这个类专门用来自动化配置web容器。这个类头上有2个注解,一个是@EnableConfigurationProperties(ServerProperties.class),这个注解的作用是将括号中的POJO类绑定到类中的方法里面使用。
这个POJO类就是包含了一些属性的普通类,它头上也有一个注解,@ConfigurationProperties(prefix = “server”, ignoreUnknownFields = true),这个注解表示,程序会将配置文件properties中以server为前缀的配置项的值绑定到这个POJO对象中。比如常用的改端口号的配置:server.port=80。
第二个是DispatcherServletAutoConfiguration,这个类是用来自动配置SpringMVC的。同样也使用了@EnableConfigurationProperties(WebMvcProperties.class)注解将用户配置的配置项注入到程序中。
那么问题1来了,文件中有那么多配置类,为何有的生效了,有的没有生效呢?原因就在于这些配置类上都有一些注解@ConditionalOnXXX,会根据一些条件才会去生效配置。
问题2:POJO绑定的格式是什么样的?答:松散绑定,即配置文件中全小写加横杠隔开对应程序中的小驼峰。last-name对应于lastName。
接下来我们来看run()方法。
最主要的2步就是读取配置文件中的配置项并绑定到POJO上,然后refresh刷新容器,将bean纳管到IOC容器中,程序启动。其中刷新容器的过程就是之前讲的spring源码中的刷新容器的过程。有兴趣的可以去我之前的博客看一下。
为什么说SpringBoot大大简化了开发流程,就是因为它遵循了约定大于配置的思想,将我们常用的组件都配置加载放到IOC容器中。当然用户也可以将自己的配置写到配置文件中,结合上面讲的2个注解绑定到POJO类上去,供组件读取使用。
总结下自动配置相关源码主要有以下几个重要的步骤:
1、从spring.factories配置文件中加载自动配置类;
2、加载的自动配置类中排除掉@EnableAutoConfiguration注解的exclude属性指定的自动配置类;
3、然后再用AutoConfigurationImportFilter接口去过滤自动配置类是否符合其标注注解(若有标注的话)@ConditionalOnClass,@ConditionalOnBean和@ConditionalOnWebApplication的条件,若都符合的话则返回匹配结果;
4、然后触发AutoConfigurationImportEvent事件,告诉ConditionEvaluationReport条件评估报告器对象来分别记录符合条件和exclude的自动配置类。
5、最后spring再将最后筛选后的自动配置类导入IOC容器中