Spring Boot 系列4 -- 统一功能处理

目录

前言

[1. Spring AOP 用户统⼀登录验证的问题](#1. Spring AOP 用户统⼀登录验证的问题)

[1.1 自定义拦截器](#1.1 自定义拦截器)

[1.2 配置拦截器并配置拦截的规则](#1.2 配置拦截器并配置拦截的规则)

[1.3 拦截器的原理源码分析](#1.3 拦截器的原理源码分析)

[2. 统一异常处理](#2. 统一异常处理)

[2.1 实现统一异常处理](#2.1 实现统一异常处理)

[2.2 测试统一异常处理](#2.2 测试统一异常处理)

[3. 统一的数据格式返回](#3. 统一的数据格式返回)

[3.1 统⼀数据返回格式的实现](#3.1 统⼀数据返回格式的实现)

[3.2 测试统一的数据返回](#3.2 测试统一的数据返回)


前言

Spring AOP是一个面向切面编程的框架,可以同设置要做处理的类为切面,设置切点进行设置拦截规则,然后定义通知,实现一定的拦截规则.但是我们原生的Spring AOP的框架使用起来比较繁琐.为了解决如此之类的Spring AOP的问题.对于Spring AOP的实战,本文将介绍三种功能.

1. 统一用户登录权限验证

2. 统一数据返回格式

3. 统一异常处理

1. Spring AOP 用户统⼀登录验证的问题

假设我们要实现这样一个功能,在多个页面进行判断用户的登录状态,然后发现当前用户的登录状态为空的时候进行跳转到登录页.

我们之前的做法是,在每个页面的路由中进行判断用户的登录状态,显然这些操作是重复的,后来我们又学习了Spring AOP,我们试想可以将需要进行验证的类进行设置为切面,在需要地方进行配置拦截的规则,然后进行设置前置通知.这个思想是可以的,但是我们实现起来会遇到两个问题.

1. 没办法获取到 HttpSession 对象(我们需要对Session进行判断当前的用户登录状态)

2. 我们要对⼀部分方法进行拦截,而另⼀部分方法不拦截,如注册⽅法和登录方法是不拦截的,这样的话排除⽅法的规则很难定义,甚至没办法定义。

针对一以上的问题,Spring 框架为我们提供了一个具体实现的拦截器:HandlerInterceptor

而我们要使用这个拦截器实现自己的功能,可以具体分为两步:

下面我们使用代码进行实现上述的功能:

1.1 自定义拦截器

具体代码:

java 复制代码
public class LoginInterceptor implements HandlerInterceptor {
    /**
     * 此方法返回一个boolean类型的值,返回true表示验证成功,继续执行后续流程
     */
    //  重写preHandle方法实现自己的业务代码
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 用户登录登录业务判断
        HttpSession session = request.getSession(false);
        if (session != null && session.getAttribute("userinfo") != null){
            // 用户已经登录
            return true;
        }
        // 没有登录,可以进行跳转到登录页面,或者返回一个401 或者403 没有权限码
        response.sendRedirect("/login1.html");
//        response.setStatus(403);
        // 上面两者取一个即可
        return false;
    }

}

1.2 配置拦截器并配置拦截的规则

红色部分就是将自定义的拦截器添加到项目中,然后绿色是一个扩展,就是在路由前面加前缀.

具体代码:

java 复制代码
package com.example.demo.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * Created with IntelliJ IDEA.
 * Description:将自定义拦截器加入到WebMvcConfigurer中,重写addInterceptors方法进行设置拦截规则
 * User: YAO
 * Date: 2023-07-14
 * Time: 15:04
 */
@Configuration
public class AppConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")  // 拦截规则设置,此处进行拦截所有请求
                .excludePathPatterns("/user/login")
                .excludePathPatterns("/user/reg")
                .excludePathPatterns("/user/get-num")
                .excludePathPatterns("/user/get-string")
                .excludePathPatterns("/img/**")
                .excludePathPatterns("/**/*.html");
    }

    // 所有的接⼝添加 api 前缀
      // 给路由进行添加前缀 ,设置为 true 表示启动前缀
//    @Override
//    public void configurePathMatch(PathMatchConfigurer configurer) {
//        configurer.addPathPrefix("api", c -> true);
//    }
}

我们对上述实现的拦截器进行验证

当进行点击的时候,因为没有Session所以直接跳转到我们设置的登录页面了.

可以看出没有进行设置过Cookie的,就是没有Session连接.

1.3 拦截器的原理源码分析

所有的方法都会执行 DispatcherServlet 中的 doDispatch 调度方法,观察源码发现:

点击这个applyPreHandle,applyPreHandle 中会获取所有拦截器 HandlerInterceptor 并执行拦截器中的 preHandle 方法,这与之前我们实现拦截器的步骤对应.

2. 统一异常处理

统一异常处理是指 在应用程序中定义一个公共的异常处理机制,用来处理所有的异常情况。 这样可以避免在应用程序中分散地处理异常,降低代码的复杂度和重复度,提高代码的可维护性和可扩展性.\

统⼀异常处理使⽤的是 @ControllerAdvice + @ExceptionHandler 来实现的,@ControllerAdvice 表示控制器通知类,@ExceptionHandler 是异常处理器,两个结合表示当出现异常的时候执行某个通知,也就是执行某个方法事件.

2.1 实现统一异常处理

2.2 测试统一异常处理

我们自己创建一个路由,其中内容出现一个空指针异常,我们查看是否能进行返回给前端处理.

我们设置的异常处理起到作用了.那么如果不设置,异常的处理,会出现什么样的现象?

就会直接将控制台打印的异常返回给前端,前端拿到数据就很懵.所以统一的异常处理还是很有必要的.

3. 统一的数据格式返回

3.1 统⼀数据返回格式的实现

下述是我们一般的统一返回的数据的格式

实现统一的数据返回格式可以使用 @ControllerAdvice + ResponseBodyAdvice 的方式实现,具体步骤如下:

  1. 创建一个类,并添加 @ControllerAdvice 注解;
  2. 实现 ResponseBodyAdvice 接口,并重写 supports 和 beforeBodyWrite 方法.

上述部分就是记性统一的数据格式返回.

其中需要注意的是,当返回的类型为String类型的时候,我们需要使用Jackson将String转换成json再进行返回.也就是绿色的部分.

java 复制代码
package com.example.demo.config;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.HashMap;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: YAO
 * Date: 2023-07-14
 * Time: 17:25
 */
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {

    @Autowired
    private ObjectMapper objectMapper;
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                  Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        HashMap<String, Object> result = new HashMap<>();
        result.put("state",1);
        result.put("msg", "");
        result.put("data", body);
        if (body instanceof String){
            // 需要特殊进行处理,因为String比较特殊,既不是基础类型也不是对象,进行重写的时候String用的格式化工具是自己独有的
            // 在body返回之前没有加载好.需要将String进行单独进行处理.使用Jackson将String转换成json再进行返回.
            try {
                return objectMapper.writeValueAsString(result);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
        return result;
    }
}

3.2 测试统一的数据返回

可以看出返回的格式是我们规定好的格式.这就变得更加统一.


好啦本文就总结到这了,点个赞吧,谢谢!!!

相关推荐
阿伟*rui2 小时前
配置管理,雪崩问题分析,sentinel的使用
java·spring boot·sentinel
XiaoLeisj4 小时前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
paopaokaka_luck4 小时前
【360】基于springboot的志愿服务管理系统
java·spring boot·后端·spring·毕业设计
dayouziei4 小时前
java的类加载机制的学习
java·学习
码农小旋风5 小时前
详解K8S--声明式API
后端
Peter_chq5 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
Yaml46 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
小小小妮子~6 小时前
Spring Boot详解:从入门到精通
java·spring boot·后端
hong1616886 小时前
Spring Boot中实现多数据源连接和切换的方案
java·spring boot·后端
aloha_7896 小时前
从零记录搭建一个干净的mybatis环境
java·笔记·spring·spring cloud·maven·mybatis·springboot