一、SpringBoot简介
1.1 Spring Boot概述
Spring Boot 是所有基于 Spring Framework 5.0 开发的项目。Spring Boot 的设计是为了让你尽可能快的跑起来 Spring 应用程序,并且尽可能减少你的配置文件。
设计目的: 用来简化 Spring 应用的初始搭建以及开发过程。
从最根本上来讲,Spring Boot 就是一些库的集合,它能够被任意项目所使用。它使用 “习惯优于配置” (项目中存在大量的配置,此外还内置一个习惯性的配置)的理念让你的项目快速运行起来。spring boot 其实不是什么新的框架,它默认配置了很多框架的使用方式,就像 maven 整合了所有的 jar 包,spring boot 整合了所有的框架,总结一下及几点:
(1)为所有 Spring 开发提供一个更快更广泛的入门体验。
(2)零配置。无冗余代码生成和XML 强制配置,遵循“约定大于配置” 。
(3)集成了大量常用的第三方库的配置, Spring Boot 应用为这些第三方库提供了几乎可以零配置的开箱即用的能力。
(4)提供一系列大型项目常用的非功能性特征,如嵌入服务器等。
使用 Spring Boot有什么好处
其实就是简单、快速、方便!平时如果我们需要搭建一个 Spring Web 项目的时候需要怎么做呢?
- 1)配置 web.xml,加载 Spring 和 Spring mvc
- 2)配置数据库连接、配置 Spring 事务
- 3)配置加载配置文件的读取,开启注解
- 4)配置日志文件
- …
- 配置完成之后部署 Tomcat 调试
- …
1.2 SpringBoot的特点
- 为基于Spring的开发提供更快的入门体验
- 开箱即用,没有代码生成,也无需XML配置。同时也可以修改默认值来满足特定的需求
- 提供了一些大型项目中常见的非功能性特性,如嵌入式服务器、外部配置等
- SpringBoot不是对Spring功能上的增强,而是提供了一种快速使用Spring的方式
1.3 SpringBoot的核心功能
- 起步依赖
起步依赖本质上是一个Maven项目对象模型(Project Object Model,POM),定义了对其他库的传递依赖,这些东西加在一起即支持某项功能。
简单的说,起步依赖就是将具备某种功能的坐标打包到一起,并提供一些默认的功能。
- 自动配置
Spring Boot的自动配置是一个运行时(更准确地说,是应用程序启动时)的过程,考虑了众多因素,才决定Spring配置应该用哪个,不该用哪个。该过程是Spring自动完成的。
二、快速创建
2.1 创建项目
2.1.1 创建Maven工程
使用idea工具创建一个maven工程
2.1.2 添加起步依赖
pom.xml
<!--父工程-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<dependencies>
<!--web起步包
注意:虽然是web工程,但不需要打war包,直接打jar就行
-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
2.1.3 主程序入口
package com.lyr;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
*
* @author lyr
* @className SpringBootApplication
*/
@SpringBootApplication
public class MainApplication {
// 主程序入口
public static void main(String[] args) {
SpringApplication.run(MainApplication.class);
}
}
2.1.4 Controller测试
package com.lyr.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* TODO
*
* @author lyr
* @className MainController
* @time 2024-02-05 10:31
*/
@RestController
public class MainController {
@RequestMapping("/hello")
public String helloWorld() {
return "Hello SpringBoot";
}
}
启动访问 http://localhost:8080/hello
2.2 工程添加热部署
编写代码立即生效,不用重启,偶尔反应慢
2.2.1 添加依赖
<!-- 热部署配置 -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
2.2.2 打开IDEA的自动编译
三、SpringBoot原理分析
3.1 启动类的String[] args参数
String[] args 是运行方法时加载的一些启动参数:java -jar -Xms 512M -Xmx 512M
-Xms 堆内存的初始大小
-Xmx 堆内存的最大值
-Xmn 新生代大小
-Xss 每个线程的堆栈大小
…
3.2 起步依赖原理分析
3.2.1 分析spring-boot-starter-parent
按住Ctrl点击pom.xml中的spring-boot-starter-parent,跳转到了spring-boot-starter-parent的pom.xml,xml部分重点配置如下
可以发现,一部分坐标的版本、依赖管理、插件管理已经定义好,所以我们的SpringBoot工程继承spring-boot-starter-parent后已经具备版本锁定等配置了(不会出现版本冲突的问题)。所以起步依赖的作用就是进行依赖的传递。
<properties>
<activemq.version>5.15.3</activemq.version>
<antlr2.version>2.7.7</antlr2.version>
<appengine-sdk.version>1.9.63</appengine-sdk.version>
<artemis.version>2.4.0</artemis.version>
<aspectj.version>1.8.13</aspectj.version>
<assertj.version>3.9.1</assertj.version>
<atomikos.version>4.0.6</atomikos.version>
<bitronix.version>2.1.4</bitronix.version>
<build-helper-maven-plugin.version>3.0.0</build-helper-maven-plugin.version>
<byte-buddy.version>1.7.11</byte-buddy.version>
... ... ...
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
... ... ...
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
</plugin>
<plugin>
<groupId>org.jooq</groupId>
<artifactId>jooq-codegen-maven</artifactId>
<version>${jooq.version}</version>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.0.1.RELEASE</version>
</plugin>
... ... ...
</plugins>
</pluginManagement>
</build>
3.2.2 分析spring-boot-starter-web
不是所有的jar都传递,需要指定,用到哪个jar包,导入哪个jar包。
按住Ctrl点击pom.xml中的spring-boot-starter-web,跳转到了spring-boot-starter-web的pom.xml,xml配置如下(部分重点配置):
从spring-boot-starter-web的pom.xml中我们可以发现,spring-boot-starter-web就是将web开发要使用的spring-web、spring-webmvc等坐标进行了“打包”,这样我们的工程只要引入spring-boot-starter-web起步依赖的坐标就可以进行web开发了,同样体现了依赖传递的作用,同时加载tomcat,只要启动main方法,就相当于起到tomcat进行开发;同时加载json,支持springmvc的数据请求和响应。
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starters</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.6.RELEASE</version>
<name>Spring Boot Web Starter</name>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.1.6.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>2.1.6.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.1.6.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.17.Final</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.1.8.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.8.RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
3.3 自动配置原理分析
3.3.1 解析启动类的run方法
3.3.1.1 初始化IOC容器
run方法的第一个作用就是初始化IOC容器
Ctrl进入run方法查看,可以看到run是一个静态有返回值的方法,返回对象是ConfigurableApplicationContext
ConfigurableApplicationContext类又继承自ApplicationContext应用上下文
ApplicationContext继承了一堆的类,我们认识的就是BeanFactory,点进去看就会发现它们几个都继承了BeanFactory,BeanFactory是最底层的接口,这个就是IOC接口,BeanFactory就是存储bean对象的IOC容器
3.3.1.2 加载启动类
run方法传入了启动类的字节码文件ProductApplication.class
,也就是说,run方法初始化bean容器之后会去扫描启动类上的注解
3.3.2 解析启动类注解
@SpringBootApplication是一个复合注解,它里面除了包含声明接口相关的注解外,还有三个核心注解
简单来说就是当run方法启动创建了IOC容器之后,通过run方法传入的字节码文件ProductApplication.class IOC会扫描到启动类上的@SpringBootApplication注解,由启动类注解帮我们做三件事
- @SpringBootConfiguration 标识启动类是配置类
- @EnableAutoConfiguration 自动配置
- @ComponentScan 包扫描
逐个分析
3.3.2.1 @SpringBootConfiguration
标明是配置类,也就是说这个启动类有配置类的功能,定义的bean就会注入到IOC容器
启动类就可以这样写:
@SpringBootApplication
public class MainApplication {
// 主程序入口
public static void main(String[] args) {
SpringApplication.run(MainApplication.class);
}
/**
* 类似于学习SSM框架里面的spring.xml的配置文件
* <bean name="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"></bean>
*/
@Bean
public RedisTemplate redisTemplate() {
return new RedisTemplate();
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
...
}
其中,@Import(AutoConfigurationImportSelector.class)
导入了 AutoConfigurationImportSelector
类
按住Ctrl点击查看 AutoConfigurationImportSelector
源码 , 加载元数据··
public String[] selectImports(AnnotationMetadata annotationMetadata) {
..................
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
..................
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
....................
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
....................
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 在META-INF/spring.factories中找不到自动配置类。需要看看文件是否正确
"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 方法的作用就是从META-INF/spring.factories文件中读取指定类对应的类名称列表
spring.factories 文件中有关自动配置的配置信息如下:摘抄重点,springboot
启动之后,会自动加载 dispatcherServlet
... ... ...
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
... ... ...
上面配置文件存在大量的以 Configuration
为结尾的类名称,这些类就是存有自动配置信息的类,而SpringApplication
在获取这些类名后再加载
我们以 DispatcherServletAutoConfiguration
为例来分析:
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
/*
* The bean name for a DispatcherServlet that will be mapped to the root URL "/"
*/
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
3.3.2.2 @ComponentScan
包扫描注解,@SpringBootApplication里面的包扫描和我们自定义的包扫描注解不一样,自定义注解是我们可以指定扫描我们用的到的模块的工具包
而启动类注解自带的包扫描的注解会扫描启动类及其所在包的子包里面的所有类的注解-------@Service,@Mapper,@Controller等等
类似于学习SSM框架里面的spring.xml的配置文件:
3.3.2.3 @EnableAutoConfiguration
涉及的是SpringBoot的两个核心原理:自动配置、起步依赖
3.4 举例自动配置
1、创建User类
2、在 resources
文件夹下面 新建 /META-INF/spring.factories
文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.lyr.pojo.User
四、SpringBoot的配置文件
4.1 SpringBoot配置文件类型
springboot支持二种类型的配置文件
- properties属性配置文件
- yaml配置文件
配置文件必须放置在项目的类加载目录下, 并且名字必须是application
SpringBoot默认会从Resources目录下加载application.properties或application.yml(application.yaml)文件
4.1.2 application.yml配置文件
4.1.2.1 yml配置文件简介
YML文件格式是YAML (YAML Aint Markup Language)编写的文件格式,YAML是一种直观的能够被电脑识别的的数据数据序列化格式,并且容易被人类阅读,容易和脚本语言交互的,可以被支持YAML库的不同的编程语言程序导入,比如: C/C++, Ruby, Python, Java, Perl, C#, PHP等。YML文件是以数据为核心的,比传统的xml方式更加简洁。
YML文件的扩展名可以使用.yml或者.yaml。
4.1.2.2 yml配置文件的语法
(1) 配置普通数据
示例代码:
# 属性的配置
# 语法: key: value
name: maweiqi
注意:value之前有一个空格
在resource
文件夹下面,新建application.yml
文件
server:
port: 18082
(2)配置对象数据
语法:
key:
key1: value1
key2: value2
或者:
key: {key1: value1,key2: value2}
在resource
文件夹下面新建 application.yml
文件
注意:key1前面的空格个数不限定,在yml语法中,相同缩进代表同一个级别,一般按一下tab键
server:
port: 18082
user:
username: zhangsan
password: 123
user类,通过注解 @ConfigurationProperties
(prefix=“配置文件中的key的前缀”)可以将配置文件中的配置自动与实体进行映射
package com.atguigu.pojo;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* User
*
* @Author: 马伟奇
* @Description:
*/
@Component
@ConfigurationProperties(value = "user")
public class User implements Serializable{
private String username;
private String password;
//生成 set get tostring
}
(3) 配置Map数据
# 语法:
map:
key: value1
key: value2
示例代码:
# map结构
map:
key1: value1
key2: value2
(4)配置数组(List、Set)数据
# 语法:
key:
- value1
- value2
或者:
key: [value1,value2]
示例代码:
# 配置数据集合
city:
- beijing
- tianjin
- shanghai
- chongqing
# 或者行内注入
city: [beijing,tianjin,shanghai,chongqing]
注意:value1与之间的 - 之间存在一个空格
4.2 SpringBoot读取配置文件的三种方法
4.2.1 读取默认配置文件application.properties或application.yml
SpringBoot分别提供3中方式读取项目的application.properties配置文件的内容。这个方式分别为:Environment类、@Value注解以及@ConfigurationProperties注解。
在application.properties文件定义如下:
# 端口
server.port=8080
# 自定义
test.value.csdn=其实不会敲代码
test.value.address=https://blog.csdn.net/YR_112233?type=blog
4.2.1.1 Environment获取属性值
package com.atguigu.jxc.test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.core.env.Environment;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/value")
public class TestController {
@Autowired
private Environment environment;
@GetMapping("/method1")
public Map value1() {
System.out.println(environment.getProperty("server.port"));
Map<String,Object> map = new HashMap<>();
map.put("CSDN",environment.getProperty("test.value.CSDN"));
map.put("address",environment.getProperty("test.value.address"));
return map;
}
}
在浏览器访问http://localhost:8080/value/method1
4.2.1.2 @Value注解
@Configuration
@Data
public class TestDomain {
@Value("${server.port}")
private String port;
}
@RestController
@RequestMapping("/value")
public class TestController {
@Autowired
private TestDomain testDomain;
@GetMapping("/method1")
public String value() {
System.out.println();
String port = testDomain.getPort();
System.out.println(port);
return "success";
}
}
@Value底层就是Environment.java
4.2.1.3 @ConfigurationProperties注解
使用@ConfigurationProperties首先建立配置文件与对象的映射关系,然后在控制器方法中使用@Autowired注解将对象注入。具体如下:
01、建立配置文件与对象的映射关系
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "test.value")//找到配置文件
@Data
public class TestDomain2 {
private String csdn;
private String address;
}
02、将对象注册在配置文件中
定义一个配置类加上注解注册,因为启动类有配置类的作用,所以也可以直接加在启动类中
import com.atguigu.jxc.test.TestDomain2;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@SpringBootApplication
@MapperScan("com.atguigu.jxc.dao")
@EnableConfigurationProperties(TestDomain2.class)//注册属性配置类对象
public class JxcApplication {
public static void main(String[] args) {
SpringApplication.run(JxcApplication.class, args);
}
}
03、使用属性配置类对象
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/value")
public class TestController {
@Autowired
private TestDomain2 testDomain2;
@GetMapping("/method4")
public String value4() {
String csdnStr = testDomain2.getCsdn();
String addressStr = testDomain2.getAddress();
return csdnStr+" "+addressStr;
}
}
04、关于自定义属性的自动提示问题和警告问题
解决步骤如下:
01、在pom.xml进行以来自动提示procossor
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
02、清理项目,重新编译 clean->compile
03、然后在重新打开application.yml
就可以出现自动提示,警告也会消失
4.2.2读取springboot中自定义的配置文件
4.2.2.1 读取springboot中自定义的properties文件,例如test.properties
test.value.csdn=其实不会敲代码
test.value.address=https://blog.csdn.net/YR_112233?type=blog
1.通过@Value和@PropertySource结合取值
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
@Data
@Component
@PropertySource("classpath:test.properties")
public class TestDomain3 {
@Value("${test.value.csdn}")
private String csdn;
@Value("${test.value.address}")
private String address;
}
2.通过@ConfigurationProperties和@PropertySource结合取值
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix="user")
@PropertySource("classpath:test.properties")
public class TestDomain4 {
@Value("${test.value.csdn}")
private String csdn;
@Value("${test.value.address}")
private String address;
}
4.2.2.2 读取springboot中自定义的yml文件,例如test.yml
test:
value:
csdn: 其实不会敲代码
address: https://blog:csdn:net/YR_112233?type=blog
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.DefaultPropertySourceFactory;
import org.springframework.core.io.support.EncodedResource;
import java.io.IOException;
import java.util.List;
public class PropertySourceFactory extends DefaultPropertySourceFactory {
@Override
public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
if (resource == null) {
return super.createPropertySource(name, resource);
}
List<PropertySource<?>> sources = new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource());
return sources.get(0);
}
}
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
@Data
@Component
@PropertySource(value="test.yml",factory= PropertySourceFactory.class)
public class TestDomain5 {
@Value("${test.value.csdn}")
private String csdn;
@Value("${test.value.address}")
private String address;
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.core.env.Environment;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/value")
public class TestController {
@Autowired
private TestDomain3 testDomain3;
@Autowired
private TestDomain4 testDomain4;
@Autowired
private TestDomain5 testDomain5;
/**
* 读取springboot中自定义的properties文件,例如test.properties
* 通过@Value和@PropertySource结合取值
*/
@GetMapping("/method5")
public String value5() {
String csdnStr = testDomain3.getCsdn();
String addressStr = testDomain3.getAddress();
return csdnStr+" "+addressStr;
}
/**
* 读取springboot中自定义的properties文件,例如test.properties
* 通过@ConfigurationProperties和@PropertySource结合取值
*/
@GetMapping("/method6")
public String value6() {
String csdnStr = testDomain4.getCsdn();
String addressStr = testDomain4.getAddress();
return csdnStr+" "+addressStr;
}
/**
* 读取springboot中自定义的yml文件,例如test.yml
* 通过@Value和@PropertySource
*/
@GetMapping("/method7")
public String value7() {
String csdnStr = testDomain5.getCsdn();
String addressStr = testDomain5.getAddress();
return csdnStr+" "+addressStr;
}
}
4.3 多环境profile切换
Spring Boot项目中配置文件的名称只能是application , 如果我们把所有的配置全都写在一个配置文件中如果配置项比较多, 配置文件就会显得比较复杂和臃肿 ! 不利于后期的项目维护和开发
例如下面几个场景 :
1.因为开发环境的变化, 我们需要修改配置文件中某一个配置项的值(比如之前是mysql数据库,切换成oracle数据库)
2.项目开发完成需要上线了 , 需要把一些环境修改成正式环境(开发有开发环境,测试有测试环境,上线有上线环境,多环境切换)
3.项目功能有BUG , 需要在开发环境下修复BUG
解决方案 :可以使用profiles配置将配置文件按照情况进行拆分配置
在一个spring boot项目中中允许使用多个YAML配置文件。这些配置文件的名称必须为application-***.yml
,并且这些配置文件必须要在application.yml
配置文件中激活之后才可以使用。
创建application-dev.yml
文件如下:
server:
port: 8001
创建application-uat.yml
文件如下:
server:
port: 8002
在 application.yml
文件中添加如下配置:
# 激活配置文件
spring:
profiles:
active: dev
注意 : 如果properties
和yml
文件都存在,如果有重叠属性,默认以properties
优先。
4.4 自定义启动器
定义一个连接池启动器 , 当用户引入了连接池启动依赖之后 , 项目中就已经自动配置了连接池
创建项目 spring-boot-jdbc-starter
引入依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>com.atguigu</groupId>
<artifactId>spring-boot-jdbc-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--引入spring‐boot‐starter;所有starter的基本配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--自动配置连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
</dependencies>
创建属性配置类
@ConfigurationProperties(prefix = "spring.jdbc.datasource")
public class DatasourceProperties {
private String driver ;
private String url;
private String username;
private String password;
// 生成set get toString方法
}
创建自动配置类
@Configuration
@EnableConfigurationProperties(DatasourceProperties.class)
public class DataSourceAutoConfiguratioin {
@Autowired
private DatasourceProperties datasourceProperties ;
@Bean
public DataSource createDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(datasourceProperties.getDriver());
dataSource.setUrl(datasourceProperties.getUrl());
dataSource.setUsername(datasourceProperties.getUsername());
dataSource.setPassword(datasourceProperties.getPassword());
return dataSource;
}
}
编写自动配置属性文件
在 resource
文件夹下面新建 META-INF/spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.atguigu.autoConfigration.DataSourceAutoConfiguratioin
做完了之后要执行install , 安装项目
配置连接池信息
新建 application-datasource.yml
spring:
jdbc:
datasource:
driver: com.mysql.jdbc.Driver
url: jdbc:mysql:///springboot_01
username: root
password: root
激活配置文件 application.yml
# 激活配置文件
spring:
profiles:
active: datasource
注入连接池, 查看连接池属性
五、SpringBoot集成其他框架
5.1 SpringBoot整合Mybatis
5.1.1 添加依赖
<!-- MyBatis -->
<!--mybatis起步依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<!-- MySQL连接驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- lombok依赖 为了简化实体类的编写代码量 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
5.1.2 添加数据库配置文件
logging:
level:
com.lyr.dao: debug # 配置日志
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://127.0.0.1:3306/mybatis_plus?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
#spring集成Mybatis环境
#pojo别名扫描包
mybatis:
type-aliases-package: com.lyr.pojo
#加载Mybatis映射文件
mapper-locations: com/lyr/dao/*.xml
5.1.3 创建实体Bean
@Data //getter,setter
@AllArgsConstructor // 全参构造
@NoArgsConstructor // 无参构造
@ToString // toString方法
public class User implements Serializable {
private Long id;
private String username;
private String password;
private String name;
}
5.1.4 编写Mapper
@Mapper // 容器加载bean
public interface UserDao {
public List<User> findAll();
}
注意:@Mapper标记该类是一个mybatis的mapper接口,可以被spring boot自动扫描到spring上下文中
5.1.5 编写Mapper映射文件
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<!-- namespace扫描mapper层接口 -->
<mapper namespace="com.lyr.dao.UserDao">
<!-- id与接口中的方法名一致,resultType是结果返回 -->
<select id="findAll" resultType="user">
select * from user
</select>
</mapper>
5.1.6 新建Service
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public List<User> findAll() {
return userDao.findAll();
}
}
5.1.7 新建Controller
@RestController
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/findAll")
public List<User> findAll() {
return userService.findAll();
}
}
加密yml配置文件
<!-- 加密 yml 配置文件 -->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
jasypt:
encryptor:
password: 123456
@SpringBootTest
public class TestMybatis {
@Autowired
private UserDao userDao;
@Autowired
private StringEncryptor stringEncryptor;
@Test
public void findAll() throws SQLException {
System.out.println(userDao.findAll());
}
@Test
public void test02() throws Exception{
String user = stringEncryptor.encrypt("root");
String pwd = stringEncryptor.encrypt("123456");
String url = stringEncryptor.encrypt("jdbc:mysql://127.0.0.1:3306/mybatis_plus?autoReconnect=true&serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8&useSSL=false");
System.out.println("password:" + user);
System.out.println("pwd:" + pwd);
System.out.println("url:" + url);
}
}
将生成的加密配置写入yml配置文件中
5.2 SpringBoot整合Junit
5.2.1 添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
5.2.2 添加测试方法
在test
文件夹下面新建测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestMybatis {
@Autowired
private UserDao userDao;
@Test
public void findAll() throws SQLException {
System.out.println(userDao.findAll());
}
}
5.3 SpringBoot整合Spring Data JPA
5.3.1 导入依赖
<!-- MySQL连接驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- springBoot JPA的起步依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
5.3.2 配置文件
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://127.0.0.1:3306/mybatis_plus?autoReconnect=true&serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8&useSSL=false
driver-class-name: com.mysql.jdbc.Driver
jpa:
database: mysql
show-sql: true
generate-ddl: true
hibernate:
ddl-auto: update
naming_strategy: org.hibernate.cfg.ImprovedNamingStrategy
main:
allow-bean-definition-overriding: true #自动覆盖同名的bean
5.3.3 Entity
@Entity
@Table(name = "person")
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "name")
private String name;
@Column(name = "age")
private Integer age;
@Column(name = "address")
private String address;
}
5.3.4 Dao
public interface PersonDao extends JpaRepository<Person,Integer> {
}
5.3.5 Service
public interface PersonService {
List<Person> findAll();
Person findPersonById(Integer id);
void savePerson(Person person);
void updatePerson(Person person);
void deletePersonById(Integer id);
}
@Service
public class PersonServiceImpl implements PersonService {
@Autowired
private PersonDao personDao;
@Override
public List<Person> findAll() {
return personDao.findAll();
}
@Override
public Person findPersonById(Integer id) {
return personDao.findById(id).get();
}
@Override
public void savePerson(Person person) {
personDao.save(person);
}
@Override
public void updatePerson(Person person) {
personDao.save(person);
}
@Override
public void deletePersonById(Integer id) {
personDao.deleteById(id);
}
}
5.3.6 测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestJpa {
@Autowired
private PersonService personService;
@Test
public void findAll() {
personService.findAll().stream().forEach(System.out::println);
}
@Test
public void findPersonById() {
System.out.println(personService.findPersonById(1));;
}
@Test
public void savePerson() {
Person person = new Person();
person.setId(2);
person.setName("李四");
person.setAge(12);
personService.savePerson(person);
}
@Test
public void updatePerson() {
Person person = new Person();
person.setId(1);
person.setAddress("上海市");
personService.updatePerson(person);
personService.findAll().stream().forEach(System.out::println);
}
@Test
public void deletePersonById() {
personService.deletePersonById(2);
}
}
5.4 SpringBoot整合Redis
5.4.1 引入依赖
<!-- spring boot redis缓存引入 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 缓存连接池-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- redis 存储 json序列化 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
5.4.2 配置文件
spring:
redis:
host: 192.168.100.100
port: 6379
database: 0
password: 123456 #默认为空
timeout: 3000ms #最大等待时间,超时则抛出异常,否则请求一直等待
lettuce:
pool:
max-active: 20 #最大连接数,负值表示没有限制,默认8
max-wait: -1 #最大阻塞等待时间,负值表示没限制,默认-1
max-idle: 8 #最大空闲连接,默认8
min-idle: 0 #最小空闲连接,默认0
5.4.3 启动Redis服务
1、本地redis服务
2、远程连接Linux服务器
#启动服务
cd /usr/local/redis-5.0.7
bin/redis-server redis.conf
#docker启动redis
#运行容器
docker run -p 6379:6379 --name myredis --privileged=true -v /app/redis/redis.conf:/etc/redis/redis.conf -v /app/redis/data:/data -d redis:6.0.8 redis-server /etc/redis/redis.conf
#进入容器
docker exec -it redis /bin/bash
#连接终端
redis-cli
5.4.4 配置Redis序列化方案
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
//首先解决key的序列化方式
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
//对hashKey配置序列化
redisTemplate.setHashKeySerializer(stringRedisSerializer);
//解决value的序列化方式
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
//序列化时将类的数据类型存入json,以便反序列化的时候转换成正确的类型
ObjectMapper objectMapper = new ObjectMapper();
//objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
// 解决jackson2无法反序列化LocalDateTime的问题
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.registerModule(new JavaTimeModule());
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
return redisTemplate;
}
}
5.4.5 测试
@SpringBootTest
@RunWith(SpringRunner.class)
public class RedisTemplateTests {
@Resource
private RedisTemplate redisTemplate;
@Resource
private DictMapper dictMapper;
@Test
public void test01(){
//存值
redisTemplate.opsForValue().set("key1","这是一段中文");
}
@Test
public void test02(){
//取值
Object key1 = redisTemplate.opsForValue().get("key1");
System.out.println(key1);
}
@Test
public void saveDict(){
Dict dict = dictMapper.selectById(1);
//存储string类型键值对,设置过期时间
redisTemplate.opsForValue().set("dict",dict,5, TimeUnit.MINUTES);
}
@Test
public void getDict(){
Object dict = redisTemplate.opsForValue().get("dict");
System.out.println(dict);
}
}
5.4.6 业务案例
先查缓存,如果缓存里面有数据,则使用缓存数据,缓存里面没有数据就查询数据库,并将数据库数据同步到缓存
@Resource
private RedisTemplate redisTemplate;
@Override
public List<Dict> listByParentId(Long parentId) {
//先查询redis中是否存在数据列表
List<Dict> dictList = null;
try {
dictList = (List<Dict>)redisTemplate.opsForValue().get("srb:core:dictList:" + parentId);
if(dictList != null){
log.info("从redis中取值");
return dictList;
}
} catch (Exception e) {
log.error("redis服务器异常:" + ExceptionUtils.getStackTrace(e));//此处不抛出异常,继续执行后面的代码
}
log.info("从数据库中取值");
dictList = baseMapper.selectList(new QueryWrapper<Dict>().eq("parent_id", parentId));
dictList.forEach(dict -> {
//如果有子节点,则是非叶子节点
boolean hasChildren = this.hasChildren(dict.getId());
dict.setHasChildren(hasChildren);
});
//将数据存入redis
try {
redisTemplate.opsForValue().set("srb:core:dictList:" + parentId, dictList, 5, TimeUnit.MINUTES);
log.info("数据存入redis");
} catch (Exception e) {
log.error("redis服务器异常:" + ExceptionUtils.getStackTrace(e));//此处不抛出异常,继续执行后面的代码
}
return dictList;
}
5.5 SpringBoot整合定时任务
5.5.1 引入依赖
<!-- 添加 Scheduled 坐标 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
5.5.2 主程序注解
@SpringBootApplication
@MapperScan("com.lyr.dao")
@EnableScheduling
public class MainApplication {
// 主程序入口
public static void main(String[] args) {
SpringApplication.run(MainApplication.class);
}
}
5.5.3 定时任务
@Component
public class TaskConfig {
/**
* @Scheduled:定时规则
* cron:,项目启动后每5秒执行一次
*
* fixedDelay:距离上一次定时任务执行完毕后N毫秒在执行,
* 执行A任务花了5秒,比如参数是3000,A任务执行完成之后,在过3秒执行
*
* fixedRate:执行周期,执行频率,
* 定时任务执行开始,在过N毫秒后执行,
* 执行A任务花了2秒,比如参数是3000,A任务执行完成之后,在过1秒后执行,
* 执行A任务花了15秒,比如参数是3000,A任务执行完成之后,立即执行。
*
* @auther
* @return void
*/
@Scheduled(fixedDelay = 3000)
public void myTask() {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(simpleDateFormat.format(new Date()));
try {
// 休眠
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
5.6 Springboot整合Swagger
可参考Api文档:http://blog.didispace.com/springbootswagger2/
Swagger官网 :http://swagger.io/
Spring Boot & Swagger UI : http://fruzenshtein.com/spring-boot-swagger-ui/
Github:https://github.com/swagger-api/swagger-core/wiki/Annotations
5.6.1 简介
作用:
1. 接口的文档在线自动生成。
2. 功能测试。
Swagger是一组开源项目,其中主要要项目如下:
-
Swagger-tools:提供各种与Swagger进行集成和交互的工具。例如模式检验、Swagger 1.2文档转换成Swagger 2.0文档等功能。
-
Swagger-core: 用于Java/Scala的的Swagger实现。与JAX-RS(Jersey、Resteasy、CXF…)、Servlets和Play框架进行集成。
-
Swagger-js: 用于JavaScript的Swagger实现。
-
Swagger-node-express: Swagger模块,用于node.js的Express web应用框架。
-
Swagger-ui:一个无依赖的HTML、JS和CSS集合,可以为Swagger兼容API动态生成优雅文档。
-
Swagger-codegen:一个模板驱动引擎,通过分析用户Swagger资源声明以各种语言生成客户端代码。
5.6.2 导入依赖
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!--swagger ui-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<!--swagger-bootstrap-ui-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.2</version>
</dependency>
5.6.3 swagger配置文件
@Configuration
@EnableSwagger2
public class Swagger2Config {
/**
* 创建API应用
* apiInfo() 增加API相关信息
* 通过select()函数返回一个ApiSelectorBuilder实例,用来控制哪些接口暴露给Swagger来展现,
* 指定扫描的包路径来定义指定要建立API的目录。
* @return
*/
@Bean
public Docket coreApiConfig(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(adminApiInfo())
.groupName("testApi")
.select()
//只显示admin下面的路径
.paths(Predicates.and(PathSelectors.regex("/user/.*")))
.build();
}
private ApiInfo adminApiInfo(){
return new ApiInfoBuilder()
.title("Swagger--api文档")
.description("接口描述")
.version("1.0")
.contact(new Contact("李燕茹","https://blog.csdn.net/YR_112233","728831102@qq.com"))
.build();
}
}
5.6.4 swagger的注解介绍
Swagger使用的注解及其说明:
@Api:用在类上,说明该类的作用。
@ApiOperation:注解来给API增加方法说明。
@ApiParam:定义在参数上
@ApiResponses:用于表示一组响应
@ApiResponse:用在@ApiResponses中,一般用于表达一个错误的响应信息
l code:数字,例如400
l message:信息,例如"请求参数没填好"
l response:抛出异常的类
@ApiModel:描述一个Model的信息(一般用在请求参数无法使用@ApiImplicitParam注解进行描述的时候)
l @ApiModelProperty:描述一个model的属性
**@ApiImplicitParams **: 用在方法上包含一组参数说明。
@ApiImplicitParam:用来注解来给方法入参增加说明。
@ApiImplicitParam的参数说明:
paramType:指定参数放在哪个地方 | header:请求参数放置于Request Header,使用@RequestHeader获取 query:请求参数放置于请求地址,使用@RequestParam获取 path:(用于restful接口)–>请求参数的获取:@PathVariable body:(不常用) form(不常用) |
---|---|
name:参数名 | |
dataType:参数类型 | |
required:参数是否必须传 | true | false |
value:说明参数的意思 | |
defaultValue:参数的默认值 |
5.6.5 示例
实体类
@Data //getter,setter
@AllArgsConstructor // 全参构造
@NoArgsConstructor // 无参构造
@ToString // toString方法
@ApiModel(value = "User对象" ,description = "用户名密码")
public class User implements Serializable {
@ApiModelProperty(value = "主键")
private Long id;
@ApiModelProperty(value = "登录名")
private String username;
@ApiModelProperty(value = "密码")
private String password;
@ApiModelProperty(value = "用户名")
private String name;
}
controller
@RestController
@RequestMapping("/user")
@Api(tags = "User管理")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/findAll")
@ApiOperation(value = "查询所有", notes = "查询所有")
public List<User> findAll() {
return userService.findAll();
}
}
访问
访问:http://localhost:8000/swagger-ui.html 或 http://localhost:8000/doc.html
有一个需要注意的地方:
Conntroller中定义的方法必须在@RequestMapper中显示的指定RequestMethod类型,否则SawggerUi会默认为全类型皆可访问, API列表中会生成7条项目。