1、Spring boot简介
Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。
如下优点:
独立运行:Spring Boot 而且内嵌了各种 servlet 容器,Tomcat、Jetty 等,现在不再需要打成war 包部署到容器中,Spring Boot 只要打成一个可执行的 jar 包就能独立运行,所有的依赖包都在一个 jar 包内。
简化配置:spring-boot-starter-web 启动器自动依赖其他组件,简少了 maven 的配置。
自动配置:Spring Boot 能根据当前类路径下的类、jar 包来自动配置 bean,如添加一个 spring boot-starter-web 启动器就能拥有 web 的功能,无需其他配置。
无代码生成和XML配置:Spring Boot 配置过程中无代码生成,也无需 XML 配置文件就能完成所有配置工作,这一切都是借助于条件注解完成的,这也是 Spring4.x 的核心功能之一。
应用监控:Spring Boot 提供一系列端点可以监控服务及应用,做健康检测。
在过去,我们要让一个Spring项目启动,往往需要配置很多的xml配置文件,但是在使用SpringBoot之后,我们甚至无需写一行xml,就可以直接将整个项目启动,这种“零配置”的做法减轻了开发人员很多的工作量,可以让开发人员一心扑在业务逻辑的设计上,使项目的逻辑更加完善。
另外Spring boot 开箱即用的方式也极大的简化了项目的创建,SpringBoot提供了我们快速创建SpringBoot项目的地方:https://start.spring.io/
我们只需要在这个网页中把整个项目起好名字,然后选好我们需要的组件,就可以直接获得一个可以跑起来的SpringBoot项目。我们只需要填完上述信息,点击Generate,就可以直接将一个SpringBoot项目下载下来,然后导入我们的IDE,Eclipse或者IDEA都可,之后就可以直接将它运行起来。
2、自动装配原理
传统的SSM或SSH项目,光集成各大组件就让一般人望而却步。但是在Spring boot 这都变得非常简单,让我们从pom.xml 文件开始。
父依赖
SpringBoot的项目都会存在一个父依赖,按住Ctrl+鼠标左键,可以点进去。
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --></parent>
点进去之后发现里面除了一些插件和配置文件的格式之外,还存在一个依赖。
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.2.1.RELEASE</version> <relativePath>../../spring-boot-dependencies</relativePath></parent>
于是再点进去,可以发现里面放了很多的依赖和依赖的版本号。由于这个文件实在太长了,所以这里只展示一部分。
所以我们可以得出第一个结论:
spring-boot-dependencies:作为父工程,存放了SpringBoot的核心依赖。我们在写或者引入一些SpringBoot依赖的时候,不需要指定版本,正是因为SpringBoot的父依赖已经帮我们维护了一套版本。另外我们还可以看到,在父依赖中也帮我们写好了资源库,不用我们自己再去配置了。
启动器
启动器就是SpringBoot的启动场景,比如我们要使用web相关的,那么就直接引入spring-boor-starter-web,那么他就会帮我们自动导入web环境下所有必需的依赖。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.2.1.RELEASE</version> </dependency>
启动器就是SpringBoot的启动场景,比如我们要使用web相关的,那么就直接引入spring-boor-starter-web,那么他就会帮我们自动导入web环境下所有必需的依赖。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot</artifactId> <version>2.2.1.RELEASE</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> <version>2.2.1.RELEASE</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> <version>2.2.1.RELEASE</version> <scope>compile</scope> </dependency> <dependency> <groupId>jakarta.annotation</groupId> <artifactId>jakarta.annotation-api</artifactId> <version>1.3.5</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.2.1.RELEASE</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> <version>1.25</version> <scope>runtime</scope> </dependency>
其中存放了自动配置相关的依赖、日志相关依赖、还有Spring-core等依赖,这些依赖我们只需要导入一个spring-boor-starter就可以直接将其全部引入,而不需要再像以前那样逐个导入了。
所以我们得出结论:SpringBoot会将所有的功能场景都封装成一个一个的启动器,供开发人员使用。
主程序,Spring boot 的入口
//@SpringBootApplication 标注,是一个SpringBoot应用
@SpringBootApplication
public class SpringbootdemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootdemoApplication.class, args);
}
}
再写SpringBoot项目的时候,总要写这么一个主程序,这个主程序最大的特点就是其类上放了一个@SpringBootApplication注解,这也正是SpringBoot项目启动的核心,也是我们要研究的重点。
点开@SpringBootApplication,可以发现它是一个组合注解,主要是由这么几个注解构成的。
@SpringBootConfiguration//核心
@EnableAutoConfiguration//核心
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
这里我们需要深入研究的是两个注解SpringBootConfiguration和EnableAutoConfiguration
SpringBootConfiguration
SpringBootConfiguration其实就携带了一个@Configuration注解,这个注解我们再熟悉不过了,他就代表自己是一个Spring的配置类。所以我们可以认为:@SpringBootConfiguration = @Configuration
EnableAutoConfiguration
顾名思义,这个注解一定和自动配置相关,点进去看源代码之后可以发现,其内部就包含了这么两个注解
@AutoConfigurationPackage //自动配置包
@Import(AutoConfigurationImportSelector.class)//自动配置导入选择
来看看@Import(AutoConfigurationImportSelector.class)中的内容:
它帮我们导入了AutoConfigurationImportSelector,这个类中存在一个方法可以帮我们获取所有的配置
getCandidateConfigurations():
这个方法可以用来获取所有候选的配置,那么这些候选的配置又是从哪来的呢?
实际上它返回了一个List,这个List是由loadFactoryNames()方法返回的,其中传入了一个getSpringFactoriesLoaderFactoryClass(),我们可以看看这个方法的内容。
我们看到了一个眼熟的词 EnableAutoConfiguration,也就是说,它实际上返回的就是标注了这个类的所有包。标注了这个类的包不就是@SpringBootApplication吗?
所以我们可以得出结论:它兜兜转转绕了这么多地方,就是为了将启动类所需的所有资源导入。
我们接着往下看,它其中还有这么一条语句,是一条断言: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.");
这个断言的意思是,configurations必须非空,否则就打印一段话,No auto configuration classes found in META-INF/spring.factories,我们把这个逻辑反过来想想。如果这个集合不为空,是不是就代表找到了这个spring.factories并且会去加载这个文件中的内容呢?
带着这个疑问,我们首先找到spring.factories这个文件,可以看到里面包含了很多自动配置属性:
我们可以随便找一个自动配置点进去,比如WebMvcAutoConfiguration,这里放了所有关于WebMvc的配置,如视图解析器、国际化等等。
分析到这里,我们就可以得出一个完整的结论了:
当我们的SpringBoot项目启动的时候,会先导入AutoConfigurationImportSelector,这个类会帮我们选择所有候选的配置,我们需要导入的配置都是SpringBoot帮我们写好的一个一个的配置类,那么这些配置类的位置,存在与META-INF/spring.factories文件中,通过这个文件,Spring可以找到这些配置类的位置,于是去加载其中的配置。
看到这里,可能有些同学会存在疑问,spring.factories中存在那么多的配置,每次启动时都是把它们全量加载吗?这显然是不现实的。
这其实也是我在看源码的时候存在疑问的地方,因为其中有一个注解并不常用,我们点开一个配置类就可以看到。
@ConditionalOnXXX:如果其中的条件都满足,该类才会生效。
所以在加载自动配置类的时候,并不是将spring.factories的配置全量加载进来,而是通过这个注解的判断,如果注解中的类都存在,才会进行加载。
所以就实现了:我们在pom.xml文件中加入stater启动器,SpringBoot自动进行配置。完成开箱即用。
总结:
SpringBoot所有自动配置类都是在启动的时候进行扫描并加载,通过spring.factories可以找到自动配置类的路径,但是不是所有存在于spring,factories中的配置都进行加载,而是通过@ConditionalOnClass注解进行判断条件是否成立(只要导入相应的stater,条件就能成立),如果条件成立则加载配置类,否则不加载该配置类。
在这里贴一个我认为的比较容易理解的过程:
SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
将这些值作为自动配置类导入容器 , 自动配置类就生效 , 帮我们进行自动配置工作;
以前我们需要自己配置的东西 , 自动配置类都帮我们解决了
整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;
它将所有需要导入的组件以全类名的方式返回 , 这些组件就会被添加到容器中 ;
它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 , 并配置好这些组件 ;
有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作;
3、约定大于配置
开箱即用的原理说完了,约定大于配置就比较好理解了。其实约定大于配置就是开箱即用中那些自动配置的细节。说的具体点就是:我们的配置文件(.yml)应该放在哪个目录下,配置文件的命名规范,项目启动时扫描的Bean,组件的默认配置是什么样的(比如SpringMVC的视图解析器)等等等等这一系列的东西,都可以被称为约定,下面就来一点一点地说一下SpringBoot中的“约定”。
maven目录结构的约定:
file:./config/
file:./
classpath:/config/
classpath:/
也就是说,spring的配置文件目录可以放在
/config
/(根目录)
resource/config/
resource/
这四个路径从上到下存在优先级关系。
SpringBoot默认配置文件的约定
SpringBoot默认可以加载以下三种配置文件:
application.yml
application.yaml
application.properties
建议使用前两种作为项目的配置文件。
项目启动时扫描包范围的约定
SpringBoot的注解扫描的默认规则是SpringBoot的入口类所在包及其子包。
若入口类所在的包是cn.objectspace.demo那么自动扫描包的范围是cn.objectspace.demo包及其下面的子包,如果service包和dao包不在此范围,则不会自动扫描。