SpringMVC 异常处理?Spring 父子容器?

张开发
2026/4/11 1:35:09 15 分钟阅读

分享文章

SpringMVC 异常处理?Spring 父子容器?
深入剖析 SpringMVC 异常处理与 Spring 父子容器在 Spring SpringMVC 的 Web 应用中有两个核心概念经常被开发者提起异常处理机制和父子容器关系。理解它们不仅有助于写出更健壮的代码还能避免许多隐藏的配置陷阱。本文将从源码逻辑到实际应用结合流程图与结构图为你系统梳理这两块知识。一、SpringMVC 异常处理机制1.1 核心流程谁来解决我的异常当 SpringMVC 的DispatcherServlet在处理请求过程中抛出异常时它并不会直接把异常抛给容器而是委托给一组异常处理器HandlerExceptionResolver。流程如下能不能全部不能处理Controller 抛出异常遍历所有HandlerExceptionResolver当前Resolver能否处理?调用 resolver.resolveException()返回 ModelAndView 或错误响应继续下一个Resolver继续向上抛出异常最终由容器处理SpringMVC 默认注册了三个异常解析器它们按固定顺序执行一旦某个解析器返回非空的ModelAndView后续解析器就不再执行。1.2 三大默认异常解析器解析器作用触发条件ExceptionHandlerExceptionResolver处理被ExceptionHandler注解标记的方法控制器类或ControllerAdvice中定义了匹配的异常处理方法ResponseStatusExceptionResolver处理带有ResponseStatus注解的异常类抛出的异常类上标注了ResponseStatusDefaultHandlerExceptionResolver处理 SpringMVC 内置的标准异常如TypeMismatchException、MissingServletRequestParameterException等1.2.1 ExceptionHandlerExceptionResolver这是最常用、最灵活的解析器。它会在Controller或ControllerAdvice类中查找带有ExceptionHandler注解的方法根据异常类型进行匹配。RestControllerAdvicepublicclassGlobalExceptionHandler{ExceptionHandler(ArithmeticException.class)publicResulthandleArithmetic(ArithmeticExceptione){returnResult.error(除数不能为零);}}1.2.2 ResponseStatusExceptionResolver如果自定义异常类上标注了ResponseStatus当该异常被抛出时解析器会自动设置 HTTP 状态码。ResponseStatus(HttpStatus.NOT_FOUND)publicclassResourceNotFoundExceptionextendsRuntimeException{}1.2.3 DefaultHandlerExceptionResolver处理 SpringMVC 自身抛出的标准异常比如参数类型不匹配、缺少必要参数等。它会返回一个默认的错误视图如 400 错误页面。1.3 异常处理整体时序图ExceptionResolversControllerHandlerAdapterDispatcherServletClientExceptionResolversControllerHandlerAdapterDispatcherServletClientalt[能处理]loop[每个解析器]请求执行 Controller调用方法抛出异常传播异常遍历解析器能否处理解析异常 → ModelAndView返回错误响应注意如果所有解析器都返回null则异常会继续上抛最终导致 500 错误或由 Servlet 容器处理。1.4 实战建议使用ControllerAdvice统一处理业务异常避免在每个 Controller 中重复写 try-catch。自定义异常时可结合ResponseStatus快速指定状态码。若需记录日志可在全局异常处理方法中添加日志输出。二、Spring 父子容器Spring SpringMVC在传统的 SSMSpring SpringMVC MyBatis项目中应用启动时会创建两个 Spring 容器父容器Root WebApplicationContext由ContextLoaderListener启动加载applicationContext.xml或通过Configuration配置的 Spring 核心组件如 Service、DAO、数据源、事务管理等。子容器Servlet WebApplicationContext由DispatcherServlet启动加载spring-mvc.xml或 MVC 配置如 Controller、视图解析器、拦截器等。2.1 父子容器结构图子容器 SpringMVC父容器 Spring父容器无法访问子容器子容器可以访问父容器Service BeanDAO Bean数据源事务管理器Controller Bean拦截器视图解析器2.2 核心区别与规则特性父容器Spring子容器SpringMVC加载时机应用启动时由ContextLoaderListener加载DispatcherServlet初始化时加载配置位置applicationContext.xml或Configurationspring-mvc.xml或ConfigurationEnableWebMvc管理的 BeanService、DAO、数据源、事务、AOP 等Controller、拦截器、视图解析器、局部异常处理器能否访问对方❌ 不能访问子容器的 Bean✅ 可以访问父容器的 BeanProperties 隔离各自加载的*.properties文件互不共享子容器无法直接使用父容器的属性文件除非通过 Bean 引用2.3 为什么这样设计职责分离父容器负责业务逻辑和数据层子容器只负责 Web 层。子容器依赖父容器但父容器不应感知 Web 层避免循环依赖。模块化可以在同一个父容器下挂载多个子容器例如多个DispatcherServlet对应不同模块。性能优化Controller 的创建和销毁由子容器管理父容器不需要扫描 Web 层的注解。2.4 常见问题与注意事项❌ 问题1父容器中扫描了Controller如果父容器配置了context:component-scan base-packagecom.example /未排除Controller会导致两个问题Controller 被父容器和子容器各初始化一次可能出现事务代理失效。ResponseBody等 Web 相关注解可能无法正常工作。✅ 正确做法父容器配置扫描时排除Controllercontext:component-scanbase-packagecom.examplecontext:exclude-filtertypeannotationexpressionorg.springframework.stereotype.Controller//context:component-scan子容器只扫描Controllercontext:component-scanbase-packagecom.exampleuse-default-filtersfalsecontext:include-filtertypeannotationexpressionorg.springframework.stereotype.Controller//context:component-scan❌ 问题2在父容器 Bean 中试图Autowired子容器的 Bean例如在 Service 中注入 Controller —— 启动时会报错因为父容器找不到对应类型的 Bean。✅ 解决方案重新审视设计Service 不应该依赖 Controller。如果确实需要极少见可以通过ApplicationContext手动获取子容器的 Bean但强烈不推荐。❌ 问题3属性文件隔离父容器加载的jdbc.properties在子容器中无法通过Value直接注入。如果需要共享可以在父容器中配置PropertySourcesPlaceholderConfigurer子容器通过引用父容器的 Bean 间接使用。2.5 现代 Spring Boot 环境下的变化在 Spring Boot 中默认只有一个容器基于SpringApplication.run创建。传统的父子容器分离不再必须但如果你手动创建SpringApplicationBuilder仍然可以实现父子容器。Spring Boot 更推荐单一容器 明确的分层包结构。三、总结知识点关键结论SpringMVC 异常处理按顺序遍历HandlerExceptionResolver优先使用ExceptionHandler其次是ResponseStatus最后处理标准异常。建议使用ControllerAdvice统一管理。父子容器父容器Spring不能访问子容器SpringMVC的 Bean子容器可以访问父容器的 Bean。属性文件相互隔离。传统 SSM 项目必须正确配置扫描包避免重复加载。理解这两个机制可以让你在排查 Web 应用异常、处理依赖注入失败、设计分层架构时更加游刃有余。如果你正在使用 Spring Boot绝大多数默认配置已经替你做好了但掌握原理仍然有助于应对复杂场景下的定制需求。 参考资料Spring Framework 官方文档HandlerExceptionResolverSpring MVC 官方文档Context Hierarchy

更多文章