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

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
日志服务 SLS,月写入数据量 50GB 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日志并进行多维度分析。
相关文章
|
1月前
|
SQL 监控 Java
在IDEA 、springboot中使用切面aop实现日志信息的记录到数据库
这篇文章介绍了如何在IDEA和Spring Boot中使用AOP技术实现日志信息的记录到数据库的详细步骤和代码示例。
在IDEA 、springboot中使用切面aop实现日志信息的记录到数据库
|
23天前
|
jenkins 持续交付
jenkins学习笔记之三:使用jenkins共享库实现日志格式化输出
jenkins学习笔记之三:使用jenkins共享库实现日志格式化输出
jenkins学习笔记之三:使用jenkins共享库实现日志格式化输出
消息中间件 缓存 监控
36 0
|
11天前
|
运维 NoSQL Java
SpringBoot接入轻量级分布式日志框架GrayLog技术分享
在当今的软件开发环境中,日志管理扮演着至关重要的角色,尤其是在微服务架构下,分布式日志的统一收集、分析和展示成为了开发者和运维人员必须面对的问题。GrayLog作为一个轻量级的分布式日志框架,以其简洁、高效和易部署的特性,逐渐受到广大开发者的青睐。本文将详细介绍如何在SpringBoot项目中接入GrayLog,以实现日志的集中管理和分析。
50 1
|
19天前
|
IDE Java 开发工具
还在为繁琐的配置头疼吗?一文教你如何用 Spring Boot 快速启动,让开发效率飙升,从此告别加班——打造你的首个轻量级应用!
【9月更文挑战第2天】Spring Boot 是一款基于 Spring 框架的简化开发工具包,采用“约定优于配置”的原则,帮助开发者快速创建独立的生产级应用程序。本文将指导您完成首个 Spring Boot 项目的搭建过程,包括环境配置、项目初始化、添加依赖、编写控制器及运行应用。首先需确保 JDK 版本不低于 8,并安装支持 Spring Boot 的现代 IDE,如 IntelliJ IDEA 或 Eclipse。
60 5
|
26天前
|
Java 应用服务中间件 HSF
Java应用结构规范问题之配置Logback以仅记录错误级别的日志到一个滚动文件中的问题如何解决
Java应用结构规范问题之配置Logback以仅记录错误级别的日志到一个滚动文件中的问题如何解决
|
26天前
|
Java 应用服务中间件 HSF
Java应用结构规范问题之配置Logback以在控制台输出日志的问题如何解决
Java应用结构规范问题之配置Logback以在控制台输出日志的问题如何解决
|
1月前
|
SQL Java 关系型数据库
SpringBoot 系列之 MyBatis输出SQL日志
这篇文章介绍了如何在SpringBoot项目中通过MyBatis配置输出SQL日志,具体方法是在`application.yml`或`application.properties`中设置MyBatis的日志实现为`org.apache.ibatis.logging.stdout.StdOutImpl`来直接在控制台打印SQL日志。
SpringBoot 系列之 MyBatis输出SQL日志
|
1月前
|
存储 Ubuntu Apache
如何在 Ubuntu VPS 上配置 Apache 的日志记录和日志轮转
如何在 Ubuntu VPS 上配置 Apache 的日志记录和日志轮转
31 6
|
1月前
|
存储 Ubuntu 应用服务中间件
如何在 Ubuntu VPS 上配置 Nginx 的日志记录和日志轮转
如何在 Ubuntu VPS 上配置 Nginx 的日志记录和日志轮转
17 4