【JavaEE】SpringBoot 统一功能处理

发布于:2025-04-19 ⋅ 阅读:(19) ⋅ 点赞:(0)


一、拦截器

拦截器:拦截器是Spring框架提供的核⼼功能之⼀,主要⽤来拦截⽤⼾的请求,在指定⽅法前后,根据业务需要执
⾏预先设定的代码。

1.1 使用

拦截器的使用步骤有两步:

  1. 定义拦截器
  2. 注册配置拦截器

1.1 定义拦截器

⾃定义拦截器:需要实现HandlerInterceptor接⼝,并重写其所有⽅法。

  • preHandle()⽅法:⽬标⽅法执⾏前执⾏. 返回true:继续执⾏后续操作;返回false:中断后续操作。
  • postHandle()⽅法:⽬标⽅法执⾏后执⾏
  • afterCompletion()⽅法:视图渲染完毕后执⾏,最后执⾏(前后端分离后,后端开发现在⼏乎不涉及视图)
package com.example.library.interceptor;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("目标方法执行前 preHandle····");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("目标方法执行后 postHandle····");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("渲染后 afterHandle····");
    }
}


1.2 注册配置拦截器

实现WebMvcConfigurer接⼝,并重写addInterceptors⽅法

package com.example.library.config;


import com.example.library.interceptor.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private LoginInterceptor loginInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**");
    }
}


1.2 拦截器详解

1.2.1 拦截路径

在注册配置拦截器的时候,可以使用InterceptorRegistry类下的addPathPatterns方法添加需要拦截的路径,使用excludePathPatterns方法添加不需要拦截的路径。
那么路径的格式就如下表:

拦截路径 含义 举例
/* ⼀级路径 能匹配 /book 不能匹配/book/bookList
/** 任意级路径 能匹配 /book ,/book/bookList····
/book/* book路径下的一级路径 能匹配/book/bookList,不能匹配/book/bookList/books
/book/** /book下的任意级路径 能匹配/book/bookList,/book/bookList/books

1.2.2 拦截器执⾏流程

执行流程就是,在我们拦截器中拦截的路径的controller层执行前,先调用preHandle方法,在执行完controller层后调用afterHandle方法

1.3 适配器模式

适配器模式:相当于插头转换器,将⼀个类的接⼝,转换成客⼾期望的另⼀个接⼝,把两个不兼容的接⼝通过⼀定的⽅式使之兼容。

适配器模式可以看作⼀种"补偿模式",⽤来补救设计上的缺陷.应⽤这种模式算是"⽆奈之举",如果在设计初期,我们就能协调规避接⼝不兼容的问题,就不需要使⽤适配器模式了。

适配器模式⻆⾊

  • Target:⽬标接⼝(可以是抽象类或接⼝),客⼾希望直接⽤的接⼝。
  • Adaptee:适配者,但是与Target不兼容。
  • Adapter:适配器类,此模式的核⼼。通过继承或者引⽤适配者的对象,把适配者转为⽬标接⼝。
  • client:需要使⽤适配器的对象。

例子:

/**
 * slf4j接⼝ 
 */
public interface slf4jApi {
    void log(String log);
}

/**
 * log4j 接⼝ 
 */
public class Log4jApi {

    public void print(String p) {
        System.out.println("Log4jApi"+p);
    }
}

/**
 * slf4j和log4j适配器 
 */
public class Slf4jLog4JAdapter implements slf4jApi {
    private Log4jApi log4jApi;

    public Slf4jLog4JAdapter(Log4jApi log4jApi) {
        this.log4jApi = log4jApi;
    }

    @Override
    public void log(String log) {
        System.out.println("slf4jApi"+log);
    }
}

/**
 * 客⼾端调⽤ 
 */
public class Slf4jClient {
    public static void main(String[] args) {
        slf4jApi slf4jApi = new Slf4jLog4JAdapter(new Log4jApi());
        slf4jApi.log("slf4jApi日志");

    }
}

上面的例子下,我们不需要改变log4j的api,只需要通过适配器转换下,就可以更换⽇志框架,使用slf4j,保障系统的平稳运⾏。

二、统一数据返回格式

2.1 简单用法

一个最基本的同一数据返回格式的代码如下:

  1. 使⽤注解@ControllerAdvice 和接口 ResponseBodyAdvice 的⽅式实现
  2. 重写ResponseBodyAdvice的support和beforeBodyWrite方法
    1. supports⽅法: 判断是否要执⾏beforeBodyWrite⽅法。rue为执⾏,false不执⾏。通过该⽅法可以
      选择哪些类或哪些⽅法的response要进⾏处理。
    2. beforeBodyWrite⽅法: 对response⽅法进⾏具体操作处理。
package com.example.library;

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;

@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return false;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        return null;
    }
}

2.2 问题及解决

在上面的代码中,如果我们的接收的Response结果是String类型,又要转为我们定义的结果类型,就会出现错误

原因:
简单的说就是是String会导致源码中方法参数不匹配。

SpringMVC默认会注册⼀些⾃带的 HttpMessageConverter (从先后顺序排列分别为
ByteArrayHttpMessageConverter ,
StringHttpMessageConverter , SourceHttpMessageConverter ,
SourceHttpMessageConverter , AllEncompassingFormHttpMessageConverter )。
其中AllEncompassingFormHttpMessageConverter 会根据项⽬依赖情况 添加对应的
HttpMessageConverter
在依赖中引⼊jackson包后,容器会把 MappingJackson2HttpMessageConverter ⾃动注册到
messageConverters 链的末尾.
Spring会根据返回的数据类型,从 messageConverters 链选择合适的
HttpMessageConverter .
当返回的数据是⾮字符串时,使⽤的 MappingJackson2HttpMessageConverter 写⼊返回对象.
当返回的数据是字符串时, StringHttpMessageConverter 会先被遍历到,这时会认为
StringHttpMessageConverter 可以使⽤.
在 ((HttpMessageConverter) converter).write(body, selectedMediaType,
outputMessage) 的处理中, 调⽤⽗类的write⽅法
由于 StringHttpMessageConverter 重写了addDefaultHeaders⽅法,所以会执⾏⼦类的⽅法
然⽽⼦类 StringHttpMessageConverter 的addDefaultHeaders⽅法定义接收参数为String,此时t为Result类型,所以出现类型不匹配"Result cannot be cast to java.lang.String"的异常

解决方法:
当返回结果是String类型的时候,我们将其转换为json字符串。

    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
    //本来就是我们要的返回格式,就不用处理了
        if(body instanceof Result) {
            return body;
        }
        ObjectMapper objectMapper = new ObjectMapper();
        if(body instanceof String) {
            try {
                return objectMapper.writeValueAsString(Result.success(body));
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        }
        return Result.success(body);
    }

三、统一异常处理

统⼀异常处理使⽤的是@ControllerAdvice +@ExceptionHandler 来实现的。
@ControllerAdvice 表⽰控制器通知类, @ExceptionHandler 是异常处理器。

package com.example.library.config;

import com.example.library.model.Result;
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;

@ControllerAdvice
@Slf4j
@ResponseBody
public class ExceptionHandle {
    @ExceptionHandler
    public Object function(Exception e) {
        log.error("异常:"+ e);
       return Result.fail("内部错误");
    }
}


网站公告

今日签到

点亮在社区的每一天
去签到