写在前面
鉴于有人留言说想要学习SpringBoot相关的知识,我这里打算写一个SpringBoot系列的相关博文,目标呢是想让看了这一系列博文的同学呢,能够对SpringBoot略窥门径,这一系列的博文初步都定下来包括SpringBoot介绍、入门、配置、日志相关、web开发、数据访问、结合docker、缓存、消息队列、检索、任务安全、分布式等等一系列的博文,工作量很大,是个漫长的过程,每一步我都尽量详细,配上截图说明,也希望对看的同学真的有用。
单纯就是想分享技术博文,还想说一句就是,如果觉得有用,请点个关注、给个赞吧,也算对我来说是个宽慰,毕竟也得掉不少头发,嘿嘿嘿
系列文章传送条
详细SpringBoot教程之入门(一)
详细SpringBoot教程之入门(二)
详细SpringBoot教程之配置文件(一)
详细SpringBoot教程之配置文件(二)
详细SpringBoot教程之日志框架
详细SpringBoot教程之Web开发(一)
详细SpringBoot教程之Web开发(二)
详细SpringBoot教程之Web开发(三)
详细SpringBoot教程之数据访问
详细SpringBoot教程之启动配置原理
详细SpringBoot教程之缓存开发
配置映射的另一种方式
上一篇博文我们进行了配置文件的编写,映射,注入以及如何进行测试相关的操作,在这里我们再来介绍另一种配置文件的映射注入,即使用其他注解的方式进行注入,我们这里要使用的注解是@Value,@Value是Spring的底层注解。
我们回想一下我们以前写SpringMvc的配置文件的时候,我们在配置文件中使用bean进行配置,然后进行相关映射,还记得不,不记得我这里写出一个bean配置回忆一下,内容如下:
1 | <bean class="Person"> |
发现没有,以前的方式是我们通过一个一个给属性赋值,比如这里我们给lastName赋值,值就是value指定的。可能你已经意识到了,没错,@Value的作用其实和这里的value一样,相当于Spring帮我们简化了,只需要通过一个注解就能赋值,不过也是一个一个赋值哦,如何使用以及运行结果如下
发现没有,结果依旧可以获取值,所以以为这我们依旧配置成功。
@ConfigurationProperties和@Value比较
我们上一篇博文使用的是@ConfigurationProperties映射yml或者properties配置文件,这篇博文前面我们举例使用@Value进行配置,那么这两者有什么区别呢?首先我这里给一个比较官方的区别
| | @ConfigurationProperties | @Value |
|–|–| – |
| 功能 | 批量注入配置文件中的属性 | 一个个指定 |
| 松散绑定| 支持 | 不支持 |
| SpELl | 不支持 | 支持 |
| JSR30数据校验| 支持 | 不支持 |
| 复杂类型封装| 支持 | 不支持 |
那么这里来解释一下什么是松散绑定呢?其实我们前面已经接触过了,只是不知道那是松散语法而已。比如我们有一个属性时lastName,按道理我们在配置文件中的属性名和Person类中的属性名需要一直对不对,不然没办法映射,但是在松散语法中,lastName和last-name是认为同一个,这种写法就是松散写法,也就是说,我们在配置文件中,person.lastName和person.last-name两种写法都可以,但是@Value是不支持松散写法的,属性名必须一致。
还有就是JSR30数据校验,其实我们以前写Spring项目的时候基本都用过,就是校验器,比如对属性是否符合邮箱格式进行校验,不符合提示错误等,而配置文件注入值数据校验,只能使用ConfigurationProperties,如下
所以如果说我们只是在某个业务逻辑中需要获取一下配置文件中的某项值,可以使用@Value比较简洁。而如果说我们专门编写一个javaBean来和配置文件进行映射,我们就直接使用ConfigurationProperties。
@PropertySource和@ImportResource比较
@PropertySource
我们前面在Person类上直接使用@ConfigurationProperties或者@Value进行映射,SpringBoot都是默认到主配置文件中寻找。而@PropertySource就是用来加载指定的配置文件。
怎么理解呢?比如我们的application.properties和application.Yml是全局配置文件,我们原先把配置内容都是写到全局配置文件中的,然后我们使用@ConfigurationProperties或者@Value进行映射,也就是说@ConfigurationProperties或者@Value是默认映射全局配置文件的,那么如果项目一旦很大,我们不可能所有配置都写到全局配置文件中,所以这个时候我们需要创建一些其他的配置文件,然后指定映射。
可能直接讲不容易理解,下面我举个例子,我们在resources下面创建一个person.properties,然后把application.properties里面的内容粘贴到person.properties中,这个时候我们通过@PropertySource,将Person类和person.properties进行映射,如下图。
@ImportResource
@ImportResource是用来导入原先Spring的配置文件,让配置文件里面的内容生效,也就是说,如果想按照以前SpringMVC的那种出入配置,我们可以使用这个注解来完成。
为了更好的理解,我们先在resourcs下创建一个bean.xml(就是以前Spring的配置文件),然后按照以前的方式写一个bean,如下内容
然后我们新写一个测试类,使用ApplicationContext容器,判断容器中是否注入了helloService,内容如下
然后启动测试类,运行之后查看发现helloService没有被注入,日志输出如下
这个时候就可以用到我们的ImportResource了,SpringBoot里面没有Spring的配置文件,我们自己编写的Spring配置文件也不能自动识别,想让Spring的配置文件生效,加载进容器,就可以使用@ImportResource标注在一个配置类上,这里我们在主配置类上标注如下,再次运行测试类
当然我们实际开发中不可能像这样使用注解一个一个导入,而我们SpringBoot推荐给容器中添加组件的方式,也就是使用全注解的方式。怎么理解呢?就是我们专门创建一些配置类,用来同意管理项目中所有的配置注入。举个例子,我们专门创建一个配置类,如下创建一个config包,然后创建一个MyAppConfig类,内容如下
我们现在来测试一下,注意了,测试之前先把主配置类里面的@ImportResource去掉,我不使用@ImportResource注解,启动测试类,helloService注入依旧成功。
配置文件占位符
RandomValuePropertySource:配置文件中可以使用随机数如
1 | ${random.value} |
还有属性配置占位符
1 | app.name=MyApp |
可以在配置文件中引用前面配置过的属性(优先级前面配置过的这里都能使用)而可以通过${app.name:默认值}来指定找不到属性时的默认值,还是一样,我在配置文件中举个例子帮助理解
profile快速切换配置
profile是Spring对不同环境提供不同配置功能的支持,可以通过激活、指定参数等方式快速切换环境,通俗点理解就是,开发环境的配置,生产环境的配置,测试环境的配置分开,使用profile可以进行快速切换
激活方式(即切换方式)
- 命令行: –spring.profiles.active=dev (打包之后,使用命令行执行的时候,指定参数)
- 配置文件:spring.profiles.active=dev
- Jvm参数 -Dspring.profiles.active=dev (在Idea修改参数)
多profile文件形式
有了激活方式,我们来看怎么使用,首先我们创建三种配置(包括原来的application.properties在内的三个),一般的配置文件格式:application-{profile}.properties。比如application-dev.properties、application-prod.properties。创建之后,我们可以在另外两个配置文件中配置我们想要的配置,然后在主配置文件中进行激活即可,如下
多profile文档块模式
上面是通过properties进行多profile的配置,我们也可以使用yml来进行多profile的配置,yml的配置更简洁,不需要额外创建像application-dev.properties、application-prod.properties这样的配置文件,只需要在主yml配置文件中,使用“—”的文档块模式,如下图
配置文件加载位置
SpringBoot启动会扫描以下位置的application.properties或者application.yml文件作为SpringBoot的默认配置文件,也就是说我们可以更改这两个配置文件的位置
- file:./config/
- file:./
- classpath:/config/
- classpath:/
说明一下classpath是在resources目录位置,file是在项目根目录的位置,以上是按照优先级从高到低的顺序,所有位置的文件都会被加载,高优先级配置内容会覆盖低优先级配置内容,配置文件的内容是互补的,也就是说高优先级中没有的配置,如果低优先级有的话一样会生效,但是如果高优先级的配置中已经有了一项配置,低优先级中相同的配置就会被覆盖掉。
我们也可以通过配置spring.config.location来改变默认配置,也就是说配置文件可以放在任意的位置,网络上或者电脑桌面任意位置,把位置路径赋给location就可以了,这样对于打包后的项目,使用命令行做运维
外部配置的加载顺序
- SpringBoot支持多种外部配置方式
- 命令行参数
- 来自java:comp/env的JNDI属性
- Java系统属性(System.getProperties)
- 操作系统环境变量
- RandomValuePropertySource配置的random.*属性值
- Jar包外的application-{profile}.properties或application.yml(带spring.profile)配置文件
- Jar包内的application-{profile}.properties或application.yml(带spring.profile)配置文件
- Jar包外的application.properties或application.yml(不带spring.profile)配置文件
- Jar包外的application.properties或application.yml(不带spring.profile)配置文件
- @Configuration注解类上的@PropertySource
- 通过SpringApplication.setDefaultProperties指定的默认属性
也就是说,除了我们项目运行配置的文件,我们可以在任何外部环境中,继续配置相关文件进行运行,及时项目已经被打包了,我们也可以使用相关命令行指令,运行在某个位置我们的配置文件,从而启动项目,非常方便运维对项目进行维护而不需要重新打包项目。
自动配置原理
前面我们其实有提高过自动配置类,即@EnableAutoConfiguration,不过只是带过了他的作用,没有深入讨论,这里我们进行深入讨论,首先先放出所有自动配置文件属性的参考,官网文档位置
然后,我们这里通过观察HttpEncodingAutoConfiguration的这个自动配置来讲解,可以在如下位置中找到
打开HttpEncodingAutoConfiguration,进入到里面,我们逐个进行分析
点进HttpProperties.class我们会惊奇的发现,里面也是使用@ConfigurationProperties(prefix = “spring.http”)进行配置,这和我们之前在application.properties中手动配置相关组件的时候,使用的是一样的方式,所以能不能体会到,其实我们可以自行配置更多组件,并进行封装
这里我们就可以总结一下自动配置文件的通用模式(即通过命名分辨)
- xxxAutoConfiguration:自动配置类
- xxxProperties:属性配置类
也就是说,我们yml/properties文件中能配置的值就来源于属性配置类
@EnableAutoConfiguration 的作用是利用AutoConfigurationImportSelector给容器中导入一些组件,详细的可以查看SpringFactoriesLoader.loadFactoryNames
意思是扫描所有jar包类路径下,META-INF/spring.factories。把扫描到的这些文件的内容装成properties对象,然后从properties中获取到EnableAutoConfiguration.class类对应的值,然后把它们添加到容器中,也就是将类路径下META-INF/spring.factories里面配置的所有EnableAutoConfiguration的值加入到容器中,类似下面这些自动配置类
1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ |
所有在配置文件中能配置的属性都是在xxxProperties类中封装这,配置文件能配置什么就可以参照某个功能对应的这个属性类,根据当前不同条件判断,决定这个配置是否生效。SpringBoot启动会加载大量的自动配置类,如果我们需要的功能有没有SpringBoot默认写好的自动配置类,那我们就手动引入或配置,在开发中,我们看这个自动配置类中到底配置了哪些组件,只要我们要用的组件存在,我们就不需要再来配置了。给容器中自动配置类添加组件的时候,会从properties类中获取某些属性,我们就可以在配置文件中指定配置属性的值。
细节
- @Conditional由Spring提供,而在Spring Boot中衍生出了以下相关的注解:
- @ConditionalOnBean:当容器中有指定Bean的条件下。
- @ConditionalOnClass:当classpath类路径下有指定类的条件下。
- @ConditionalOnCloudPlatform:当指定的云平台处于active状态时。
- @ConditionalOnExpression:基于SpEL表达式的条件判断。
- @ConditionalOnJava:基于JVM版本作为判断条件。
- @ConditionalOnJndi:在JNDI存在的条件下查找指定的位置。
- @ConditionalOnMissingBean:当容器里没有指定Bean的条件。
- @ConditionalOnMissingClass:当类路径下没有指定类的条件下。
- @ConditionalOnNotWebApplication:当项目不是一个Web项目的条件下。
- @ConditionalOnProperty:当指定的属性有指定的值的条件下。
- @ConditionalOnResource:类路径是否有指定的值。
- @ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者有多个但是指定了首选的Bean。
- @ConditionalOnWebApplication:当项目是一个Web项目的条件下。
以上组合注解均位于spring-boot-autoconfigure jar包下的org.springframework.boot.autoconfigure.condition包
Debug查看详细的自动配置报告
SpringBoot的debug功能还是非常有帮助的,比如它可以帮助我们查看项目加载注入了哪些自动配置类,从而使得我们不用一个类一个类点入查看。SpringBoot启动的时候,加载主配置类,进而开启了自动配置功能@EnableAutoConfiguration。
因此,我们可以在配置文件中开启debug=true,来控制打印自动配置报告,这样我们就可以很方便的知道自动配置类生效