前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringBoot 2.x 进阶 之 Web

SpringBoot 2.x 进阶 之 Web

作者头像
AI码真香
发布2022-09-13 17:22:44
4310
发布2022-09-13 17:22:44
举报
文章被收录于专栏:AI码真香
一、内容说明

接着上一篇,SpringBoot2.x 教你快速入门,本篇内容我们来学习 SpringBoot2.X 进阶 Web 方面开发常用的一些知识点。

1.1、简介

步骤:

  1. 创建SpringBoot应用,选择相应的Starter
  2. 在配置文件中指定必要的少量配置
  3. 编写业务代码

Web开发的自动配置类:WebMvcAutoConfiguration

二、静态资源的映射
2.1、静态资源的位置

查看WebMvcAutoConfiguration——>getStaticLocations()

静态资源的默认位置:

  • "classpath:/META-INF/resources/"
  • "classpath:/resources/"
  • "classpath:/static/"
  • "classpath:/public/"

当然我们可以再配置文件中修改静态资源的路径:

代码语言:javascript
复制
# 指定静态资源的位置
spring.resources.static-locations=classpath:/static,classpath:/public

备注:static 下的静态资源图片以及静态html文件,通过浏览器可以直接访问到

2.2、欢迎页

favicon.ico 放到任意一个静态资源文件夹中即可!

三、表单验证
3.1、简介

前台提交一些表单时候,往往有一些字段内容需要我们校验一下,比如:姓名、密码、年龄、字段非空,字段长度限制,邮箱格式验证呀等等这些类型。当然前端也可以做一些校验,但后端如果也要做一些信息校验时候,我们如何来做呢?

3.2、校验相关的注解
代码语言:javascript
复制
@Null 只能是null
@NotNull 不能为null 注意用在基本类型上无效,基本类型有默认初始值
@AssertFalse 必须为false
@AssertTrue 必须是true

字符串/数组/集合检查:(字符串本身就是个数组)
@Pattern(regexp="reg") 验证字符串满足正则
@Size(max, min) 验证字符串、数组、集合长度范围
@NotEmpty 验证字符串不为空或者null
@NotBlank 验证字符串不为null或者trim()后不为空

数值检查:同时能验证一个字符串是否是满足限制的数字的字符串
@Max 规定值得上限int
@Min 规定值得下限
@DecimalMax("10.8") 以传入字符串构建一个BigDecimal,规定值要小于这个值 
@DecimalMin 可以用来限制浮点数大小
@Digits(int1, int2) 限制一个小数,整数精度小于int1;小数部分精度小于int2
@Digits 无参数,验证字符串是否合法
@Range(min=long1,max=long2) 检查数字是否在范围之间
这些都包括边界值

日期检查:Date/Calendar
@Post 限定一个日期,日期必须是过去的日期
@Future 限定一个日期,日期必须是未来的日期

其他验证:
@Vaild 递归验证,用于对象、数组和集合,会对对象的元素、数组的元素进行一一校验
@Email 用于验证一个字符串是否是一个合法的右键地址,空字符串或null算验证通过
@URL(protocol=,host=,port=,regexp=,flags=) 用于校验一个字符串是否是合法URL
3.3、表单验证方法

这里简单举例来说明下,如何使用注解的方式来进行表单校验。

在实体 Bean 里需要校验的字段上面添加注解

代码语言:javascript
复制
package com.xmlvhy.girl.entity;

import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;

/**
 * Author: 小莫
 * Date: 2019-01-18 17:43
 * Description:<描述>
 */
@Entity
@Data
@NoArgsConstructor
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String name;

    //@Min(value = 18, message = "未成年禁止入内")
    private Integer age;

    @NotNull(message = "金钱不能为空")
    private Integer money;
}

Controller 中接收参数时,使用 @Valid 注解进行校验

代码语言:javascript
复制
@PostMapping(value = "girls")
public Result <Girl> girlAdd(@Valid Girl girl, BindingResult result){
	if (result.hasErrors()) {
            return ResultUtil.fail(1,result.getFieldError().getDefaultMessage());
        }
        return ResultUtil.success(girlRepository.save(girl));
}

@Valid 和 BindingResult 是相对应的,如果有多个 @Valid,那么每个 @Valid 后面跟着的 BindingResult 就是这个 @Valid 的验证结果,顺序不能乱。

由于我们示例演示,返回都是json数据,这里定义了返回结果的类

定义返回结果的最外层实体类封装

代码语言:javascript
复制
package com.xmlvhy.girl.entity;

import lombok.Data;

/**
 * Author: 小莫
 * Date: 2019-01-25 15:43
 * Description:<描述>
 */
@Data
public class Result<T> {
    /*错误码*/
    private Integer code;
    /*提示信息*/
    private String message;
    /*具体的内容*/
    private T data;
}

封装一个工具类

代码语言:javascript
复制
package com.xmlvhy.girl.util;

import com.xmlvhy.girl.entity.Result;

/**
 * Author: 小莫
 * Date: 2019-01-25 15:54
 * Description:<描述>
 */

public class ResultUtil {

    public static Result success(Object data){
        Result result = new Result();
        result.setCode(0);
        result.setMessage("成功");
        result.setData(data);
        return result;
    }

    public static Result success(){
        return success(null);
    }

    public static Result fail(Integer code, String message){
        Result result = new Result();
        result.setCode(code);
        result.setMessage(message);
        return result;
    }
}

接下来我们来测试一下,这里我使用 postman 工具测试:

请求结果成功返回的情形:

请求结果失败的返回情形:

四、AOP 的使用

使用AOP统一处理请求日志。

4.1、什么是 AOP
代码语言:javascript
复制
1.AOP是一种编程方式
    与语言无关,是一种程序设计思想
    面向切面(AOP)Aspect Oriented Programming
    面向对象(OOP)Object Oriented Programming
    面向过程(POP)Procedure Oriented Programming

2.面向过程到面向对象
    面向过程:假如下雨了,我打开了雨伞
    面向对象:天气->下雨,我->打

3.换个角度看世界,换个姿势处理问题

4.将通用逻辑从业务逻辑中分离出来

通过一个简单的流程图,演示如何使用AOP去处理一个请求:

提取执行相同的代码为一个切面:

4.2、如何使用AOP

POM.xml 文件中,添加 aop 的依赖

代码语言:javascript
复制
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

编写一个切面通知类

代码语言:javascript
复制
package com.xmlvhy.girl.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * Author: 小莫
 * Date: 2019-01-25 10:57
 * Description:<描述>
 */
//定义一个切面
@Aspect
@Component
@Slf4j
public class HttpAspect {

    //定义切点
    @Pointcut("execution(public * com.xmlvhy.girl.controller.GirlController.*(..))")
    public void log(){
    }

    //前置通知
    //@Before("execution(public * com.xmlvhy.girl.controller.GirlController.*(..))")
    @Before("log()")
    public void doBefore(JoinPoint join){
        //url
         ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        log.info("url= {}",request.getRequestURL());
        //method
        log.info("method= {}",request.getMethod());
        //请求的ip
        log.info("ip= {}",request.getRemoteAddr());
        //类方法
        log.info("class_method= {}", join.getSignature().getDeclaringTypeName() + "." +join.getSignature().getName());
        //参数
        log.info("args= {}",join.getArgs());
        log.info("==============doBefore 所请求接口的方法执行之前执行============");
    }

    //后置通知
    //@After("execution(public * com.xmlvhy.girl.controller.GirlController.*(..))")
    @After("log()")
    public void doAfter(){
        log.info("==============doAfter 所请求接口的方法执行之后执行============");
    }

    //@AfterReturning可修饰AfterReturning增强处理,AfterReturning增强处理将在目标方法正常完成后被织入
    @AfterReturning(pointcut = "log()",returning = "object")
    public void doAfterReturning(Object object){
        log.info("response= {}",object);
        log.info("==============doAfterReturning 所请求接口的方法执行之后返回结果之前执行============");
    }
}
4.3、常用的注解说明
代码语言:javascript
复制
@Aspect:声明当前类是一个切面处理类
@Component:声明当前类是一个Bean,由Spring的IOC容器进行管理
@Pointcut:声明需要处理的切点

spring aop 通知(advice)分成五类:

@Before:前置通知[Before advice]:在连接点前面执行,前置通知不会影响连接点的执行,除非此处抛出异常。

@AfterReturning:正常返回通知[After returning advice]:在连接点正常执行完成后执行,如果连接点抛出异常,则不会执行。
@AfterThrowing:异常返回通知[After throwing advice]:在连接点抛出异常后执行。

@After:返回通知[After (finally) advice]:在连接点执行完成后执行,不管是正常执行完成,还是抛出异常,都会执行返回通知中的内容。

@Around:环绕通知[Around advice]:环绕通知围绕在连接点前后,比如一个方法调用的前后。这是最强大的通知类型,能在方法调用前后自定义一些操作。环绕通知还需要负责决定是继续处理join point(调用ProceedingJoinPoint的proceed方法)还是中断执行。
4.4、定义一个接口
代码语言:javascript
复制
@GetMapping(value = "girls")
public List<Girl> girlList(){
    log.info("get girlList");
    return girlRepository.findAll();
}

使用 postman 访问一下,查看一下打印出的日志:

通过日志,我们可以清晰的看到相关方法的执行先后顺序。

五、全局异常处理
5.1、为什么要定义异常处理?
代码语言:javascript
复制
1、在框架层面封装checked exception,将其转化为unchecked exception,避免开发过程中编写繁冗的try...catch代码。

2、业务层面的开发,根据程序代码职责定义不同的RuntimeException(它就是unchecked exception,一般定义为RuntimeException的子类)

3、通过前两个观点,系统中自定义的异常将只存在unchecked exception,系统只在于客户端交换数据的上层,设置统一异常处理机制,并将一些异常转化为用户所能理解的信息传达给用户。

4、其他如业务层,数据持久层,等底层只负责将异常抛出即可,但要注意不要丢失掉异常堆栈(这一点是初学者容易犯的一个错误)。
5.2、什么是异常处理?

如果不加异常处理的话,程序出错了,用户可能不知道是啥原因。但加上异常处理后,用户能最快时间定位错误信息。例如,当一个SpringBoot 程序出现异常时,会默认的给出我们一个异常提示页面:Whitelabel Error Page

但如果我们想要一些相对较友好的提示信息或页面,那么就需要我们进行全局的异常处理了。

  • 定义错误码页面
  • 定义异常通知
5.3、定义错误码页面的方式

创建错误状态码.html页面,放在templates/error目录中,当发生错误时会自动到该目录下查找对应的错误页面。

例如可以创建如4xx.html或5xx.html页面,用来匹配所有该类型的错误(会先进行精确匹配)

5.4、定义异常通知的方式

这里我们做一个简单的用例,获取某个人的年龄并判断,小于10 ,返回“你应该在上小学”,大于10且小于16 ,返回“你可能在上初中”。

我们先封装异常返回信息类:

定义一个枚举类:

代码语言:javascript
复制
package com.xmlvhy.girl.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * Author: 小莫
 * Date: 2019-01-25 18:59
 * Description:<描述>
 */
@Getter
@AllArgsConstructor
public enum ResultEnum {
    UNKNOWN_ERROR(-1,"未知错误"),
    SUCCESS(100,"成功"),
    PRIMARY_SCHOOL(100,"你可能在上小学"),
    MIDDLE_SCHOOL(101,"你可能在上初中"),
    ;
    private Integer code;
    private String message;
}

定义一个自定义异常类:

代码语言:javascript
复制
package com.xmlvhy.girl.exception;

import com.xmlvhy.girl.enums.ResultEnum;
import lombok.Data;

/**
 * Author: 小莫
 * Date: 2019-01-25 18:36
 * Description:<描述>
 */
@Data
public class GirlException extends RuntimeException {
    private Integer code;

    public GirlException(ResultEnum resultEnum) {
        super(resultEnum.getMessage());
        this.code = resultEnum.getCode();
    }
}

这里错误码以及异常信息,我们都统一定义在定义的枚举类中,这样看起来会比较清爽!另外,自定义的异常类,需要继承的RuntimeException 类而不是Exception 类,原因是:springboot 中只对 RuntimeException 类型进行捕获。

使用自定义异常:

代码语言:javascript
复制
@Override
public void getAge(Integer id) throws Exception {
     if (girlRepository.findById(id).isPresent()) {
        Girl girl = girlRepository.findById(id).get();
         log.info("girl= {}",girl);
        Integer age = girl.getAge();
        if (age < 10) {
             log.info("你还在上小学吧");
            //throw new Exception("你还在上小学吧!");
            //throw new GirlException(100,"你还在上小学吧!");
            throw new GirlException(ResultEnum.PRIMARY_SCHOOL);
        }else if(age > 10 && age < 16){
             log.info("你可能在上中学");
            //throw new Exception("你可能在上初中!");
            //throw new GirlException(101,"你可能在上初中!");
            throw new GirlException(ResultEnum.MIDDLE_SCHOOL);
         }
    }
}

定义一个全局异常处理类:

代码语言:javascript
复制
package com.xmlvhy.girl.exception;

import com.xmlvhy.girl.entity.Result;
import com.xmlvhy.girl.util.ResultUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * Author: 小莫
 * Date: 2019-01-25 16:33
 * Description:<描述>
 */
//定义该类为全局异常捕获类
@ControllerAdvice
@Slf4j
public class ExceptionHandle {

    //标记要捕获的异常
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Result exceptionHandle(Exception e){
        //判断异常是否是自定义 GirlException 异常的一个实例
        if (e instanceof GirlException) {
            GirlException girlException = (GirlException) e;
            //log.info("[自定义异常] {}",girlException);
            return  ResultUtil.fail(girlException.getCode(),girlException.getMessage());
        }else{
            log.info("[系统异常] {}", e);
            return ResultUtil.fail(-1, "未知错误");
        }
    }
}

Controller 中我们定义一个接口,来验证一下结果:

代码语言:javascript
复制
@GetMapping(value = "/girls/getAge/{id}")
public void getAge(@PathVariable("id") Integer id) throws Exception {
    girlService.getAge(id);
}

首先我们先插入几条数据到数据库中:

同样使用 postman 工具来测试一下:

年龄小于10的情况:

年龄大于10小于16的情况:

出现系统异常而非自定义异常的情况:

以上,则完成全局异常的处理。

代码语言:javascript
复制
参考链接:
https://segmentfault.com/a/1190000008752288

本文涉及的相关 获取源码

本文作者: AI码真香

本文标题: SpringBoot 2.x 进阶 之 Web

本文网址: https://www.xmlvhy.com/article/70.html

版权说明: 自由转载-非商用-非衍生-保持署名 署名-非商业性使用4.0 国际 (CC BY-NC 4.0)

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、内容说明
    • 1.1、简介
    • 二、静态资源的映射
      • 2.1、静态资源的位置
        • 2.2、欢迎页
        • 三、表单验证
          • 3.1、简介
            • 3.2、校验相关的注解
              • 3.3、表单验证方法
              • 四、AOP 的使用
                • 4.1、什么是 AOP
                  • 4.2、如何使用AOP
                    • 4.3、常用的注解说明
                      • 4.4、定义一个接口
                      • 五、全局异常处理
                        • 5.1、为什么要定义异常处理?
                          • 5.2、什么是异常处理?
                            • 5.3、定义错误码页面的方式
                              • 5.4、定义异常通知的方式
                              相关产品与服务
                              容器服务
                              腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                              领券
                              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档