SpringBoot两大核心原理
SpringBoot核心就是两大块:
第一个,就是自动化配置:
SpringBoot提供了一套通用的配置机制,让我们配置文件里配的这些属性直接全部的能够通过简化的方式,通过这些默认值的方式,然后在运行期把他对应到我么要装配的这些类上去,说起来还是很抽象,我们一会通过一个具体的实例来看,
第二个就是定义了很多spring-boot-starter,比如说Web的starter,WebMVC的starter,JDBC的starter,mybatis的starter。各种starter。这个starter是什么呢?就是把对应的这个框架这个技术跟SpringBoot,Spring体系中间粘起来,如果我们说Spring就是一个框架,划分好这种格子,骨架全都有了,中间没有填具体的内容,那SpringBoot它里面的这些starter,官方就有一两百个,这些starter就相当于已经帮我们把要往格子里填的那些其他的框架其他的组件中间糊了一层胶水,这个starter就是那层胶水,这样的话,你就不需要做任何其他的操作,直接可以把对应的第三方的那个框架,那个组件插在Spring体系里面去。
在SpringBoot里我们配置配置文件默认的情况下可以用application.yaml或者application.properties这样的配置文件,这两个文件是一样的。作为他的入口点,而且这两个文件的名字是定死的。 在这两种文件里都是文本的,一个是有层次结构有缩进的,一个就像我们正常使用的properties,全是key=value的方式。那么我们所有的配置,前面的key都是a.b.c.d这种方式来配的,同意让他们按照前缀来进行区分和分组,比如说我们常用的spring.web,这个前缀表示是SpringWeb模块使用的配置,那他在后面配的各种参数就会最终提供给SpringWeb这个starter用。把SpringWeb相关的这些组件拉起来,把参数塞进去,
所以我们具体来看一下我们通过application.yaml或者properties配置了各种参数,再在SpringBoot加载启动的时候通过我们的各种配置类和自动配置的这些类(Configuration)加载上来,再通过这些Configuration配置最终把我们的Spring容器里的Bean生成创建出来,然后把他初始化好,通过这个链条就把SpringBoot整个拉起来了。
在我们的properties和yaml文件里面主要是用前缀来区分不同的参数配置,相同的前缀组成了一组配置。
一组配置都是提供给具体的某个组件的,或者我们叫某个starter。
所有装配的单元都是以starter为单位的,每个starter就相当于是在Spring框架里面我们往中间的某个格子里插过去的。在Spring整个体系相当于一大面墙,墙上插进去一个组件,或者集成进去的一个框架。
为什么要约定大于配置
说到这里就不得不提SpringBoot里我们有一个很重要的一个原则,就叫约定大于配置。SpringBoot通过约定大于配置帮我们天然的已经定义好了很多规则,很多的默认的行为,默认的参数,这样的话就使得我们,比如说我们想new出来一个最简单的这样一个应用程序,Web应用程序还是CS应用程序,这样一个demo几乎可以说是少量的代码,甚至零代码零配置都可以run起来。这种特点我们一般有一个名词,叫开箱即用。就相当于你去买个东西买回来以后,包装一拆就能用。不需要你自己再去做任何的组装。
举个例子,JVM里面有1000多个参数,但是咱们正常使用的时候,如果常规下开发测试的时候,不需要条约的时候,你可以不使用任何的参数,直接用java,后面类名字就可以把我们一个java程序直接给run起来。这1000多个参数一个都没有配,为什么呢,他都默认给了你默认值。给了你一些相关的这种参数的组合。当你需要额外的去定制,额外的去调优它,根据你的一些软硬件条件,系统运行的一些特点、访问量等去调他的时候,你才需要去精确的调校相关的参数。这种思想我们现在用得越来越多了,就是先给大家一个通用的版本,里面的东西是按照各种配方搭配好的,可以满足绝大部分的需求,如果你有额外的需求在另说。在上面做一些相关的定制就好了。
具体说来在SpringBoot里我们常见的有如下的一些重要的默认约定:
- Maven的目录结构:默认有resources文件夹存放配置文件。默认打包方式为jar。(我们成为fatjar,它会把所有的依赖都打进去,然后他自己有一个嵌入的比如说Web容器。这个时候如果我们是Web项目,直接可以把这个jar包run起来。他就可以对外提供Http的服务,web服务了)。
- 默认的配置文件:application.properties或application.yml文件。(yaml有点像是一个类似于这种有Json格式的这样一个配置文件,他跟properties文件唯一的不同是properties相当于是打平的)
- 默认通过spring.profiles.active属性来决定运行环境时的配置文件。我们可以有多种不同的配置文件,这样我们在同样一个项目里,这里面每一组就相当于是我们一个profile,比如说我们常见的有开发的dev,测试的test,然后可能还有prepared预生产以及生产环境prod。当我们有很多种不同的配置文件,开发环境的它里面配置的参数都是我们在开发环境使用的,比如连接数据库,连接字符串,用URL也是我们开发环境数据库。那么测试环境这套配置里面可能连的就是测试环境的数据库。这个时候我们就可以通过spring.profiles.active来控制我们当前的run起来这套程序他到底是哪套环境。
- EnableAutoConfiguration默认对于依赖的starter进行自动装载。就把我们引进来的这些要自动装配的starter把他们装配起来。特别是如果我们引入的这些starter自己上面又加了一些Ebable相关的条件,这时候可能我们还需要单独使用一些Enable,比如Enable Double这样的一些开关,把我们这些组件整个最上层的开关再打开。
- spring-boot-starter-web中默认包含spring-mvc相关依赖以及内置的web容器,使得构建一个web应用更加简单。Spring最开始它让我们做J2EE的应用程序,脱离掉了J2EE的容器,脱离掉了EJB的容器。现在SpringBoot向前再迈进了一步,直接帮我们做J2EE应用程序,做Web应用程序,对web容器的依赖也都去掉了。这样我们构建一个web应用程序,构建一个J2EE的一个应用程序就特别的简单,什么依赖都不需要了。Spring自己就把他全部搞定。
自动化配置原理
说了那么多,那么SpringBoot这两大核心原理:自动装配,自动配置和starter机制这种拟合的脚手架,到底是什么原理。怎么run起来的,怎么起作用的。这就是我们接下来讲的重点。
这里有一个自动化配置的简化的一个模型:
首先我们每个SpringBoot程序都需要有一个入口,这个入口里面需要有一个入口函数,就是我们静态的main方法。这里面我们可用SpringApplication,这是SpringBoot自带的。.run当前这个类,在这个类上我们可以用@EnableAutoConfiguration。
有了这样一个入口,接下来我们可以定义一个我们需要配置的比如一个WebConfiguration这样一个类。大家需要注意的是,这个类作为一个配置类,我们就需要在它上面加一个注解,@Configuration表示它是用来做配置的类。然后在它上面需要加两个注解。一个是跟刚才一样@Configuration表示他是一个配置的类,另外一个注解也非常重要,就是@Import(WebConfiguration.class),表明这个AutoConfiguration类是用来自动的装配WebConfiguration这个类的。然后接下来我们也可以在当前的应用程序里,在resources/META-INF下面,创建一个文件,spring.factories,在里面把我们这个WebAutoConfiguration,自动配置的这个类给注进去,前面的key是EnableAutoConfiguration:这个类的权限类名称。然后他的值就是我们刚才写的这个WebAutoConfiguration。
当SpringBoot他扫描所有的jar包里,只要在里面找到了META-INF下面有spring.factories文件,就会把里面这些AutoConfiguration的这样一些类全部拉出来,然后按需要进行配置。
代码示例:
当我们很多情况下啥也不配,都会取默认值。如果在配置文件里指定一些配置值,就会把默认值覆盖。甚至我们可以做一些在配置里,前缀有这些匹配的驱动的部分,当我们配置了这样一些前缀的属性在配置文件里,他才会真正的去初始化这样的一些组件。这就是所谓的各种条件化装配。
此外SpringBoot在他自己启动的时候也会加载大量的默认可以自动化配置这样的一些组件。都在spring.factories文件中。
SpringBoot自动配置注解
@SpringBootApplication
SpringBoot应用标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot就会运行这个类的main方法来启动SpringBoot项目。
@SpringBootApplication上面组合了其他的注解:
@SpringBootConfiguration。
这块就是SpringBoot自己需要去做刚才那些自定义的一些加载,需要去做装配的东西。
@EnableAutoConfiguration。
自动的配置,如果我们有很多自定义的、自动配置的东西,通过他来拉。
@AutoConfigurationPackage。
有些包要去扫描,要去把他下面的很多文件需要自动配置的东西扫描,然后进行配置。
@Import(AutoConfigurationImportSelector.class)。
前面我们看到SpringBoot里面可能要加载的有非常非常多的AutoConfiguration相关的类,这些类里面哪些是需要加载的,哪些是不需要加载的,怎么来从中选择一些我们需要的,这个注解就会帮我们做这一系列的事情。最终就会把那些我们现在SpringBoot项目需要的那些依赖需要的那些组件,需要的那些AutoConfiguration转配进来。把他们拉起来。再用我们的各种参数来组装他们,最终就变成了我们Spring容器里的装配好,配置好了这些Bean,然后我们就可以使用了。
加载所有META-INF/spring.factories中存在的配置类(类似SpringMVC中加载所有converter)。
条件化自动配置
所谓的条件化自动配置,为什么会有这个东西?大家想一想,我们配置了那么多那么多东西,他们如果相互之间运行期冲突了怎么办?比如说我们引入了两个数据库连接池,每一个数据库连接池,它的自动化装配这块都想用自己来创建一个数据库连接池。但是我们一般情况下一个应用系统只有一个数据库,这时候有两个,Spring进行各种自动化的配置这种注入的时候可能就报错了,不允许有两个,怎么办呢?这个时候我们就需要这些所谓的条件化的自动化配置。帮我们根据一些条件来判断,我们是要创建装配这个类的一个实例变成一个Bean放在我们的Spring容器里。还是我们这次初始化就不干了,当发生冲突的时候只有有的这种配置做出让步,才能把这次冲突消灭于无形。
- @ConditionalOnBean:当存在某个Bean的时候,目前要自动化配置,这个配置才开始启动配置,也就是说很可能我要依赖前面那个Bean,用前面那个Bean作为条件,或者它的一部分内容作为我这个Bean装配的时候需要的一些属性的值,我才能起来。这个时候就可以用这个条件化的自动配置。
- @ConditionalOnClass:如果我这个Bean整个要被自动化的配置起来,那么我需要某个Class是存在的,如果没有就没办法起来。
- @ConditionalOnMissingBean:如果我们有一组的Bean他们相互之间只能有一个,多的话就冲突了,就像我们刚才说的数据库连接池一样,那么我们就可以用这个。当这个类型的Bean不存在,我就自动初始化的配置出来一个。如果前面存在着某个自动化配置已经配置出来了,比如说一个数据库连接池,那么我就不初始化了,不配置了。这样的话就可以保证我们当前这个JVM内、Spring容器内只存在这种类型的Bean只有一个。就避免了冲突。
- @ConditionalOnProperty:当存在某种属性的时候,那我就开始配置。
- @ConditionalOnResource:当存在某种资源的时候开始配置。
- @ConditionalOnSingleCandidate:我们可能存在一个某种类型的Bean,也可能存在多个,这个时候Spring内其实有一种机制叫@Primary,就可以把其中的某一个指定成他是主要的。如果我们去注入一个的时候,就可以拿到这一个去注入。这时候他也是不冲突的,这种情况下我们就可以用这个注解。如果出现这种情况不管是一个还是多个,但是其中有一个主要的。也可以通过这个进行自动化配置。
- @ConditionalOnWebApplication:判断当前环境他是一个Web项目,那我就初始化这一堆,比如说Web需要的东西。如果当前环境不是Web项目,那这样的一些自动化配置就不起作用,就不配置了。
所以Conditional这一系列的条件化的自动配置,就让我们把整个Spring容器里的所有的Bean他们相互之间的关系、装配这个过程变得非常的灵活,虽然我们代码写好了,注解也写好了,但是根据我们运行时的一些状态,启动的一些顺序等等。包括我们的环境都不同,就可以让我们最终拉起来的这些Bean,他们是不同的。
此外我们还可以通过自定义的方式,我们再去额外的定义一些Conditional,定义一些我们需要的条件,匹配某个具体的一个条件。一个方式。比如一个系统的恒变量等等。总之这块你想要多灵活你就可以把它做到多灵活。通过这种运行时的灵活的配置,我们就使得一套程序可以适应不同的环境,产生不同的作用。