【SpringBoot学习笔记 六】JSR303数据校验、日志配置及输出、静态资源映射

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 【SpringBoot学习笔记 六】JSR303数据校验、日志配置及输出、静态资源映射

前面5篇Blog详细介绍了SpringBoot的实现原理,本篇Blog从实战的角度来看下SpringBoot开发时我们还需要做哪些工作,这里我选择了三个比较重要的模块来进行介绍,包括我们的数据验证,日志配置以及静态资源的映射,这篇博客结束后我们即将进入到SpringBoot的各种整合和集成的学习中,可以理解为SpringBoot基础学习的最后一篇吧。

JSR303数据校验

JSR 是 Java Specification Requests 的缩写,即 Java 规范提案。存在各种各样的 JSR,简单的理解为 JSR 是一种 Java 标准。JSR 303 就是数据检验的一个标准(Bean Validation (JSR 303))

为什么使用JSR303

处理一段业务逻辑,首先要确保数据输入的正确性,所以需要先对数据进行检查,保证数据在语义上的正确性,再根据数据进行下一步的处理。前端可以通过 js 程序校验数据是否合法,后端同样也需要进行校验。而后端最简单的实现就是直接在业务方法中对数据进行处理,但是不同的业务方法可能会出现同样的校验操作,这样就出现了数据的冗余。首先可以想象下在使用SR 303 规范之前Struts是如何对传入参数进行校验的,需要我们自己编写validate。

@Override
  public void validate() {                                         //该方法为基础actionsupport自带的方法,支持表单验证
    if (username == null || username.length() < 4 || username.length() > 6) {
      this.addActionError("用户名长度必须为4-6位");                      //添加错误信息
      this.addFieldError(username, "filed用户名长度必须为4-6位");     //添加自定义的fielderror的错误信息
    }
    if (password.length() < 4 || password.length() > 6) {
      this.addActionError("密码长度必须在4-6位之间");
    } else if (repassword.length() < 4 || repassword.length() > 6) {
      this.addActionError("确认密码长度必须在4-6位之间");
    } else if (!password.equals(repassword)) {
      this.addActionError("确认密码不等于密码");
    }
    if (age < 0) {
      this.addActionError("年龄不能为负");
    }
    if (null == birthday) {
      this.addActionError("出生日期不能为空");
    }
    if (null == graduate) {
      this.addActionError("毕业日期不能为空");
    }
    if (!(null == birthday) && !(null == graduate)) {
      Calendar c1 = Calendar.getInstance();
      c1.setTime(birthday);                 //获取日期时间
      Calendar c2 = Calendar.getInstance();
      c2.setTime(graduate);
      if (c2.before(c1)) {        //比较时间先后顺序
        this.addActionError("毕业日期不能早于出生日期");
      }
    }
  }

而JSR 303 使用 Bean Validation,即在 Bean 上添加相应的注解,去实现数据校验。这样在执行业务方法前,都会根据注解对数据进行校验,从而减少自定义的校验逻辑,减少代码冗余

JSR303常见参数

JSR303常见参数如下,在被检查项属性加注解即可。

我们最常用的一些检查项和示例如下:

@NotNull(message="名字不能为空")
private String userName;
@Max(value=120,message="年龄最大不能查过120")
private int age;
@Email(message="邮箱格式错误")
private String email;
空检查
@Null       验证对象是否为null
@NotNull    验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank   检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty   检查约束元素是否为NULL或者是EMPTY.
Booelan检查
@AssertTrue     验证 Boolean 对象是否为 true  
@AssertFalse    验证 Boolean 对象是否为 false  
长度检查
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内  
@Length(min=, max=) string is between min and max included.
日期检查
@Past       验证 Date 和 Calendar 对象是否在当前时间之前  
@Future     验证 Date 和 Calendar 对象是否在当前时间之后  
@Pattern(value)    验证 String 对象是否符合正则表达式的规则

JSR303使用实践

我们编写一个Controller传一些参数进行测试:

1 引入validation依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

2 创建要校验的入参Model

这里我们可以注解一些信息,进行数据验证。

package com.example.springboot.model;
import lombok.Data;
import org.hibernate.validator.constraints.Range;
import org.springframework.stereotype.Component;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
@Data
@Component
public class PersonDTO implements Serializable {
    private static final long serialVersionUID = 281903912367009575L;
    @NotBlank(message = "用户名不能为空")
    private String username;
    @Range(min = 15,max = 60,message = "年龄必须在15岁到60岁之间")
    private String age;
    @Email(message = "必须是合法的邮箱地址")
    private String email;
}

3 创建PersonController

请求到来时只有数据都通过验证才打印

package com.example.springboot.controller;
import com.example.springboot.model.PersonDTO;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Controller;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
/**
 * * @Name HelloSpringController
 * * @Description
 * * @author tianmaolin
 * * @Data 2021/9/22
 */
@RestController
public class HelloSpringController {
    @GetMapping ("/hello")
    public String hello(@Valid @ModelAttribute PersonDTO personDTO) throws JsonProcessingException {
        //创建一个jackson的对象映射器,用来解析数据
        ObjectMapper mapper = new ObjectMapper();
        //将我们的对象解析成为json格式
        String jsonStr = mapper.writeValueAsString(personDTO);
        //由于@RestController注解,这里会将str转成json格式返回,十分方便
        return jsonStr;
    }
}

4 测试实现方式

我们请求一下,先填一个错的信息,由于没有前端信息处理错误页面,所以展示默认的,

由于没有前端信息处理错误页面,所以展示默认的,我们可以从控制台看输出的错误日志

然后我们按照要求填写入参则可以正确显示:

日志配置及输出

在项目开发中,日志十分的重要,不管是记录运行情况还是定位线上问题,都离不开对日志的分析。在 Java 领域里存在着多种日志框架,如 JCL、SLF4J、Jboss-logging、jUL、log4j、log4j2、logback 等等

日志框架的选择

市面上常见的日志框架有很多,它们可以被分为两类:日志门面(日志抽象层)和日志实现

通常情况下,日志由一个日志门面与一个日志实现组合搭建而成,Spring Boot 选用 SLF4J + Logback 的组合来搭建日志系统,SLF4J 是目前市面上最流行的日志门面,使用 Slf4j 可以很灵活的使用占位符进行参数占位,简化代码,拥有更好的可读性

SLF4J 的使用

在项目开发中,记录日志时不应该直接调用日志实现层的方法,而应该调用日志门面(日志抽象层)的方法。在使用 SLF4J 记录日志时,我们需要在应用中导入 SLF4J 及日志实现,并在记录日志时调用 SLF4J 的方法

package com.example.springboot.controller;
import com.example.springboot.model.PersonDTO;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
/**
 * * @Name HelloSpringController
 * * @Description
 * * @author tianmaolin
 * * @Data 2021/9/22
 */
@RestController
@Slf4j
public class HelloSpringController {
    Logger logger = LoggerFactory.getLogger(HelloSpringController.class);
    @GetMapping ("/hello")
    public String hello(@Valid @ModelAttribute PersonDTO personDTO) throws JsonProcessingException {
        //创建一个jackson的对象映射器,用来解析数据
        ObjectMapper mapper = new ObjectMapper();
        //将我们的对象解析成为json格式
        String jsonStr = mapper.writeValueAsString(personDTO);
        //由于@RestController注解,这里会将str转成json格式返回,十分方便
        logger.info(jsonStr);
        return jsonStr;
    }
}

请求时可以看到控制台的日志输出:

静态资源映射

在 Web 应用中会涉及到大量的静态资源,例如 JS、CSS 和 HTML 等。我们知道,Spring MVC 导入静态资源文件时,需要配置静态资源的映射,我在【Spring MVC学习笔记 三】深入实践Spring MVC控制器中提到过SpringMVC的处理方式。但在 SpringBoot 中则不再需要进行此项配置,因为 SpringBoot 已经默认完成了这一工作。

Spring Boot 默认为我们提供了 3 种静态资源映射规则:WebJars 映射;默认资源映射;静态首页(欢迎页)映射

WebJars 映射

为了让页面更加美观,让用户有更多更好的体验,Web 应用中通常会使用大量的 JS 和 CSS,例如 jQuery,Backbone.js 和 Bootstrap 等等。通常我们会将这些 Web 前端资源拷贝到 Java Web 项目的 webapp 相应目录下进行管理。但是 Spring Boot 项目是以 JAR 包的形式进行部署的,不存在 webapp 目录,那么 Web 前端资源该如何引入到 Spring Boot 项目中呢?WebJars 可以完美的解决上面的问题,它可以 Jar 形式为 Web 项目提供资源文件。

WebJars 可以将 Web 前端资源(JS,CSS 等)打成一个个的 Jar 包,然后将这些 Jar 包部署到 Maven 中央仓库中进行统一管理,当 Spring Boot 项目中需要引入 Web 前端资源时,只需要访问 WebJars 官网,找到所需资源的 pom 依赖,将其导入到项目中即可

所有通过 WebJars 引入的前端资源都存放在当前项目类路径(classpath)下的/META-INF/resources/webjars/目录中,Spring Boot 通过 MVC 的自动配置类 WebMvcAutoConfiguration 为这些 WebJars 前端资源提供了默认映射规则,部分源码如下

public void addResourceHandlers(ResourceHandlerRegistry registry) {
    if (!this.resourceProperties.isAddMappings()) {
        logger.debug("Default resource handling disabled");
    } else {
        //WebJars 映射规则
        this.addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
        this.addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
            registration.addResourceLocations(this.resourceProperties.getStaticLocations());
            if (this.servletContext != null) {
                ServletContextResource resource = new ServletContextResource(this.servletContext, "/");
                registration.addResourceLocations(new Resource[]{resource});
            }
        });
    }
}

在 Spring Boot 项目 的 pom.xml 中添加以下依赖,将 jquery 引入到该项目中

<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>jquery</artifactId>
    <version>3.6.0</version>
</dependency>

如下图所示:

启动 Spring Boot,浏览器访问http://localhost:8080/webjars/jquery/3.6.0/jquery.js访问 jquery.js

默认资源映射

当访问项目中的任意资源(即/**)时,Spring Boot 会默认从以下路径中查找资源文件(优先级依次降低):

  1. classpath:/META-INF/resources/
  2. classpath:/resources/
  3. classpath:/static/
  4. classpath:/public/

这些路径又被称为静态资源文件夹当我们请求某个静态资源(即以.html结尾的请求)时,Spring Boot 会先查找优先级高的文件夹,再查找优先级低的文件夹,直到找到指定的静态资源为止,在 项目的 src/main/resources 下的 static 目录中创建一个 hello.html,代码如下

<!DOCTYPE html>
<html lang="en">
<head></head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<body>
<h1>第一个SpringBoot静态资源</h1>
</body>
</html>

启动 Spring Boot,浏览器访问 http://localhost:8080/hello.html

静态首页(欢迎页)映射

静态资源文件夹下的所有 index.html 被称为静态首页或者欢迎页,它们会被 /** 映射,换句话说就是,当我们访问/或者/index.html时,都会跳转到静态首页(欢迎页),在项目的 src/main/resources 下的 public 目录中创建一个 index.html,代码如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>我是首页,也就是欢迎页,你一个看到的就是我</h1>
</body>
</html>

访问静态首页或欢迎页时,其查找顺序也遵循默认静态资源的查找顺序,即先查找优先级高的目录,在查找优先级低的目录,直到找到 index.html 为止

总结一下

这篇Blog从实战角度简单了解了下SpringBoot的一些常用操作手段,例如通过JSR303进行便捷的数据校验,通过日志配置及输出实现日志的打印,而这一切都已经给我们封装好了,静态资源映射也都固定好了目录和位置,可以说SpringBoot真的是为我们开发操碎了心,只要大概了解规则就能只关心业务的像个傻瓜一样开发了

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
16天前
|
JSON Java 数据格式
springboot中表字段映射中设置JSON格式字段映射
springboot中表字段映射中设置JSON格式字段映射
42 1
|
16天前
|
XML 安全 Java
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
本文介绍了Java日志框架的基本概念和使用方法,重点讨论了SLF4J、Log4j、Logback和Log4j2之间的关系及其性能对比。SLF4J作为一个日志抽象层,允许开发者使用统一的日志接口,而Log4j、Logback和Log4j2则是具体的日志实现框架。Log4j2在性能上优于Logback,推荐在新项目中使用。文章还详细说明了如何在Spring Boot项目中配置Log4j2和Logback,以及如何使用Lombok简化日志记录。最后,提供了一些日志配置的最佳实践,包括滚动日志、统一日志格式和提高日志性能的方法。
140 30
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
|
14天前
|
Java 中间件
SpringBoot入门(6)- 添加Logback日志
SpringBoot入门(6)- 添加Logback日志
56 5
|
9天前
|
Java 开发者 微服务
手写模拟Spring Boot自动配置功能
【11月更文挑战第19天】随着微服务架构的兴起,Spring Boot作为一种快速开发框架,因其简化了Spring应用的初始搭建和开发过程,受到了广大开发者的青睐。自动配置作为Spring Boot的核心特性之一,大大减少了手动配置的工作量,提高了开发效率。
28 0
|
1月前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
48 4
|
1月前
|
Java API 数据库
Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐
本文通过在线图书管理系统案例,详细介绍如何使用Spring Boot构建RESTful API。从项目基础环境搭建、实体类与数据访问层定义,到业务逻辑实现和控制器编写,逐步展示了Spring Boot的简洁配置和强大功能。最后,通过Postman测试API,并介绍了如何添加安全性和异常处理,确保API的稳定性和安全性。
38 0
|
13天前
|
缓存 IDE Java
SpringBoot入门(7)- 配置热部署devtools工具
SpringBoot入门(7)- 配置热部署devtools工具
26 2
 SpringBoot入门(7)- 配置热部署devtools工具
|
4天前
|
存储 前端开发 JavaScript
springboot中路径默认配置与重定向/转发所存在的域对象
Spring Boot 提供了简便的路径默认配置和强大的重定向/转发机制,通过合理使用这些功能,可以实现灵活的请求处理和数据传递。理解并掌握不同域对象的生命周期和使用场景,是构建高效、健壮 Web 应用的关键。通过上述详细介绍和示例,相信读者能够更好地应用这些知识,优化自己的 Spring Boot 应用。
12 3
|
5天前
|
Java 中间件
SpringBoot入门(6)- 添加Logback日志
SpringBoot入门(6)- 添加Logback日志
15 1
|
12天前
|
Java 数据库连接
SpringBoot配置多数据源实战
第四届光学与机器视觉国际学术会议(ICOMV 2025) 2025 4th International Conference on Optics and Machine Vision
40 8

热门文章

最新文章