1. SpringBoot基础回顾

1.1 约定优于配置

Spring Boot 是所有基于Spring开发的项目的起点。SpringBoot的设计是就是未来让给你尽可能快的跑起来Spring应用程序并且尽可能减少你的配置文件。

约定优于配置,又称约定编程,是一种软件设计范式。

本质上,就是系统、类库或框架应该假定合理的默认值,而非要求提供不必要的配置。只有偏离约定时,才需要写相关配置。

约定优于配置简单理解,就是遵循约定。

1.2 SpringBoot概念

1. spring优缺点介绍

优点:

Spring是java企业版(javaEE)的轻量级代替品。无需开发重量级的EJB,Spring为企业级Java开发提供了一种相对简单的方法,通过IOC(程序解耦)和AOP(方法的动态增强),用简单Java对象(POJO)实现EJB。

缺点:

  • 配置重量级,很繁琐,即使Sping2.5出现注解扫描和Spring3.0基于Java类型安全的可重构配置方式,仍然会要挤占开发时间;

  • 项目依赖配置管理困难,版本兼容问题频出

2. springboot解决上述问题的实现

起步依赖

本质是一个Maven项目对象模型(Project Object Model),定义了对其他库的传递依赖,把具备功能的依赖坐标打包到一起,支持默认的功能。

自动配置

springboot会把一些配置类的bean自动注入到ioc容器中,我们可以直接引用。

springboot总结:快速启动spring应用的使用方式 + 基于约定优于配置思想   +    两大核心(起步依赖,自动配置)

1.3 SpringBoot案例实现

1. 案例实现

案例需求:使用Spring Initializr方式构建Spring Boot项目,并请求Controller中的目标方法,将返回值响应到页面

  1. 使用Spring Initializr方式构建Spring Boot项目 本质上说,Spring Initializr是一个Web应用,它提供了一个基本的项目结构,能够帮助我们快速构建一个基础的Spring Boot项目

“Project SDK”用于设置创建项目使用的JDK版本,这里,使用之前初始化设置好的JDK版本即可; 在“Choose Initializr Service URL(选择初始化服务地址)”下使用默认的初始化服务地址“https://start.spring.io”进行Spring Boot项目创建(注意使用快速方式创建Spring Boot项目时,所在主机须在联网状态下)

Spring Boot项目就创建好了。创建好的Spring Boot项目结构如图:

使用Spring Initializr方式构建的Spring Boot项目会默认生成项目启动类、存放前端静态资源和页面的文件夹、编写项目配置的配置文件以及进行项目单元测试的测试类

  1. 创建Controller com.lagou包下创建名称为controller的包,在该包下创建一个请求处理控制类HelloController, 并编写一个请求处理方法 (注意:将项目启动类SpringBootDemoApplication移动到com.lagou包下)
@RestController // 该注解为组合注解,等同于Spring中 @Controller + @ResponseBody 注解
public class DemoController {
@RequestMapping("/demo")
public String demo(){
return "hello springBoot";
}
}
  1. 运行项目 运行主程序启动类SpringbootDemoApplication,项目启动成功后,在控制台上会发现Spring Boot项目默认启动的端口号为8080,此时,可以在浏览器上访问“http://localhost:8080/hello”,至此,构建Spring Boot项目就完成了

1.4 单元测试与热部署

1. 单元测试

2. 热部署

在开发项目过程中,当修改了某些代码后需要本地验证时,需要重启本地服务进行验证,启动这个项目,如果项目庞大的话还是需要较长时间的,spring开发团队为我们带来了一个插件:spring-boot-devtools,很好的解决了本地验证缓慢的问题。但这里所谓的热部署实际也是重启了服务,只是不需要自己手动重启。

1.添加spring-boot-devtools热部署依赖启动器

在Spring Boot项目进行热部署测试之前,需要先在项目的pom.xml文件中添加spring-boot-devtools热部署依赖启动器:

<!-- 引入热部署依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>

由于使用的是IDEA开发工具,添加热部署依赖后可能没有任何效果,接下来还需要针对IDEA开发工具进行热部署相关的功能设置。

  1. IDEA工具热部署设置

选择IDEA工具界面的【File】->【Settings】选项,打开Compiler面板设置页面

选择Build下的Compiler选项,在右侧勾选“Build project automatically”选项将项目设置为自动编译,单击【Apply】→【OK】按钮保存设置

在项目任意页面中使用组合快捷键“Ctrl+Shift+Alt+/”打开Maintenance选项框,选中并打开Registry页面

列表中找到“compiler.automake.allow.when.app.running”,将该选项后的Value值勾选,用于指定IDEA工具在程序运行过程中自动编译,最后单击【Close】按钮完成设置

3. 热部署原理分析

该原理其实很好说明,就是我们在编辑器上启动项目,然后改动相关的代码,然后编辑器自动触发编译替换掉历史的.class文件后,项目检测到有文件变更后会重启srpring-boot项目。

可以看到,我们引入了插件后,插件会监控我们classpath的资源变化,当classpath有变化后,会触发重启

这里提到了,该插件重启快速的原因:这里对类加载采用了两种类加载器,对于第三方jar包采用baseclassloader来加载,对于开发人员自己开发的代码则使用restartClassLoader来进行加载,这使得比停掉服务重启要快的多,因为使用插件只是重启开发人员编写的代码部分。

@Component
public class Devtools implements InitializingBean {
private static final Logger log = LoggerFactory.getLogger(Devtools.class);
@Override
public void afterPropertiesSet() throws Exception {
log.info("guava-jar classLoader: " + DispatcherServlet.class.getClassLoader().toString());
log.info("Devtools ClassLoader: " + this.getClass().getClassLoader().toString());
}
}

先去除spring-boot-devtools插件,跑下工程:

可以看到,DispatcherServlet(第三方jar包)和Devtools(自己编写的类)使用的都是AppClassLoader加载的。

现在加上插件,然后执行下代码:

发现第三方的jar包的类加载器确实是使用的系统的类加载器,而我们自己写的代码的类加载器为RestartClassLoader,并且每次重启,类加载器的实例都会改变。

4. 排除资源

某些资源在更改后不一定需要触发重新启动。例如,Thymeleaf模板可以就地编辑。默认情况下,改变资源/META-INF/maven , /META-INF/resources , /resources , /static , /public ,或/templates不触发重新启动,但确会触发现场重装。如果要自定义这些排除项,则可以使用该spring.devtools.restart.exclude 属性。例如,仅排除/static , /public 您将设置以下属性:

spring:
devtools:
restart:
log-condition-evaluation-delta: false

1.5 全局配置文件

1. 全局配置文件概述及优先级

全局配置文件能够对一些默认配置值进行修改及自定义配置。

Spring Boot使用一个application.properties或者application.yaml的文件作为全局配置文件

也可以从ConfigFileApplicationListener这类便可看出,其中DEFAULT_SEARCH_LOCATIONS属性设置了加载的目录:

翻译成文件系统:

–file:./config/
–file:./
–classpath:/config/
–classpath:/

翻译成语言如下( 按照优先级从高到低的顺序):

  1. 先去项目根目录找config文件夹下找配置文件件

  2. 再去根目录下找配置文件

  3. 去resources下找cofnig文件夹下找配置文件

  4. 去resources下找配置文件

整个设计非常巧妙。SpringBoot会从这四个位置全部加载主配置文件,如果高优先级中配置文件属性与低优先级配置文件不冲突的属性,则会共同存在— 互补配置。

SpringBoot会加载全部主配置文件;互补配置;

备注:
这里说的配置文件,都还是项目里面。最终都会被打进jar包里面的,需要注意。
1、如果同一个目录下,有application.yml也有application.properties,默认先读取application.properties。
2、如果同一个配置属性,在多个配置文件都配置了,默认使用第1个读取到的,后面读取的不覆盖前面读取到的。
3、创建SpringBoot项目时,一般的配置文件放置在“项目的resources目录下”

Spring Boot 2.4 改进了处理 application.properties 和 application.yml 配置文件的方式,

如果是2.4.0之前版本,优先级properties>yaml

但是如果是2.4.0的版本,优先级yaml>properties

如果想继续使用 Spring Boot 2.3 的配置逻辑,也可以通过在 application.properties 或者

application.yml 配置文件中添加以下参数:

spring.config.use-legacy-processing = true

如果我们的配置文件名字不叫application.properties或者application.yml,可以通过以下参数来指定配置文件的名字,myproject是配置文件名

java -jar myproject.jar --spring.config.name=myproject

我们同时也可以指定其他位置的配置文件来生效

指定配置文件和默认加载的这些配置文件共同起作用形成互补配置。

java -jar run-0.0.1-SNAPSHOT.jar --spring.config.location=D:/application.properties

2. application.properties配置文件

使用Spring Initializr方式构建Spring Boot项目时,会在resource目录下自动生成一个空的application.properties文件,Spring Boot项目启动时会自动加载application.properties文件。

我们可以在application.properties文件中定义Spring Boot项目的相关属性,当然,这些相关属性可以是系统属性、环境变量、命令参数等信息,也可以是自定义配置文件名称和位置

server.port=8081
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.config.name=application
public class Pet {

// 类型
private String type;
// 名称
private String name;
// 省略属性getXX()和setXX()方法
// 省略toString()方法
}
@Component
@ConfigurationProperties(prefix = "person") //实现批量注入(set方法)
public class Person {

private int id; //id
private String name; //名称
private List hobby; //爱好
private String[] family; //家庭成员
private Map map;
private Pet pet; //宠物

// 省略属性getXX()和setXX()方法
// 省略toString()方法
}

@ConfigurationProperties(prefix = "person")注解的作用是将配置文件中以person开头的属性值通过setXX()方法注入到实体类对应属性中

@Component注解的作用是将当前注入属性值的Person类对象作为Bean组件放到Spring容器中,只有这样才能被@ConfigurationProperties注解进行赋值

打开项目的resources目录下的application.properties配置文件,在该配置文件中编写需要对Person类设置的配置属性

配置文件书写提示

编写application.properties配置文件时,由于要配置的Person对象属性是我们自定义的,SpringBoot无法自动识别,所以不会有任何书写提示。在实际开发中,为了出现代码提示的效果来方便配置,在使用@ConfigurationProperties注解进行配置文件属性值注入时,可以在pom.xml文件中添加一个Spring Boot提供的配置处理器依赖:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>

2. application.yaml配置文件

YAML文件格式是Spring Boot支持的一种JSON超集文件格式,以数据为中心,比properties、xml等更适合做配置文件

  • yml和xml相比,少了一些结构化的代码,使数据更直接,一目了然

  • 相比properties文件更简洁

  • YAML文件的扩展名可以使用.yml或者.yaml。

  • application.yml文件使用 “key:(空格)value”格式配置属性,使用缩进控制层级关系。

这里,针对不同数据类型的属性值,介绍一下YAML

  1. value值为普通数据类型(例如数字、字符串、布尔等)

当YAML配置文件中配置的属性值为普通数据类型时,可以直接配置对应的属性值,同时对于字符串类型的属性值,不需要额外添加引号,示例代码如下:

server:
port: 8080
servlet:
context-path: /hello
  1. value值为数组和单列集合

当YAML配置文件中配置的属性值为数组或单列集合类型时,主要有两种书写方式:缩进式写法和行内式写法。

其中,缩进式写法还有两种表示形式,示例代码如下

person:
hobby:
- play
- read
- sleep

或者使用如下示例形式

person:
hobby:
play,
read,
sleep

上述代码中,在YAML配置文件中通过两种缩进式写法对person对象的单列集合(或数组)类型的爱好hobby赋值为play、read和sleep。其中一种形式为“-(空格)属性值”,另一种形式为多个属性值之前加英文逗号分隔(注意,最后一个属性值后不要加逗号)。

person:
hobby: [play,read,sleep]

通过上述示例对比发现,YAML配置文件的行内式写法更加简明、方便。另外,包含属性值的中括号“[]”还可以进一步省略,在进行属性赋值时,程序会自动匹配和校对

  1. value值为Map集合和对象

当YAML配置文件中配置的属性值为Map集合或对象类型时,YAML配置文件格式同样可以分为两种书写方式:缩进式写法和行内式写法。

其中,缩进式写法的示例代码如下

person:
map:
k1: v1
k2: v2

对应的行内式写法示例代码如下

person:
map: {k1: v1,k2: v2}

在YAML配置文件中,配置的属性值为Map集合或对象类型时,缩进式写法的形式按照YAML文件格式编写即可,而行内式写法的属性值要用大括号“{}”包含。

接下来,在Properties配置文件演示案例基础上,通过配置application.yaml配置文件对Person对象进行赋值,具体使用如下

(1)在项目的resources目录下,新建一个application.yaml配置文件,在该配置文件中编写为Person类设置的配置属性

person:
id: 2
name: lucy
hobby: [吃饭,睡觉,打王者]
family: [father,mother]
map: {k1: v1,k2: v2}
pet: {type: pig,name: 佩奇}

1.6 属性注入

使用Spring Boot全局配置文件设置属性时:

如果配置属性是Spring Boot已有属性,例如服务端口server.port,那么Spring Boot内部会自动扫描并读取这些配置文件中的属性值并覆盖默认属性。

如果配置的属性是用户自定义属性,例如刚刚自定义的Person实体类属性,还必须在程序中注入这些配置属性方可生效。

1. 属性注入常用注解

@Configuration:声明一个类作为配置类

@Bean:声明在方法上,将方法的返回值加入Bean容器

@Value:属性注入

@ConfigurationProperties(prefix = "jdbc"):批量属性注入

@PropertySource("classpath:jdbc.properties")指定外部属性文件。在类上添加

2. @Value属性值注入

  1. 引入数据源连接依赖
<dependency>
<groupId>com.github.drtrang</groupId>
<artifactId>druid-spring-boot2-starter</artifactId>
<version>1.1.10</version>
</dependency>
  1. application.properties添加信息
# 数据源相关配置信息
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/springboot_h
jdbc.username=root
jdbc.password=123456
  1. 配置数据源

创建JdbcConfiguration类: 使用spring中的value注解对每个属性进行注入,用bean注解将返回值添加到容器中

@Configuration
public class JdbcConfiguration {
@Value("${jdbc.url}")
String url;
@Value("${jdbc.driverClassName}")
String driverClassName;
@Value("${jdbc.username}")
String username;
@Value("${jdbc.password}")
String password;
@Bean
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(url);
dataSource.setDriverClassName(driverClassName);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}

3. @ConfigurationProperties批量注入

@EnableConfigurationProperties 是Spring Boot 提供的一个注解,使用该注解用于启用应用对另外一个注解@ConfigurationProperties 的支持,,用于设置一组使用了注解

@ConfigurationProperties 的类,用于作为 bean 定义注册到容器中。

新建JdbcProperties ,用来进行属性注入:

@Configuration
@EnableConfigurationProperties(JdbcConfiguration.class)
@ConfigurationProperties(prefix = "jdbc")
public class JdbcConfiguration {

private String driverClassName;

private String url;

private String username;

private String password;

public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}

public void setUrl(String url) {
this.url = url;
}

public void setUsername(String username) {
this.username = username;
}

public void setPassword(String password) {
this.password = password;
}

@Bean
public DataSource dataSource(){

DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(driverClassName);
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
return druidDataSource;

}

@Override
public String toString() {
return "JdbcConfiguration{" +
"driverClassName='" + driverClassName + '\'' +
", url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}

除了@ConfigurationProperties 用于注释类之外,您还可以在公共@Bean 方法上使用它。当要将属性绑定到控件之外的第三方组件时,这样做特别有用。

创建一个其他组件类

public class AnotherComponent {

// 是否启用
private Boolean enabled;

// IP地址
private InetAddress remoteAddress;

}

创建MyService

@Configuration
public class MyService {


@ConfigurationProperties(prefix = "another")
@Bean
public AnotherComponent anotherComponent(){
return new AnotherComponent();
}

}

配置文件

another.enabled=true
another.remoteAddress=192.168.1.11

松散绑定

Spring Boot使用一些宽松的规则将环境属性绑定到@ConfigurationProperties bean,因此环境属性名和bean属性名之间不需要完全匹配

例如属性类:

@Component
@ConfigurationProperties("acme.my-person.person")
public class OwnerProperties {

public String firstName;

public void setFirstName(String firstName) {
this.firstName = firstName;
}
}

acme:
myPerson:
person:
FIRST_NAME: 泰森

4. @ConfigurationProperties vs @Value