聊城手机站网站公司益阳购物网站开发设计

张小明 2025/12/31 8:46:46
聊城手机站网站公司,益阳购物网站开发设计,绵阳网站的建设,温州平面设计公司目录 Spring Boot统一功能处理详解#xff08;新手完整版#xff09; 1. 拦截器详解 1.1 什么是拦截器 1.2 完整代码实现#xff08;逐行注释#xff09; 1.2.1 定义登录拦截器 1.2.2 注册拦截器到Spring MVC 1.3 拦截器执行流程图解 2. 统一数据返回格式 2.1 为什…目录Spring Boot统一功能处理详解新手完整版1. 拦截器详解1.1 什么是拦截器1.2 完整代码实现逐行注释1.2.1 定义登录拦截器1.2.2 注册拦截器到Spring MVC1.3 拦截器执行流程图解2. 统一数据返回格式2.1 为什么需要统一格式2.2 完整实现代码2.2.1 统一结果类Result2.2.2 全局响应处理器ResponseAdvice2.3 String类型问题的代码体现3. 统一异常处理3.1 为什么需要统一处理3.2 完整实现代码3.2.1 全局异常处理器3.2.2 自定义业务异常3.3 异常处理调用链演示4. 完整项目结构示例5. 知识点与代码的完整对应关系表6. 新手最容易踩的坑6.1 拦截器不生效6.2 String类型异常6.3 异常没捕获7. 总结与最佳实践7.1 三者的协作流程图7.2 代码层面的最佳实践8. 最后的话这里是为您生成的Markdown文档内容完全按照您的要求整理保留了所有代码注释和详细讲解。Spring Boot Unified Function HandlingSpring Boot统一功能处理详解新手完整版我会整合拦截器、统一返回格式和异常处理三部分内容提供带逐行注释的完整代码并详细说明每个知识点如何体现在代码中。1. 拦截器详解1.1 什么是拦截器拦截器是Spring MVC提供的安检门机制能在请求到达Controller之前、之后以及请求完成时插入自定义逻辑。它就像你去商场时要经过的安检安检前检查包裹(preHandle)安检后刷卡(postHandle)离开时记录时间(afterCompletion)。核心应用场景登录认证检查用户是否登录如未登录不能访问订单页面日志记录记录每个请求的处理时间权限控制判断用户是否有权限访问某个接口性能监控统计接口响应时间1.2 完整代码实现逐行注释1.2.1 定义登录拦截器// import关键字导入其他包中的类就像你要用别人的工具得先拿来 // slf4jSimple Logging Facade for Java日志门面框架类似一个日志的翻译官 // 它能让你在不改代码的情况下切换log4j、logback等具体实现 import lombok.extern.slf4j.Slf4j; // Spring框架的组件注解标记这个类为Spring管理的Bean就像商品贴上条形码入库 // Spring容器会自动创建它的实例其他地方可以直接借用 import org.springframework.stereotype.Component; // Spring MVC的核心接口实现它就拥有了拦截请求的能力 // 类似安检员资格证只有拿到这个证才能在指定位置检查 import org.springframework.web.servlet.HandlerInterceptor; // Servlet规范提供的HTTP请求对象封装了客户端发送的所有信息 // 包括请求头、参数、Cookie等相当于快递包裹单 import jakarta.servlet.http.HttpServletRequest; // Servlet规范提供的HTTP响应对象用于向客户端返回数据 // 相当于快递回执单你可以填写返回内容和状态 import jakarta.servlet.http.HttpServletResponse; // Session是会话对象用于在多次请求间保存用户状态 // 就像商场的储物柜存一次东西多次取前提是有钥匙 import jakarta.servlet.http.HttpSession; /** * 登录拦截器 * Slf4jLombok注解自动生成日志记录器log不用写LoggerFactory.getLogger() * Component让Spring管理这个拦截器否则无法注册使用 */ Slf4j Component public class LoginInterceptor implements HandlerInterceptor { /** * preHandle在Controller方法执行前调用安检门第一道关卡 * 返回true 放行绿灯返回false 拦截红灯 * * param request HTTP请求对象包裹单 * param response HTTP响应对象回执单 * param handler 要执行的Controller方法目标商店 * return boolean 是否允许通过 */ Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 记录日志表示拦截器开始工作 // {}是占位符实际值会替换到这里比字符串拼接性能更好 log.info(LoginInterceptor.preHandle() - 开始检查用户登录状态, URI: {}, request.getRequestURI()); // 获取Session参数false表示没有就别新建 // 就像找储物柜钥匙false表示找不到就别给我新钥匙 HttpSession session request.getSession(false); // 检查Session是否存在且包含用户信息 // 是短路与左边为false右边不执行避免空指针 if (session ! null session.getAttribute(user) ! null) { log.info(用户已登录放行请求); return true; // 放行继续执行Controller里的方法 } // 没登录设置401状态码Unauthorized未授权 // 就像商场保安说请出示会员卡 response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // SC_UNAUTHORIZED就是401的常量 log.warn(用户未登录请求被拦截); return false; // 拦截不执行后续操作 } /** * postHandle在Controller方法执行后、视图渲染前调用第二道关卡 * 可以修改ModelAndView里的数据或视图名称 * 就像买完东西后可以在包装袋上加点装饰 */ Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, org.springframework.web.servlet.ModelAndView modelAndView) throws Exception { log.info(LoginInterceptor.postHandle() - Controller执行完毕准备渲染视图); // 示例可以给所有页面统一添加当前用户信息 if (modelAndView ! null) { modelAndView.addObject(currentTime, System.currentTimeMillis()); } } /** * afterCompletion在整个请求完成后调用最后关卡 * 视图已经渲染完毕客户端已经收到响应 * 通常用于资源清理比如关闭流、记录最终日志 * 就像顾客离开商场后保安做收尾工作 */ Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { log.info(LoginInterceptor.afterCompletion() - 请求处理完成状态码: {}, response.getStatus()); // 如果有异常可以在这里记录 if (ex ! null) { log.error(请求处理过程中发生异常, ex); } } }代码如何体现拦截器特性implements HandlerInterceptor直接实现接口这是Java的契约编程相当于签了合同就必须实现三个方法。preHandle返回true/false核心机制代码中通过if(session...)判断来决定是否放行这就是拦截的本质。三个方法的执行顺序通过日志可以观察到Spring MVC框架保证了先执行preHandle再执行Controller然后postHandle最后afterCompletion。1.2.2 注册拦截器到Spring MVC// Spring的依赖注入注解自动从容器中找LoginInterceptor实例并注入 // 就像你点外卖Autowired表示平台自动分配骑手你不用自己找 import org.springframework.beans.factory.annotation.Autowired; // 标记类为配置类替代传统的XML配置文件 // Configuration 这是一个配置文件Spring启动时要读取 import org.springframework.context.annotation.Configuration; // 拦截器注册表用于添加和管理拦截器 // 就像商场的安检门管理中心可以指定哪些门需要安检 import org.springframework.web.servlet.config.annotation.InterceptorRegistry; // Spring MVC配置接口实现它可以自定义MVC行为如添加拦截器、资源处理器等 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; // 工具类用于创建不可变列表 import java.util.Arrays; import java.util.List; /** * Web配置类 * Configuration告诉Spring我是个配置类启动时加载我 * implements WebMvcConfigurer表示我要自定义Spring MVC的行为 */ Configuration public class WebConfig implements WebMvcConfigurer { // Autowired自动注入Spring容器中已经存在的LoginInterceptor对象 // 这里不需要newSpring会帮我们创建好并注入 Autowired private LoginInterceptor loginInterceptor; // 定义不需要拦截的路径列表 // Arrays.asList()快速创建固定大小的列表 // 就像列个免检名单名单上的人不用过安检 private ListString excludePaths Arrays.asList( /user/login, // 登录接口本身不能拦截否则无法登录 /user/register, // 注册接口 /static/**, // 静态资源CSS/JS/图片不用拦截 /error/** // 错误页面 ); /** * addInterceptors重写父类方法用于注册拦截器 * Spring MVC启动时会自动调用这个方法 * * param registry 拦截器注册表安检门管理中心 */ Override public void addInterceptors(InterceptorRegistry registry) { // registry.addInterceptor()添加拦截器 // addPathPatterns(/**)拦截所有路径**表示任意层级子路径 // excludePathPatterns()排除指定路径白名单 // 链式调用像搭积木一样连续配置 registry.addInterceptor(loginInterceptor) .addPathPatterns(/**) // 先全部拦截 .excludePathPatterns(excludePaths); // 再开放部分路径 log.info(拦截器注册完成已应用登录验证); } }代码如何体现配置灵活性/**的匹配规则代码中通过字符串模式匹配实现/表示路径分隔符*通配符表示匹配任意字符。excludePathPatterns()代码中通过列表排除实现了黑名单机制这是实际项目中最常用的方式。ConfigurationSpring的约定启动时扫描所有标记了此注解的类自动执行配置方法。1.3 拦截器执行流程图解用户请求 → Tomcat → DispatcherServlet → applyPreHandle() ↓ (返回false则中断) ↓ (返回true则继续) 拦截器preHandle() Controller方法执行 ↓ ↓ 拦截器postHandle() ←─────────── 方法返回 ↓ 视图渲染 ↓ 拦截器afterCompletion() ←─────── 请求完成代码体现在DispatcherServlet.doDispatch()方法中Spring源码三个方法被按顺序调用这就是结论拦截器有固定执行顺序的实现依据。2. 统一数据返回格式2.1 为什么需要统一格式想象你是前端开发后端同事张三返回{name:张三}李四返回李四王五返回true。你要写三种不同的解析逻辑维护成本极高统一格式后所有接口都返回{ status: 200, data: 实际数据, errorMessage: , timestamp: 1234567890 }前端只需写一套解析逻辑取data字段即可。2.2 完整实现代码2.2.1 统一结果类Result// Lombok的Data注解自动生成getter/setter/toString/equals/hashCode方法 // 相当于让Lombok帮你写样板代码你只需关注业务字段 import lombok.Data; /** * 统一返回结果类模板 * param T 泛型表示data字段可以是任何类型String、User、List等 * 就像快递盒可以装任何东西 */ Data public class ResultT { // int状态码用数字表示结果200成功500失败等 private int status; // String错误信息失败时告诉用户/前端具体原因 private String errorMessage; // T泛型实际业务数据成功时存放返回内容 private T data; // long时间戳记录响应的毫秒时间用于调试和监控 private long timestamp; /** * 私有构造方法防止外部直接new Result() * 强制使用工厂方法创建保证统一性 */ private Result() { // System.currentTimeMillis()获取当前系统时间的毫秒值 // 从1970年1月1日00:00:00到现在的总毫秒数 this.timestamp System.currentTimeMillis(); } /** * 静态工厂方法创建成功响应 * static类方法无需创建对象直接调用 Result.success() * T泛型方法让编译器自动推断T的类型 * * param data 要返回的业务数据 * return 包装后的统一结果 */ public static T ResultT success(T data) { ResultT result new Result(); result.setStatus(200); // HTTP状态码200表示成功 result.setData(data); // 将业务数据放入data字段 // errorMessage保持null表示没有错误 return result; } /** * 静态工厂方法创建失败响应 */ public static T ResultT fail(String errorMessage) { ResultT result new Result(); result.setStatus(500); // HTTP状态码500表示服务器错误 result.setErrorMessage(errorMessage); // 设置错误详情 // data保持null return result; } /** * 自定义状态码和数据的响应 * 用于特殊场景如参数验证失败400未授权401 */ public static T ResultT custom(int status, String errorMessage, T data) { ResultT result new Result(); result.setStatus(status); result.setErrorMessage(errorMessage); result.setData(data); return result; } }代码如何体现统一性私有构造函数代码中的private Result()强制所有创建必须通过success()/fail()方法这就是统一的制度保障。泛型T代码中的泛型设计让Result能包装任何类型这是通用的实现手段。工厂方法static方法让创建点集中便于后续添加统一逻辑如自动填充traceId。2.2.2 全局响应处理器ResponseAdvice// Spring核心类方法参数描述包含返回值的类型、注解等信息 // 就像产品说明书告诉你这个方法的返回值是什么 import org.springframework.core.MethodParameter; // HTTP媒体类型如application/json, text/html // 决定返回什么格式的数据 import org.springframework.http.MediaType; // 服务器端HTTP请求抽象Spring封装后的请求对象 import org.springframework.http.server.ServerHttpRequest; // 服务器端HTTP响应抽象Spring封装后的响应对象 import org.springframework.http.server.ServerHttpResponse; // ControllerAdvice控制器通知对所有的Controller生效 // 类似广播站向所有Controller发送统一指令 import org.springframework.web.bind.annotation.ControllerAdvice; // ResponseBodyAdvice响应体增强接口在返回数据写入响应前进行修改 // 就像包装工在商品出厂前统一装箱 import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; // Jackson库的核心类用于Java对象和JSON字符串互转 // 就像翻译官把Java对象翻译成JSON语言 import com.fasterxml.jackson.databind.ObjectMapper; // Lombok的SneakyThrows注解自动处理受检异常不用写try-catch // 简化代码但不建议在复杂场景使用 import lombok.SneakyThrows; // Lombok的日志注解 import lombok.extern.slf4j.Slf4j; /** * 全局响应处理器 * ControllerAdvice这个类会影响所有的Controller返回值 * Slf4j记录日志 */ Slf4j ControllerAdvice public class ResponseAdvice implements ResponseBodyAdviceObject { /** * ObjectMapper是Jackson的核心类线程安全可以共享一个实例 * 用于将Result对象转换为JSON字符串 */ private static ObjectMapper mapper new ObjectMapper(); /** * supports方法决定是否要执行beforeBodyWrite * return true表示所有返回值我都要处理 * return false表示这个返回值我不处理原样返回 * * param returnType 方法返回类型产品说明书 * param converterType 消息转换器类型翻译官类型 * return 是否处理 */ Override public boolean supports(MethodParameter returnType, Class converterType) { // 这里我们统一处理所有返回类型所以直接返回true // 实际项目中可以根据注解、包名等条件过滤 return true; } /** * beforeBodyWrite在响应体写入前执行核心方法 * 这是装箱的地方把原始数据包装成Result * * param body 原始的返回值可能要包装的商品 * param returnType 方法返回类型 * param selectedContentType 选中的内容类型如application/json * param selectedConverterType 选中的转换器 * param request 请求对象 * param response 响应对象 * return 包装后的对象 */ SneakyThrows // 自动抛出异常简化代码 Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { log.info(ResponseAdvice: 包装返回值, 原始类型{}, body ! null ? body.getClass().getName() : null); // 1. 如果已经是Result类型说明已经被包装过了可能是手动返回的 // instanceofJava的实例判断关键字你是Result吗 if (body instanceof Result) { log.info(已经是Result类型无需包装); return body; // 直接返回避免重复包装 } // 2. 如果是String类型需要特殊处理坑重点 // 因为String类型的返回值会被StringHttpMessageConverter处理 // 它只接受String不接受Result对象 if (body instanceof String) { log.info(String类型使用Jackson转为JSON字符串); // 先包装成Result再用ObjectMapper转为JSON字符串 // writeValueAsString将Java对象转为JSON字符串 return mapper.writeValueAsString(Result.success(body)); } // 3. 其他类型Object、List、自定义类等 // 直接调用Result.success()包装 log.info(普通对象类型直接包装); return Result.success(body); } }代码如何体现统一包装逻辑implements ResponseBodyAdviceObject代码层面的契约Spring MVC保证所有Controller返回前都会调用此类的beforeBodyWrite。if (body instanceof Result)代码中的短路逻辑避免重复包装这是统一的保护机制。if (body instanceof String)代码中的特殊处理这是解决String类型转换异常的关键体现了对Spring内部机制的深入理解。2.3 String类型问题的代码体现问题根源代码层面分析在org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport中Spring注册了默认的消息转换器// Spring源码片段注释说明 protected final void addDefaultHttpMessageConverters(ListConverter converters) { // 1. ByteArray转换器处理byte[] converters.add(new ByteArrayHttpMessageConverter()); // 2. String转换器处理String优先级高 converters.add(new StringHttpMessageConverter()); // 3. 如果Classpath中有Jackson才添加JSON转换器优先级低 if (jackson2Present) { converters.add(new MappingJackson2HttpMessageConverter()); } }结论与代码的联系当你的Controller返回String时Spring会遍历转换器列表第一个匹配的StringHttpMessageConverter被选中。它期望收到String但ResponseAdvice返回了Result对象类型不匹配导致异常。我们的解决方案代码中if (body instanceof String) { return mapper.writeValueAsString(Result.success(body)); // 主动转换为String }这就是在代码层面主动适配StringHttpMessageConverter的行为提前把Result转为JSON字符串。3. 统一异常处理3.1 为什么需要统一处理想象你的Controller里有100个接口每个都写try { // 业务逻辑 } catch (Exception e) { return Result.fail(错误); }重复代码太多维护困难。统一异常处理就像一个中央错误处理中心所有未捕获的异常都汇集到这里处理。3.2 完整实现代码3.2.1 全局异常处理器// 导入Result统一结果类 import com.example.demo.model.Result; // 异常处理核心注解标记此方法处理哪种异常 import org.springframework.web.bind.annotation.ExceptionHandler; // 控制器通知注解对全局生效 import org.springframework.web.bind.annotation.ControllerAdvice; // 标识返回JSON数据不是视图 import org.springframework.web.bind.annotation.ResponseBody; // HTTP状态码枚举404, 500等 import org.springframework.http.HttpStatus; // 指定响应状态码的注解 import org.springframework.web.bind.annotation.ResponseStatus; // 日志注解 import lombok.extern.slf4j.Slf4j; /** * 全局异常处理器 * ControllerAdvice捕获所有Controller抛出的异常 * ResponseBody返回JSON格式的错误信息 */ Slf4j ResponseBody ControllerAdvice public class ErrorAdvice { /** * ExceptionHandler(Exception.class)捕获所有Exception及其子类 * 这是兜底处理器处理未被特定方法捕获的异常 * * ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)设置HTTP状态码为500 * 告诉浏览器服务器内部错误 * * param e 捕获到的异常对象包含错误堆栈 * return 统一错误响应 */ ExceptionHandler(Exception.class) ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public ResultObject handleGeneralException(Exception e) { // log.error记录错误日志{}是占位符 // e.getMessage()获取异常简要信息 // e第三个参数传入异常对象会打印完整堆栈 log.error(系统发生未处理异常: {}, e.getMessage(), e); // 返回统一错误格式隐藏内部细节给友好提示 return Result.fail(系统繁忙请稍后再试); } /** * 专门处理空指针异常 * 当代码中出现null.xx()时触发 * * ResponseStatus返回500状态码 */ ExceptionHandler(NullPointerException.class) ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public ResultObject handleNullPointerException(NullPointerException e) { log.error(发生空指针异常: {}, e.getMessage(), e); // 提供比系统繁忙更具体的提示但不过度暴露细节 return Result.fail(系统错误: 未初始化的对象被引用); } /** * 处理算术异常如除零错误 * * ResponseStatus(HttpStatus.BAD_REQUEST)返回400状态码 * 400表示客户端请求有误这里是数学逻辑错误 */ ExceptionHandler(ArithmeticException.class) ResponseStatus(HttpStatus.BAD_REQUEST) public ResultObject handleArithmeticException(ArithmeticException e) { log.error(发生算术异常: {}, e.getMessage(), e); return Result.fail(计算错误: e.getMessage()); } /** * 处理非法参数异常通常由参数校验失败抛出 * 如NotNull校验不通过 */ ExceptionHandler(IllegalArgumentException.class) ResponseStatus(HttpStatus.BAD_REQUEST) public ResultObject handleIllegalArgumentException(IllegalArgumentException e) { log.warn(参数校验失败: {}, e.getMessage()); // warn级别表示警告不是严重错误 return Result.fail(参数错误: e.getMessage()); } }代码如何体现统一ControllerAdvice代码层面的作用域控制Spring会创建代理拦截所有Controller抛出的异常这是统一的实现基础。ExceptionHandler(Exception.class)代码中的异常类型匹配Spring通过 instanceof 判断异常类型选择合适的处理方法。return Result.fail()所有处理器都返回Result类型这是统一格式的代码约束。3.2.2 自定义业务异常// 继承RuntimeException运行时异常不需要强制try-catch // 区别于受检异常如IOException业务异常通常是可预期的 public class BusinessException extends RuntimeException { // 业务错误码比HTTP状态码更精细化 // 如1001用户不存在1002余额不足 private int errorCode; /** * 构造方法只传错误信息 * 默认错误码为500 */ public BusinessException(String message) { super(message); // 调用父类构造方法 this.errorCode 500; // 默认服务器错误 } /** * 构造方法传错误码和错误信息 */ public BusinessException(int errorCode, String message) { super(message); this.errorCode errorCode; } // Getter方法让外部可以获取错误码 public int getErrorCode() { return errorCode; } } /** * 用户相关业务异常 * 继承BusinessException语义更清晰 */ public class UserException extends BusinessException { public UserException(String message) { super(10001, message); // 固定用户模块错误码为10001 } } /** * 资源未找到异常 */ public class ResourceNotFoundException extends BusinessException { public ResourceNotFoundException(String resourceName, Object id) { super(404, String.format(%s[id%s]不存在, resourceName, id)); // String.format格式化字符串%s是占位符 // 例如new ResourceNotFoundException(图书, 1) // 消息为图书[id1]不存在 } }代码如何体现业务语义extends RuntimeException代码中的继承关系表明这是非受检异常调用者可以选择处理或不处理。String.format()代码中的字符串模板动态生成错误信息这是友好提示的实现方式。不同异常类代码中通过类名区分业务场景UserException、ResourceNotFoundException这是精细化处理的代码基础。3.3 异常处理调用链演示RestController RequestMapping(/test) public class TestController { GetMapping(/user/{id}) public ResultUser getUser(PathVariable Long id) { // 1. 参数校验 if (id null || id 0) { // 抛出参数异常会被handleIllegalArgumentException捕获 throw new IllegalArgumentException(用户ID必须为正整数); } // 2. 查询用户 User user userService.findById(id); if (user null) { // 抛出业务异常需要额外处理逻辑见下 throw new ResourceNotFoundException(用户, id); } // 3. 返回结果会被ResponseAdvice包装 return Result.success(user); // 实际返回的是Result里的User对象 } } // 在ErrorAdvice中添加业务异常处理器 ExceptionHandler(BusinessException.class) ResponseStatus(HttpStatus.BAD_REQUEST) public ResultObject handleBusinessException(BusinessException e) { // 代码体现 instanceof BusinessException判断 // 由于ResourceNotFoundException extends BusinessException所以会被此方法捕获 log.warn(业务异常[错误码:{}]: {}, e.getErrorCode(), e.getMessage()); // 使用错误码和错误信息创建Result // 代码体现Result.custom()支持自定义状态码 return Result.custom(e.getErrorCode(), e.getMessage(), null); }结论与代码的联系结论异常处理有优先级子类异常优先于父类。代码体现Spring在ExceptionHandler匹配时会优先匹配最具体的异常类型。由于ResourceNotFoundException是BusinessException的子类如果两个处理方法都存在Spring会选择更匹配的那个。但通常我们只保留父类处理器通过instanceof和getErrorCode()来区分具体类型。4. 完整项目结构示例src/main/java/com/example/demo/ ├── DemoApplication.java // 启动类 ├── config/ │ ├── WebConfig.java // 拦截器注册上面已详解 │ └── ResponseAdvice.java // 统一返回上面已详解 ├── controller/ │ ├── UserController.java // 用户接口 │ └── BookController.java // 图书接口 ├── interceptor/ │ └── LoginInterceptor.java // 登录拦截上面已详解 ├── exception/ │ ├── ErrorAdvice.java // 统一异常上面已详解 │ ├── BusinessException.java // 业务异常基类 │ ├── UserException.java // 用户异常 │ └── ResourceNotFoundException.java // 资源未找到 ├── model/ │ ├── Result.java // 统一结果上面已详解 │ └── User.java // 用户实体 └── constant/ └── Constants.java // 常量类5. 知识点与代码的完整对应关系表知识点结论代码体现位置代码如何体现拦截器执行顺序preHandle→Controller→postHandle→afterCompletionDispatcherServlet.doDispatch()源码方法按顺序调用applyPreHandle()在ha.handle()之前applyPostHandle()在之后拦截路径匹配/**匹配任意层级addPathPatterns(/**)字符串**被Spring的AntPathMatcher类解析递归匹配所有子路径统一返回包装所有返回值变成ResultResponseAdvice.beforeBodyWrite()通过instanceof判断类型调用Result.success()包装String类型问题String需特殊处理防止转换异常if (body instanceof String)分支主动调用ObjectMapper.writeValueAsString()转为JSON字符串适配StringHttpMessageConverter异常处理优先级子类异常优先匹配ExceptionHandler(Exception.class)位置Spring通过ExceptionDepthComparator比较异常继承深度深度小的优先适配器模式作用解耦DispatcherServlet和ControllerHandlerAdapter接口及其实现类supports()方法做类型检查handle()方法做统一调用DispatcherServlet无需关心具体类型开闭原则对扩展开放对修改关闭新增XxxHandlerAdapter类添加新Controller类型时只需新增适配器类无需修改DispatcherServlet源码6. 新手最容易踩的坑6.1 拦截器不生效问题LoginInterceptor写了但没效果。原因没实现WebMvcConfigurer或没加Configuration。代码检查点确认WebConfig类上有Configuration且实现了WebMvcConfigurer。6.2 String类型异常问题返回String时报错ClassCastException。原因没做instanceof String判断。代码检查点确认ResponseAdvice中有if (body instanceof String)分支。6.3 异常没捕获问题抛了异常但返回500错误。原因ControllerAdvice没扫描到或异常类型不匹配。代码检查点确认ErrorAdvice在Spring Boot主启动类的同级或子包下。7. 总结与最佳实践7.1 三者的协作流程图用户请求 ↓ LoginInterceptor.preHandle() (登录检查) ↓ (放行) Controller执行业务 ↓ (抛异常) ErrorAdvice捕获异常 → 返回Result.fail() ↓ (正常返回) ResponseAdvice.beforeBodyWrite() → 包装成Result.success() ↓ 返回给前端统一格式7.2 代码层面的最佳实践// Controller层示例保持简洁 RestController RequestMapping(/api/user) public class UserController { Autowired private UserService userService; GetMapping(/{id}) public User getUser(PathVariable Long id) { // 直接返回User不包Result // 1. 参数校验失败抛IllegalArgumentException if(id 0) throw new IllegalArgumentException(ID无效); // 2. 业务逻辑可能抛BusinessException User user userService.getById(id); // 3. 直接返回数据由ResponseAdvice统一包装 // 代码体现这里不包Result保持简洁 return user; } }代码体现最佳实践Controller层不手动包装Result代码职责单一只关注业务。Service层抛出业务异常throw new BusinessException(...)代码语义清晰。全局层ResponseAdvice和ErrorAdvice做统一处理代码复用性高。8. 最后的话适配器模式本质回顾在你的Spring MVC图片案例中代码层面的区别是不用适配器If-Else地狱// DispatcherServlet必须知道所有Controller类型 if (handler instanceof Controller) { // 强转调用 } else if (handler instanceof HttpRequestHandler) { // 强转调用 } // 新增类型必须修改这段代码用适配器开闭原则// DispatcherServlet代码永远不变 HandlerAdapter adapter getHandlerAdapter(handler); // 自动找到适配器 adapter.handle(request, response, handler); // 统一调用 // 新增Controller类型只需添加新适配器类 public class NewControllerAdapter implements HandlerAdapter { public boolean supports(Object handler) { return handler instanceof NewType; } public ModelAndView handle(...) { /* 新逻辑 */ } } // DispatcherServlet源码无需修改结论与代码联系的终极答案设计模式的结论是通过代码结构体现的。适配器模式的解耦结论体现在DispatcherServlet不依赖具体Controller类型而是依赖HandlerAdapter接口。新增Controller时代码修改范围被限制在新增类而不是修改核心调度逻辑这就是开闭原则的代码级证明。希望这份详细的代码注释和知识点解析能帮你彻底理解
版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

国外的网站可以做百度推广吗美食网站建设策划书

分布式协作软件的安全与互操作性解析 在如今的数字化时代,分布式协作软件在企业和个人的日常工作中扮演着越来越重要的角色。像Groove这样的软件,致力于在完全去中心化的对等模式下运行,同时兼顾安全性和易用性。而在文件共享网络领域,如何实现不同网络之间的互操作性也是…

张小明 2025/12/25 22:43:06 网站建设

旅游网站建设课程设计微信网站怎么建立

5分钟搞定JeecgBoot分库分表:ShardingSphere完整实战手册 【免费下载链接】jeecg-boot jeecgboot/jeecg-boot 是一个基于 Spring Boot 的 Java 框架,用于快速开发企业级应用。适合在 Java 应用开发中使用,提高开发效率和代码质量。特点是提供…

张小明 2025/12/25 22:43:07 网站建设

北京网站建设制作株洲网站制作

OpenAI GPT-OSS-20B:Apache 2.0协议下的企业级大模型新标杆 【免费下载链接】gpt-oss-20b-BF16 项目地址: https://ai.gitcode.com/hf_mirrors/unsloth/gpt-oss-20b-BF16 导语 OpenAI推出的GPT-OSS-20B开源大模型凭借Apache 2.0许可与MXFP4量化技术&#x…

张小明 2025/12/25 22:43:04 网站建设

自动优化网站软件没有了网站字体排版技巧

想要在普通PC上体验苹果系统吗?VMware Unlocker就是你需要的终极解决方案!这款开源工具能够解除VMware对macOS系统的限制,让你在Windows和Linux平台上轻松创建和运行macOS虚拟机,完全免费且操作简单。 【免费下载链接】unlocker …

张小明 2025/12/25 22:43:06 网站建设

免费建社交网站网站网络营销公司

前言 在爬虫应用场景中,单页面爬取仅能满足简单的数据采集需求,而批量爬取多页面、多目标网址的数据才是解决实际业务问题的核心能力。批量爬取的核心挑战在于如何高效管理待爬取 URL 队列、控制爬取节奏、避免重复爬取,并保证大规模数据采集…

张小明 2025/12/25 22:43:04 网站建设

上海高档网站建设中国交通建设集团网站

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

张小明 2025/12/31 7:23:55 网站建设