SpringBoot快速入门(转载)
创建 SpringBoot 项目(IDEA)
使用 Spring Initializr 创建
新建项目,进行如图配置
![image-20220529112153248](https://gitee.com/zhang-rui-xin/image/raw/master/image-20220529112153248.png)
下一步,选择 SpringBoot 版本,搜 Web,添加 Spring Web 依赖
![image-20220529112416519](https://gitee.com/zhang-rui-xin/image/raw/master/image-20220529112416519.png)
点击完成,等待 maven 依赖导入完成,SpringBoot启动类就可以运行了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fAxhJU2s-1655976160769)(https://gitee.com/zhang-rui-xin/image/raw/master/image-20220623164623428.png)]
运行 SpringBootDemoApplication
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NV7w6OZX-1655976160769)(https://gitee.com/zhang-rui-xin/image/raw/master/image-20220529113407140.png)]
包下新建一个 controller (Java包的命名规范,包名都用小写)包,controller 包下新建一个 HelloController 类,输入一下内容
@RestController
public class HelloController {
@RequestMapping("/hello")
public String sayHello(){
return "Hello World";
}
}
本地浏览器访问 localhost:8080/hello,看到浏览器显示 Hello World,表示运行成功
YAML入门
SpringBoot 默认使用以下 2 种全局的配置文件,其文件名是固定的。
- application.properties
- application.yml
YAML简介
YAML 全称 YAML Ain’t Markup Language,它是一种以数据为中心的标记语言,比 XML 和 JSON 更适合作为配置文件。
举个栗子:
server:
port: 8080
该例设置了服务器端口
YAML语法
YAML 的语法如下:
- 使用缩进表示层级关系。
- 缩进时不允许使用 Tab 键,只允许使用空格。
- 缩进的空格数不重要,但同级元素必须左侧对齐。
- 大小写敏感。
YAML其他规范访问原文官网:http://c.biancheng.net/spring_boot/yaml.html
Spring Boot 配置绑定
SpringBoot 提供了以下 2 种方式进行配置绑定:
- 使用 @ConfigurationProperties 注解
- 使用 @Value 注解
@ConfigurationProperties
使用该注解前,先引入该注解处理器依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
通过 Spring Boot 提供的 @ConfigurationProperties 注解,可以将全局配置文件中的配置数据绑定到 JavaBean 中。下面我们以 Spring Boot 项目 helloworld 为例,演示如何通过 @ConfigurationProperties 注解进行配置绑定。
1. 在 helloworld 的全局配置文件 application.yml 中添加以下自定义属性。
person:
lastName: 张三
age: 18
boss: false
birth: 1990/12/12
maps: { k1: v1,k2: 12 }
lists:
‐ lisi
‐ zhaoliu
dog:
name: 迪迪
age: 5
2. 创建一个名为 Person 的实体类,并将配置文件中的属性映射到这个实体类上,代码如下。
/**
* 将配置文件中配置的每一个属性的值,映射到这个组件中
*
* @ConfigurationProperties:告诉 SpringBoot 将本类中的所有属性和配置文件中相关的配置进行绑定;
* prefix = "person":配置文件中哪个下面的所有属性进行一一映射
*
* 只有这个组件是容器中的组件,才能使用容器提供的@ConfigurationProperties功能;
*/
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String lastName;
private Integer age;
private Boolean boss;
private Date birth;
private Map<String, Object> maps;
private List<Object> lists;
private Dog dog;
public Person() {
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Boolean getBoss() {
return boss;
}
public void setBoss(Boolean boss) {
this.boss = boss;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
public Map<String, Object> getMaps() {
return maps;
}
public void setMaps(Map<String, Object> maps) {
this.maps = maps;
}
public List<Object> getLists() {
return lists;
}
public void setLists(List<Object> lists) {
this.lists = lists;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public Person(String lastName, Integer age, Boolean boss, Date birth, Map<String, Object> maps, List<Object> lists, Dog dog) {
this.lastName = lastName;
this.age = age;
this.boss = boss;
this.birth = birth;
this.maps = maps;
this.lists = lists;
this.dog = dog;
}
@Override
public String toString() {
return "Person{" +
"lastName='" + lastName + '\'' +
", age=" + age +
", boss=" + boss +
", birth=" + birth +
", maps=" + maps +
", lists=" + lists +
", dog=" + dog +
'}';
}
}
注意:
1、使用 @Component 注解标注实体类,让该类处于 IOC ( Spring 知识 )容器中,才能享受 Spring Boot 的强大功能。
2、JavaBean 标识了 @ConfigurationProperties(prefix = ‘person’),表示将 JavaBean 中的所有属性与配置文件中以“person”为前缀的配置进行绑定
3、创建一个名为 Dog 的 JavaBean
public class Dog {
private String name;
private String age;
public Dog() {
}
public Dog(String name, String age) {
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(String age) {
this.age = age;
}
public String getName() {
return name;
}
public String getAge() {
return age;
}
}
4、修改 HelloController 的代码
@RestController
public class HelloController {
@Autowired
private Person person;
@RequestMapping("/hello")
public Person sayHello(){
return person;
}
}
5、重启项目,使用浏览器访问 “http://localhost:8080/hello”
运行结果:
![image-20220529131235014](https://gitee.com/zhang-rui-xin/image/raw/master/image-20220529131235014.png)
@Value
当我们只需要读取配置文件中的某一个配置时,可以通过 @Value 注解获取。
修改 Person 类的内容
@Component
public class Person {
@Value("${person.lastName}")
private String lastName;
@Value("${person.age}")
private Integer age;
@Value("${person.boss}")
private Boolean boss;
@Value("${person.birth}")
private Date birth;
private Map<String, Object> maps;
private List<Object> lists;
private Dog dog;
public Person() {
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Boolean getBoss() {
return boss;
}
public void setBoss(Boolean boss) {
this.boss = boss;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
public Map<String, Object> getMaps() {
return maps;
}
public void setMaps(Map<String, Object> maps) {
this.maps = maps;
}
public List<Object> getLists() {
return lists;
}
public void setLists(List<Object> lists) {
this.lists = lists;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public Person(String lastName, Integer age, Boolean boss, Date birth, Map<String, Object> maps, List<Object> lists, Dog dog) {
this.lastName = lastName;
this.age = age;
this.boss = boss;
this.birth = birth;
this.maps = maps;
this.lists = lists;
this.dog = dog;
}
@Override
public String toString() {
return "Person{" +
"lastName='" + lastName + '\'' +
", age=" + age +
", boss=" + boss +
", birth=" + birth +
", maps=" + maps +
", lists=" + lists +
", dog=" + dog +
'}';
}
}
@Value 与 @ConfigurationProperties 对比
@Value 和 @ConfigurationProperties 注解都能读取配置文件中的属性值并绑定到 JavaBean 中,但两者存在以下不同。
1. 使用位置不同
- @ConfigurationProperties:标注在 JavaBean 的类名上;
- @Value:标注在 JavaBean 的属性上。
2. 功能不同
- @ConfigurationProperties:用于批量绑定配置文件中的配置;
- @Value:只能一个一个的指定需要绑定的配置。
3. 松散绑定支持不同
@ConfigurationProperties:支持松散绑定(松散语法),例如实体类 Person 中有一个属性为 firstName,那么配置文件中的属性名支持以下写法:
- person.firstName
- person.first-name
- person.first_name
- PERSON_FIRST_NAME
@Vaule:不支持松散绑定。
4. SpEL 支持不同
- @ConfigurationProperties:不支持 SpEL 表达式;
- @Value:支持 SpEL 表达式。
5. 复杂类型封装
- @ConfigurationProperties:支持所有类型数据的封装,例如 Map、List、Set、以及对象等;
- @Value:只支持基本数据类型的封装,例如字符串、布尔值、整数等类型。
6. 应用场景不同
@Value 和 @ConfigurationProperties 两个注解之间,并没有明显的优劣之分,它们只是适合的应用场景不同而已。
- 若只是获取配置文件中的某项值,则推荐使用 @Value 注解;
- 若专门编写了一个 JavaBean 来和配置文件进行映射,则建议使用 @ConfigurationProperties 注解。
我们在选用时,根据实际应用场景选择合适的注解能达到事半功倍的效果。
原文链接:http://c.biancheng.net/spring_boot/config-bind.html
@PropertySource
如果将所有的配置都集中到 application.properties 或 application.yml 中,那么这个配置文件会十分的臃肿且难以维护,因此我们通常会将与 Spring Boot 无关的配置(例如自定义配置)提取出来,写在一个单独的配置文件中,并在对应的 JavaBean 上使用 @PropertySource 注解指向该配置文件。
1、以 helloworld 为例,将与 person 相关的自定义配置移动到 src/main/resources 下的 person.properties 中(注意,必须把 application.properties 或 application.yml 中的相关配置删除),如下图。
![image-20220529144449326](https://gitee.com/zhang-rui-xin/image/raw/master/image-20220529144449326.png)
person.last-name=李四
person.age=12
person.birth=2000/12/15
person.boss=false
person.maps.k1=v1
person.maps.k2=14
person.lists=a,b,c
person.dog.name=dog
person.dog.age=2
2、在 Person 使用 @PropertySource 注解指向 person.properties,代码如下。
@PropertySource(value = "classpath:person.properties")//指向对应的配置文件
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String lastName;
private Integer age;
private Boolean boss;
private Date birth;
private Map<String, Object> maps;
private List<Object> lists;
private Dog dog;
public Person() {
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Boolean getBoss() {
return boss;
}
public void setBoss(Boolean boss) {
this.boss = boss;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
public Map<String, Object> getMaps() {
return maps;
}
public void setMaps(Map<String, Object> maps) {
this.maps = maps;
}
public List<Object> getLists() {
return lists;
}
public void setLists(List<Object> lists) {
this.lists = lists;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public Person(String lastName, Integer age, Boolean boss, Date birth, Map<String, Object> maps, List<Object> lists, Dog dog) {
this.lastName = lastName;
this.age = age;
this.boss = boss;
this.birth = birth;
this.maps = maps;
this.lists = lists;
this.dog = dog;
}
@Override
public String toString() {
return "Person{" +
"lastName='" + lastName + '\'' +
", age=" + age +
", boss=" + boss +
", birth=" + birth +
", maps=" + maps +
", lists=" + lists +
", dog=" + dog +
'}';
}
}
![image-20220529144731031](https://gitee.com/zhang-rui-xin/image/raw/master/image-20220529144731031.png)
@ConfigurationProperties / @Value 读取 properties 文件乱码
在 idea 设置中将这些编码格式都设置为 UTF-8
![image-20220529145453599](https://gitee.com/zhang-rui-xin/image/raw/master/image-20220529145453599.png)
再从 @PropertySource 中也设置字符编码格式( encoding )为 UTF-8
@PropertySource(value = "classpath:person.properties" , encoding = "UTF-8")
Spring Boot 导入 Spring 配置
@ImportResource 导入 Spring 配置文件
在主启动类上使用 @ImportResource 注解可以导入一个或多个 Spring 配置文件,并使其中的内容生效。
1、新建 PersonService 接口并将其实现
public interface PersonService {
public Person getPersonInfo();
}
public class PersonServiceImpl implements PersonService {
@Autowired
private Person person;
@Override
public Person getPersonInfo() {
return person;
}
}
**2、在 resources 下添加一个名为 beans.xml 的 Spring 配置文件 **
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="personService" class="com.example.spring_boot_demo.service.impl.PersonServiceImpl"/>
</beans>
3、创建测试类,引入IOC容器测试
@SpringBootTest
public class PersonServiceTest {
//IOC容器
@Autowired
ApplicationContext ioc;
@Test
public void testHelloService() {
//校验 IOC 容器中是否包含组件 personService
boolean b = ioc.containsBean("personService");
if (b) {
System.out.println("personService 已经添加到 IOC 容器中");
} else {
System.out.println("personService 没添加到 IOC 容器中");
}
}
}
(未添加 @ImportResource 注解)运行:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jBEc57ik-1655976160770)(https://gitee.com/zhang-rui-xin/image/raw/master/image-20220529153143231.png)]
4、在主启动类上添加 @ImportResource 注解,将 Spring 配置文件 beans.xml 添加到项目
@ImportResource(locations = {"classpath:/beans.xml"})
@SpringBootApplication
public class SpringBootDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDemoApplication.class, args);
}
}
再次运行测试方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WVrb9jli-1655976160771)(https://gitee.com/zhang-rui-xin/image/raw/master/image-20220529154020675.png)]
成功将 Spring 配置文件加入项目
全注解方式:请参考原文:http://c.biancheng.net/spring_boot/import-spring-config.html
Spring Boot Profile (多环境)
多 Profile 文件方式
properties 配置
创建 4 个配置文件:
- application.properties:主配置文件
- application-dev.properties:开发环境配置文件
- application-test.properties:测试环境配置文件
- application-prod.properties:生产环境配置文件
在 application.properties 文件中,指定默认服务器端口号为 8080,并通过以下配置激活生产环境(prod)的 profile。
#默认端口号
server.port=8080
#激活指定的profile
spring.profiles.active=prod
在 application-dev.properties 中,指定开发环境端口号为 8081,配置如下
# 开发环境
server.port=8081
在 application-test.properties 中,指定测试环境端口号为 8082,配置如下。
# 测试环境
server.port=8082
在 application-prod.properties 中,指定生产环境端口号为 8083,配置如下。
# 生产环境
server.port=8083
重启 Spring Boot 主启动程序,控制台输出如下图。
![image-20220529160701106](https://gitee.com/zhang-rui-xin/image/raw/master/image-20220529160701106.png)
通过上图可以看到,我们指定的生产环境(prod) Profile 生效了,且服务器端口为 8083。
多 Profile 文档块模式
详细请参考原文:http://c.biancheng.net/spring_boot/profile.html
Spring Boot 默认配置文件
默认配置文件
Spring Boot 项目中可以存在多个 application.properties 或 apllication.yml。
Spring Boot 启动时会扫描以下 5 个位置的 application.properties 或 apllication.yml 文件,并将它们作为 Spring boot 的默认配置文件。
- file:./config/*/
- file:./config/
- file:./
- classpath:/config/
- classpath:/
注:file: 指当前项目根目录;classpath: 指当前项目的类路径,即 resources 目录。
以上所有位置的配置文件都会被加载,且它们优先级依次降低,序号越小优先级越高。其次,位于相同位置的 application.properties 的优先级高于 application.yml。
所有位置的文件都会被加载,高优先级配置会覆盖低优先级配置,形成互补配置,即:
- 存在相同的配置内容时,高优先级的内容会覆盖低优先级的内容;
- 存在不同的配置内容时,高优先级和低优先级的配置内容取并集。
参考原文:http://c.biancheng.net/spring_boot/default-config.html
Spring Boot 日志
SLF4J 的使用
public class HelloWorld {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(HelloWorld.class);
//调用 sl4j 的 info() 方法,而非调用 logback 的方法
logger.info("Hello World");
}
}
注解使用方法:import lombok.extern.slf4j.Slf4j;
打个 @Slf4j 注解,通过 log.info() 记录日志
@RestController
@Slf4j
public class HelloController {
@Autowired
private Person person;
@RequestMapping("/hello")
public Person sayHello(){
log.info("Hello World!~");
return person;
}
}
日志级别
日志的输出都是分级别的,当一条日志信息的级别大于或等于配置文件的级别时,就对这条日志进行记录。
常见的日志级别如下(优先级依次升高)。
序号 | 日志级别 | 说明 |
---|---|---|
1 | trace | 追踪,指明程序运行轨迹。 |
2 | debug | 调试,实际应用中一般将其作为最低级别,而 trace 则很少使用。 |
3 | info | 输出重要的信息,使用较多。 |
4 | warn | 警告,使用较多。 |
5 | error | 错误信息,使用较多。 |
Thymeleaf
Spring Boot 整合 Thymeleaf ,引入依赖:
<!--Thymeleaf 启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
ThymeleafAutoConfiguration 使用 @EnableConfigurationProperties 注解导入了 ThymeleafProperties 类,该类包含了与 Thymeleaf 相关的自动配置属性,其部分源码如下。
@ConfigurationProperties(
prefix = "spring.thymeleaf"
)
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING;
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
private boolean checkTemplate = true;
private boolean checkTemplateLocation = true;
private String prefix = "classpath:/templates/";
private String suffix = ".html";
private String mode = "HTML";
private Charset encoding;
private boolean cache;
...
}
由此可知,只需要将 HTML 页面放在 “classpath:/templates/” 中,Thymeleaf 就能够自动进行渲染
例如:
1、创建一个名为 hello.html 的页面,并将该页面放在项目类路径(resources)下的 templates 目录中,hello.html 代码如下。
<!DOCTYPE html>
<!--导入thymeleaf的名称空间-->
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--th:text 为 Thymeleaf 属性,用于获取指定属性的值-->
<h1 th:text="'欢迎来到'+${name}"></h1>
</body>
</html>
3、新建一个控制类 HelloController,并通过参数 map 传递数据到前台页面中,代码如下。
@Controller
public class HelloController {
@RequestMapping("/hello")
public String hello(Map<String, Object> map) {
//通过 map 向前台页面传递数据
map.put("name", "德莱联盟");
return "hello";
}
}
运行结果:
![image-20220529200947319](https://gitee.com/zhang-rui-xin/image/raw/master/image-20220529200947319.png)
Spring Boot 拦截器
在 Spring Boot 项目中,使用拦截器功能通常需要以下 3 步:
- 定义拦截器;
- 注册拦截器;
- 指定拦截规则(如果是拦截所有,静态资源也会被拦截)。
定义拦截器
创建一个拦截器类,实现 HandlerInterceptor 接口即可
HandlerInterceptor 中定义了三个方法:
- preHandle (HttpServletRequest request, HttpServletResponse response, Object handler)
该方法在控制器处理请求方法之前执行,返回 true 表示继续执行,返回 false 表示中断后续操作
- postHandle (HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
该方法在控制器处理请求方法调用之后、解析视图之前执行,可以通过此方法对请求域中的模型和视图做进一步修改。
-
afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
该方法在视图渲染结束后执行,可以通过此方法实现资源清理、记录日志信息等工作
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
/**
* 目标方法执行前
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object loginUser = request.getSession().getAttribute("loginUser");
if (loginUser == null) {
//未登录,返回登陆页
request.setAttribute("msg", "您没有权限进行此操作,请先登陆!");
request.getRequestDispatcher("/index.html").forward(request, response);
return false;
} else {
//放行
return true;
}
}
/**
* 目标方法执行后
*
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("postHandle执行{}", modelAndView);
}
/**
* 页面渲染后
*
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("afterCompletion执行异常{}", ex);
}
}
request, response);
return false;
} else {
//放行
return true;
}
}
/**
* 目标方法执行后
*
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info(“postHandle执行{}”, modelAndView);
}
/*
* 页面渲染后
*
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info(“afterCompletion执行异常{}”, ex);
}
}