1. SpringBoot的概念
1.1什么是Spring[回顾]
Spring是一个开源框架,2003 年兴起的一个轻量级的Java 开发框架,作者:Rod Johnson 。Spring是为了解决企业级应用开发的复杂性而创建的,简化开发。
为了降低Java开发的复杂性,Spring采用了以下4种关键策略:
- 基于POJO的轻量级和最小侵入性编程,所有东西都是bean;
- 通过IOC,依赖注入(DI)和面向接口实现松耦合;
- 基于切面(AOP)和惯例进行声明式编程;
- 通过切面和模版减少样式代码,RedisTemplate,xxxTemplate。
1.2 什么是SpringBoot
什么是SpringBoot?SpringBoot就是一个javaweb的开发框架,和SpringMVC类似,对比其他javaweb框架的好处,官方说是简化开发,约定大于配置, you can “just run”,能迅速的开发web应用,几行代码开发一个http接口。
所有的技术框架的发展似乎都遵循了一条主线规律:从一个复杂应用场景衍生 一种规范框架,人们只需要进行各种配置而不需要自己去实现它,这时候强大的配置功能成了优点;发展到一定程度之后,人们根据实际生产应用情况,选取其中实用功能和设计精华,重构出一些轻量级的框架;之后为了提高开发效率,嫌弃原先的各类配置过于麻烦,于是开始提倡“约定大于配置”,进而衍生出一些一站式的解决方案。
是的,这就是Java企业级应用->J2EE->spring->springboot的过程。
随着 Spring 不断的发展,涉及的领域越来越多,项目整合开发需要配合各种各样的文件,慢慢变得不那么易用简单,违背了最初的理念,甚至人称配置地狱。Spring Boot 正是在这样的一个背景下被抽象出来的开发框架,目的为了让大家更容易的使用 Spring 、更容易的集成各种常用的中间件、开源软件;Spring Boot 基于 Spring 开发,Spirng Boot 本身并不提供 Spring 框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序。也就是说,它并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具。Spring Boot 以约定大于配置的核心思想,默认帮我们进行了很多设置,多数 Spring Boot 应用只需要很少的 Spring 配置。同时它集成了大量常用的第三方库配置(例如 Redis、MongoDB、Jpa、RabbitMQ、Quartz 等等),Spring Boot 应用中这些第三方库几乎可以零配置的开箱即用。
简单来说就是SpringBoot其实不是什么新的框架,它默认配置了很多框架的使用方式,就像maven整合了所有的jar包,spring boot整合了所有的框架 。Spring Boot 出生名门,从一开始就站在一个比较高的起点,又经过这几年的发展,生态足够完善,Spring Boot 已经当之无愧成为 Java 领域最热门的技术。
1.3 为什么要学习SpringBoot
从1.2小节我们可以知道当前 Spring 的缺点:
- 复杂的配置
项目各种配置是开发时的损耗, 写配置挤占了写应用程序逻辑的时间。 - 混乱的依赖管理
项目的依赖管理非常的繁琐。决定项目里要用哪些库就已经够让人头痛的了,你还要知道这些库的哪个版本和其他库不会有冲突,这是一个棘手的问题。并且,一旦选错了依赖的版本,随之而来的就是各种的不兼容的bug。
SpringBoot可以帮助我们解决上述两个问题。
1.4 SpringBoot的特点
Spring Boot的主要优点:
- 为所有Spring开发者更快的入门,它是快速开发spring应用的框架;
- 开箱即用,提供各种默认配置来简化项目配置
- 内嵌式容器(例如tomcat和jetty容器)简化Web项目,不需要单独安装容器,jar包直接发布一个web应用;
- 没有冗余代码生成和XML配置的要求;
- 简化maven配置,parent这种方式,一站式引入需要的各种依赖;
- 基于注解的零配置思想;
- 和各种流行框架(SpringMVC、Mybatis、Spring Cloud)无缝整合。
总结:SpringBoot 是Spring快速开发脚手架,通过约定大于配置,优化了混乱的依赖管理,和复杂的配置,让我们用java -jar方式,运行启动java web项目。
2. SpringBoot入门案例
2.1 准备工作
- jdk 1.8.0_261;
- Maven-3.6.3;
- SpringBoot 2.x;
- IDEA 2020.2.3 X64
2.2 创建项目
2.2.1 使用Spring Initializr 的 Web页面创建项目
-
点击上方链接,或者手动打开 https://start.spring.io/ ;
-
填写项目信息;
-
点击“Generate”按钮生成项目,下载此项目;
-
解压项目包,并用IDEA以Maven项目导入,一路下一步即可,直到项目导入完毕。
-
如果是第一次使用,可能速度会比较慢,包比较多、需要耐心等待一切就绪。
2.2.2 使用 IDEA 直接创建项目
- 创建一个新项目;
- 选择spring initalizr , 可以看到默认就是去官网的快速构建工具那里实现;
- 填写项目信息;
- 选择初始化的依赖组件(初学勾选 Web 即可)
- 填写项目路径;
- 等待项目构建成功;
- 右键项目,选择“Add Frameworks Support”,添加Maven依赖。
2.3 生成项目分析
2.3.1 项目结构目录
2.3.2 POM.xml文件
其中项目的pom文件已经自动生成:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--
SpringBoot提供了一个名为spring-boot-starter-parent的构件,里面已经对各种常用依赖(并非全部)的版本进行了管理,
我们的项目需要以这个项目为父工程,这样我们就不用操心依赖的版本问题了,需要什么依赖,直接引入坐标即可!
-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.lijinghua</groupId>
<artifactId>springboot01</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot01</name>
<description>Demo project for Spring Boot</description>
<!--jdk版本-->
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--为了让SpringBoot帮我们完成各种自动配置,我们必须引入SpringBoot提供的自动配置依赖,我们称为 启动器 。
因为我们是web项目,这里我们引入web启动器
需要注意的是,我们并没有在这里指定版本信息。因为SpringBoot的父工程已经对版本进行了管理了。
-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- springboot单元测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 打包插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3.1小节会深入介绍spring-boot-starter-parent
2.3.3 项目依赖
这个时候,我们会发现项目中出现了大量的依赖:
这些都是SpringBoot根据spring-boot-starter-web这个依赖自动引入的,而且所有的版本都已经管理好,不会出现冲突。
2.3.4 启动类
其中,项目自动生成了一个启动类 Springboot01Application (默认为项目名+Application):
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Springboot01Application {
public static void main(String[] args) {
SpringApplication.run(Springboot01Application.class, args);
}
}
通过运行这个启动类中的main方法,SpringBoot就可以通过内置的tomcat容器,启动一个生产级别的web应用。
2.4 编写Controller层
-
在主程序的同级目录下,新建一个controller包;
一定要在启动类所在包或子包目录下,否则识别不到!!后面的3.2.4小节给出了答案。
-
在包中新建一个HelloController类
@RestController//== @ResponseBody+@Controller public class HelloController { @RequestMapping(value = "hello",method = RequestMethod.GET) public String hello(){ return "hello SpringBoot!"; } }
2.5 启动测试
-
接下来,我们运行main函数,查看控制台:
-
浏览器发起请求:
后台打印的信息看出,第一次发起请求时,实例化DispacherServlet。
2.6 将项目打包运行
-
将项目打成jar包,点击 maven的 package;
注意,如果该操作报错,可以配置打包时跳过项目运行测试用例:
<!-- 在工作中,很多情况下我们打包是不想执行测试用例的,跳过测试用例执行 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <!--跳过项目运行测试用例--> <skipTests>true</skipTests> </configuration> </plugin>
-
运行jar包
到jar包所在的目录下运行命令:
java -jar xxx.jar
3. SpringBoot自动配置原理
通过第2节的简单配置,我们就可以启动一个web应用了,那么它到底是怎么运行的呢?下面就简单说说SpringBoot的自动配置原理。
3.1 POM.xml
3.1.1 父依赖 spring-boot-starter-parent
首先它主要是依赖一个父项目,对各种常用依赖(并非全部)的版本进行了管理!
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
ctrl+左击"spring-boot-starter-parent" ,进去发现还有一个父依赖,这里才是真正管理SpringBoot应用里面所有依赖版本的地方 :
再ctrl+左击"spring-boot-dependencies",进去发现有个属性列表管理着很多版本:
因此,就像前面提到的那样,以后我们导入依赖默认是不需要写版本;但是如果导入的包没有在依赖中管理着就需要手动配置版本。
3.1.2 启动器 spring-boot-starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
- springboot-boot-starter-xxx:就是SpringBoot的场景启动器。
- spring-boot-starter-web:帮我们导入了web模块正常运行所依赖的组件。
SpringBoot将所有的功能场景都抽取出来,做成一个个的starter (启动器),只需要在项目中引入这些 starter 即可,所有相关的依赖都会导入进来,我们要用什么功能就导入什么样的场景启动器即可;我们未来也可以自己自定义 starter。
3.2 主启动类
//@SpringBootApplication 来标注一个主程序类,说明这是一个Spring Boot应用
@SpringBootApplication
public class Springboot01Application {
public static void main(String[] args) {
SpringApplication.run(Springboot01Application.class, args);
}
}
通过运行这个启动类中的main方法,SpringBoot就可以通过内置的tomcat容器,启动一个生产级别的web应用。那么它帮我们干了什么呢?往下看-------------------------------------
3.2.1 注解 @SpringBootApplication
-
作用
标注在某个类上说明这个类是SpringBoot的主启动类 ,SpringBoot就应该运行这个类的main方法来启动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 { //...... }
3.2.2 注解 @ComponentScan
这个注解在Spring中很重要 ,它对应XML配置中的组件扫描元素。
-
作用
配置之后会自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中。
那么符合条件的意思又是什么呢??先留个坑,后面会解释。
3.2.3 注解 @SpringBootConfiguration
@Configuration
public @interface SpringBootConfiguration {}
-
作用
SpringBoot的配置类,标注在某个类上,表示这是一个SpringBoot的配置类。
-
我们继续进去注解查看@Configuration:
@Component public @interface Configuration {}
- 这里的 @Configuration:说明这是一个配置类 ,配置类就是对应Spring的xml 配置文件;
- 里面的 @Component :说明启动类本身也是Spring中的一个组件而已,负责启动应用!
3.2.4 注解 @EnableAutoConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
-
作用
开启自动配置功能。以前我们需要自己配置的东西,而现在SpringBoot可以自动帮我们配置 ; @EnableAutoConfiguration告诉SpringBoot开启自动配置功能,这样自动配置才能生效。
基于你所添加的依赖,SpringBoot会去猜测你大概需要什么样的配置,然后去找默认配置项加载,往下看。
-
我们继续进去@AutoConfigurationPackage查看:
@AutoConfigurationPackage 的作用是, 帮助我们自动配置包。
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import({Registrar.class}) public @interface AutoConfigurationPackage { String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; }
-
@import
Spring底层注解@import ,给容器中导入一个组件;
-
Registrar.class
将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器。
这也就是说明了,为什么之前说一定要把代码都写到启动类所在目录及子目录下的原因。 现在知道了组件扫描会扫描的符合条件的bean是什么意思了吧。
-
-
@Import({AutoConfigurationImportSelector.class})
这里也是在给容器导入组件,AutoConfigurationImportSelector.class会自动配置导入选择器。
-
查看AutoConfigurationImportSelector.class源码:
// 获得候选的配置 protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { //这里的getSpringFactoriesLoaderFactoryClass()方法 //返回的就是我们最开始看的启动自动导入配置文件的注解类;EnableAutoConfiguration List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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; }
-
这个方法又调用了 SpringFactoriesLoader 类的静态方法loadFactoryNames:
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); //这里它又调用了 loadSpringFactories 方法 return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); }
-
继续点击查看 loadSpringFactories 方法:
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { //获得classLoader, 我们返回可以看到这里得到的就是EnableAutoConfiguration标注的类本身 MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader); if (result != null) { return result; } else { try { //去获取一个资源 "META-INF/spring.factories" Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories"); LinkedMultiValueMap result = new LinkedMultiValueMap(); //将读取到的资源遍历,封装成为一个Properties 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 factoryClassName = ((String)entry.getKey()).trim(); String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue()); int var10 = var9.length; for(int var11 = 0; var11 < var10; ++var11) { String factoryName = var9[var11]; result.add(factoryClassName, factoryName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException var13) { throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13); } } }
-
发现多次提到了一个文件:spring.factories
这个文件在autoconfigure包下,里面有很多自动配置的文件,这就是自动配置的根源。随便点一个自动配置类进去看,发现这个配置类中注入了一些Bean。
而且里面的配置类上有几个重要的注解(以WebMvcAutoConfiguration 为例):
-
@Configuration
声明这个类是一个配置类
-
@ConditionalOnWebApplication(type = Type.SERVLET)
这里的意思就是判断当前应用是否是web应用,如果是,当前配置类生效。
-
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
这里的条件是OnClass,也就是满足以下类存在:Servlet、DispatcherServlet、WebMvcConfigurer,其中Servlet只要引入了tomcat依赖自然会有,后两个需要引入SpringMVC才会有。这里就是判断你是否引入了相关依赖,引入依赖后该条件成立,当前类的配置才会生效! -
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
这个条件与上面不同,OnMissingBean,是说环境中没有指定的Bean这个才生效。其实这就是自定义配置的入口,也就是说,如果我们自己配置了一个WebMVCConfigurationSupport的类,那么这个默认配置就会失效。
总之!!也就是通过当前条件的判断,决定这个配置类是否生效!
-
-
总结
-
SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值;
-
将这些值作为自动配置类导入容器 ,自动配置类就生效 ,帮我们进行自动配置工作;
通过反射实例化为对应标注了@Configuration的JavaConfig形式的IOC容器配置类 , 然后将这些都汇总成为一个个实例并加载到IOC容器中。
-
整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;
-
它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 ,并配置好这些组件;
-
有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作。
-
3.2.5 SpringApplication类
@SpringBootApplication
public class SpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
}
我们之前是通过运行这个main方法去启动一个应用的,它是怎么运行的呢?这个类实例化时主要做了以下四件事情:
- 推断应用的类型是普通的项目还是Web项目;
- 查找并加载所有可用初始化器 , 设置到initializers属性中;
- 找出所有的应用程序监听器,设置到listeners属性中;
- 推断并设置main方法的定义类,找到运行的主类。
//SpringApplication的构造器
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
// ......
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.setInitializers(this.getSpringFactoriesInstances();
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
4. 全注解配置和属性注入
在入门案例中,我们没有任何的配置,就可以实现一个SpringMVC的项目了,快速、高效!但是可能还会有疑问,如果没有任何的xml,那么我们如果要配置一个Bean该怎么办?
4.1 Spring配置历史[了解前提]
-
Spring1.0时代
在此时因为jdk1.5刚刚出来,注解开发并未盛行,因此一切Spring配置都是xml格式,想象一下所有的bean都用xml配置,细思极恐啊,心疼那个时候的程序员2秒。
-
Spring2.0时代
Spring引入了注解开发,但是因为并不完善,因此并未完全替代xml,此时的程序员往往是把xml与注解进行结合,貌似我们之前都是这种方式。
-
Spring3.0及以后
3.0以后Spring的注解已经非常完善了,因此Spring推荐大家使用完全的java配置来代替以前的xml,不过似乎在国内并未推广盛行。然后当SpringBoot来临,人们才慢慢认识到java配置的优雅。
4.2 YAML语法学习[了解前提]
4.2.1 YAML概述
YAML是 “YAML Ain’t a Markup Language” (YAML不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:“Yet Another Markup Language”(仍是一种标记语言)。
这种语言以数据做为中心,而不是以标记语言为重点!
4.2.2 YAML的作用
作为配置文件,配置bean属性或者修改SpringBoot自动配置的默认值,因为SpringBoot在底层都给我们自动配置好了。使用YAML作为配置文件使用时开发中最常用的,也是SpringBoot所推荐的。
打个比方,我们可以在配置文件中修改Tomcat 默认启动的端口号!测试一下!
server:
port: 8081
springboot 使用一个全局的配置文件application.properties,或者是 application.yml,上述配置需要在这里配置。
4.2.3 YAML语法结构
以前的配置文件,大多数都是使用xml来配置;比如一个简单的端口配置,我们来对比下yaml和xml、properties:
-
传统xml配置
<server> <port>8081<port> </server>
-
properties配置
- 语法结构 : key=value
server.port=8081
-
yaml配置
- 语法结构 :key:空格 value
server: prot: 8081
4.2.4 YAML语法
- 空格不能省略;
- 以缩进来控制层级关系,只要是左边对齐的一列数据都是同一个层级的;
- 属性和值的大小写都是十分敏感的。
说明:语法要求严格!
4.2.4.1 字面量
字面量直接写在后面就可以 ,字符串默认不用加上双引号或者单引号。
#字面量:普通的值 [数字/布尔值/字符串]
k: v
注意:
“ ” 双引号,会转义字符串里面的特殊字符 , 特殊字符会作为本身想表示的意思;
name: "li \n jinghua" #输出: li 换行 jinghua
‘’ 单引号,不会转义特殊字符 , 特殊字符最终会变成和普通字符一样输出。
name: 'li \n jinghua' #输出: li \n jinghua
4.2.4.2 对象与Map
student:
name: lijinghua
age: 23
行内写法:
student: {name: lijinghua,age: 23}
4.2.4.3 数组(List、Set)
用 - 值表示数组中的一个元素,比如:
pets:
- cat
- dog
- pig
行内写法:
pets: [cat,dog,pig]
4.2.5 YAML中的随机数与占位符$
#示例如下:
person:
name: lijinghua${random.uuid} # 随机uuid
age: ${random.int} # 随机int
dog:
# 引用person.dogName 的值,如果不存在就用 :后面的值,即 other,然后拼接上_旺财
name: ${person.dogName:other}_旺财
age: 1
4.3 全注解配置和属性注入的方式
4.3.1 Spring和SpringBoot中的常用注解
Spring全注解配置主要靠java类和一些注解,比较常用的注解有:
-
@Configuration
这是 Spring 3.0 添加的一个注解,声明一个类作为配置类,代替xml文件。
-
@Bean
声明在方法上,Spring会自动调用该方法,将方法的返回值加入Bean容器,代替 < bean > 标签。
默认bean名称为方法名,可以通过@Bean(“自定义名字”),来指定新的对象名。
-
@Value
属性注入。
-
@PropertySource
指定外部属性文件。默认不支持YAML文件(4.3.3小节叙述了使用YAML时怎么指定文件)。
格式–> classpath:要指定的文件
-
@ConfigurationProperties
是SpringBoot提供的一种新的属性注入方式,支持各种java基本数据类型及复杂类型的注入。 默认从全局配置文件中获取值。
-
@EnableConfigurationProperties
一般配合@ConfigurationProperties使用。
-
示例如下:
@ConfigurationProperties(prefix = "jdbc") public class JdbcProperties { private String url; private String driverClassName; private String username; private String password; // ... 略 } @Configuration //在Spring容器中创建属性读取类JdbcProperties的实例 //并声明要使用 JdbcProperties 这个类的对象,这里起到充当配置文件的作用 @EnableConfigurationProperties(JdbcProperties.class) public class JdbcConfig { @Bean public DataSource dataSource(JdbcProperties jdbc) { DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl(jdbc.getUrl()); dataSource.setDriverClassName(jdbc.getDriverClassName()); dataSource.setUsername(jdbc.getUsername()); dataSource.setPassword(jdbc.getPassword()); return dataSource; } }
-
注入方式
-
@Autowired属性注入
@Autowired private JdbcProperties prop;
-
构造方法注入
private JdbcProperties prop; public JdbcConfig(Jdbcproperties prop){ this.prop = prop; }
-
声明有@Bean的方法参数注入(上面示例)
@Bean public Datasource dataSource(JdbcProperties prop){/*...*/}
注意,这里相当于省略了个注解:
@Bean public DataSource dataSource(@Autowired JdbcProperties jdbc) {/*...*/}
-
-
4.3.2 使用properties进行全注解配置
接下来我们给个例子,用java类和注解来尝试实现连接池配置:
-
引入Druid连接池依赖
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.3</version> </dependency>
-
创建一个jdbc.properties文件,编写jdbc属性
jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/ssm_hospital jdbc.username=root jdbc.password=lijinghua
-
编写java配置类
@Configuration//声明这是一个配置类 @PropertySource("classpath:jdbc.properties")//指定外部的属性文件 @ConfigurationProperties(prefix = "jdbc")// 将配置文件中前缀为jdbc的所有属性一一映射过来;或者可以使用@Value注解一个个配置 public class JdbcConfig { //@Value("${jdbc.url}") String url; //@Value("${jdbc.driverClassName}") String driverClassName; //@Value("${jdbc.username}") String username; //@Value("${jdbc.password}") String password; @Bean//将方法的返回值加入Bean容器,默认名字为dataSource(与方法名相同) public DataSource dataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl(url); dataSource.setDriverClassName(driverClassName); dataSource.setUsername(username); dataSource.setPassword(password); return dataSource; } }
也可以直接在dataSource方法中直接使用@ConfigurationProperties:
@Configuration//声明这是一个配置类 @PropertySource("classpath:jdbc.properties")//指定外部的属性文件 public class JdbcConfig { @Bean//将方法的返回值加入Bean容器,默认名字为dataSource(与方法名相同) // 声明要注入的属性前缀,SpringBoot会自动把相关属性通过调用这个Bean(此处是DataSource)的set方法,注入到DataSource中 @ConfigurationProperties(prefix = "jdbc") public DataSource dataSource() { return new DruidDataSource(); } }
使用的前提是:该类必须有对应属性的set方法!
-
使用自动注入注解(Spring or JDK)注入DataSource
@Autowired private DataSource dataSource; @RequestMapping(value = "hello", method = RequestMethod.GET) public String hello() { return "hello SpringBoot!" + dataSource; }
4.3.3 使用YAML进行全注解配置
与4.3.2小节同样的例子:
-
引入Druid连接池依赖
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.3</version> </dependency>
-
编写application.yml文件,添加jdbc属性
jdbc: driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/ssm_hospital username: root jdbc.password: lijinghua
注意:这里写法上有些区别,因为@PropertySource注解默认是不支持加载YML文件的,所以我们在这里把配置信息写在application.yml中。如果要想指定配置文件,可以点击这里参考。
-
编写java配置类
@Configuration//声明这是一个配置类 public class JdbcConfig { @Bean//将方法的返回值加入Bean容器,默认名字为dataSource(与方法名相同) @ConfigurationProperties(prefix = "jdbc") public DataSource dataSource() { return new DruidDataSource(); } }
-
IDEA 提示,springboot配置注解处理器没有找到!
解决方法:
<!-- 导入配置文件处理器,配置文件进行绑定就会有提示,需要重启 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
-
使用自动注入注解(Spring or JDK)注入DataSource
@Autowired private DataSource dataSource; @RequestMapping(value = "hello", method = RequestMethod.GET) public String hello() { return "hello SpringBoot!" + dataSource; }
4.3.4 @Value与@ ConfigurationProperties对比总结
在我们使用全注解配置的时候,总会用到两个注解:@Value 和 @ConfigurationProperties。
-
对比
-
@ConfigurationProperties只需要写一次即可, @Value则需要每个字段都添加。谁方便就不用说了吧?
-
松散绑定:不严格要求属性文件中的属性名与成员变量名一致。支持驼峰,中划线,下划线等等转换,甚至支持对象引导。
比如我的yml中写的last-name,这个和lastName是一样的, - 后面跟着的字母默认是大写的。这就是松散绑定。
-
JSR303数据校验 , 这个就是我们可以在字段是增加一层过滤器验证, 可以保证数据的合法性。
示例:Springboot中可以用@Validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。
@Component //注册bean @ConfigurationProperties(prefix = "person") @Validated //数据校验 public class Person { @NotNull(message="名字不能为空") private String name; } /* (1)空检查 @Null 验证对象是否为null @NotNull 验证对象是否不为null, 无法查检长度为0的字符串 @NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格 @NotEmpty 检查约束元素是否为NULL或者是EMPTY. (2)Booelan检查 @AssertTrue 验证 Boolean 对象是否为 true @AssertFalse 验证 Boolean 对象是否为 false (3)长度检查 @Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内 @Length(min=, max=) string is between min and max included. (4)日期检查 @Past 验证 Date 和 Calendar 对象是否在当前时间之前 @Future 验证 Date 和 Calendar 对象是否在当前时间之后 @Pattern 验证 String 对象是否符合正则表达式的规则 .......等等,除此以外,我们还可以自定义一些数据校验规则 */
-
复杂类型封装,yml中可以封装对象 , 使用@Value就不支持。
-
-
总结
- 配置yml和配置properties都可以获取到值 , 强烈推荐 yml;
- 如果我们在某个业务中,只需要获取配置文件中的某个值,可以使用一下 @value;
- 如果说,我们专门编写了一个JavaBean来和配置文件进行一一映射,就直接@configurationProperties。
5. 多环境切换
profile是Spring对不同环境提供不同配置功能的支持,可以通过激活不同的环境版本,实现快速切换环境。
5.1 多配置文件
我们在主配置文件编写的时候,文件名可以是 application-{profile}.properties/yml , 用来指定多个环境版本。
例如:application-test.properties 代表测试环境配置; application-dev.properties 代表开发环境配置。
但是Springboot并不会直接启动这些配置文件,它默认使用application.properties主配置文件,我们需要通过一个配置来选择需要激活的环境:
#比如在配置文件中指定使用dev环境(加载application-dev.properties),
#我们可以通过设置不同的端口号进行测试;
#我们启动SpringBoot,就可以看到已经切换到dev下的配置了;
spring.profiles.active=dev
5.2 YAML的多文档块
和properties配置文件中一样,但是使用yml去实现不需要创建多个配置文件,更加方便了 !
server:
port: 80
#多环境切换
#选择要激活那个环境块
spring:
profiles:
active: dev
---
server:
port: 8083
spring:
profiles: dev #配置环境的名称
---
server:
port: 8084
spring:
profiles: prod #配置环境的名称
注意:如果yml和properties同时都配置了端口,并且没有激活其他环境 , 默认会使用properties配置文件的!
5.3 配置文件加载位置
SpringBoot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件:
- 优先级1:项目路径下的config文件夹配置文件
- 优先级2:项目路径下配置文件
- 优先级3:资源路径下的config文件夹配置文件
- 优先级4:资源路径下配置文件
注意:
- 优先级由高到底,高优先级的配置会覆盖低优先级的配置;
- SpringBoot会从这四个位置全部加载主配置文件,互补配置。
扩展,我们还可以通过spring.config.location来改变默认的配置文件位置,即指定位置加载配置文件。
项目打包好以后,我们可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置;这种情况,一般是后期运维做的多,相同配置,外部指定的配置文件优先级最高。
java -jar spring-boot-config.jar --spring.config.location=F:/application.properties