96SEO 2026-02-23 12:16 12
MVC执行流程二、SpringBoot源码跟踪三、自定义优雅的全局异常处理脚手架starter自定义异常国际化引入封装基础异常封装基础异常扫描器并注册到ExceptionHandler中项目分享以及改进点

今天这里叙述的全局异常处理是SpringBoot在Servlet场景下的处理机制重点是Servlet模式当然WEBFLUX今天不做过多描述SpringBoot2.2.x以后引入的一种响应式web开发在SpringBoot启动类中可以看到
SpringApplication(ResourceLoader
WebApplicationType.deduceFromClasspath();deduceFromClasspath方法
既然是SringBoot的webServlet场景自然不可以放过的就是DispatchServlet一整个执行流程那就从面试书籍中cp一张
这里SpringBoot究竟如何设计了异常处理呢走进源码探索真相
说到DispatchServlet的请求处理那就直接找到核心方法doDispatch(HttpServletRequest
点进源码不难发现寻找Handler和执行Handler这整整一大块用了复合try–catch进行包裹
checkMultipart(request);multipartRequestParsed
getHandler(processedRequest);if
{noHandlerFound(processedRequest,
response);return;}HandlerAdapter
getHandlerAdapter(mappedHandler.getHandler());......mv
mappedHandler.getHandler());......applyDefaultViewName(processedRequest,
mv);mappedHandler.applyPostHandle(processedRequest,
err);}processDispatchResult(processedRequest,
dispatchException);在try的结束处我们可以看到小异常到大异常源码中并没有打印堆栈而是封装成dispatchException
最后交给processDispatchResult方法去处理请求分发的结果
而processDispatchResult方法内部则是对异常进行了解析也叫resolveException
{logger.debug(ModelAndViewDefiningException
((ModelAndViewDefiningException)
exception).getModelAndView();}else
processHandlerException(request,
null);}}再次进入非视图异常的处理方法processHandlerException中我们看到了多个异常处理器去循环处理异常直到循环结束如果返回值不为NULL说明该异常能够被解析并且处理完毕返回ModelAndView
(this.handlerExceptionResolvers
this.handlerExceptionResolvers)
resolver.resolveException(request,
{break;}}}DispatchServlet类初始化时从容器中获取handlerExceptionResolvers
该类的接口表示Spring容器中处理异常的处理器类根据debug可以看到Spring容器中含有两个解析器类一个是默认的兜底的异常解析器类另一个是HandlerExceptionResolverComposite内部维护着spring容器的异常解析器列表
那么HandlerExceptionResolverComposite处理器类是从哪里来的接着我们跳转到WebMvcConfigurationSupport类,观察其诞生之地
handlerExceptionResolver(Qualifier(mvcContentNegotiationManager)
ArrayList();configureHandlerExceptionResolvers(exceptionResolvers);if
{addDefaultHandlerExceptionResolvers(exceptionResolvers,
contentNegotiationManager);}extendHandlerExceptionResolvers(exceptionResolvers);HandlerExceptionResolverComposite
HandlerExceptionResolverComposite();composite.setOrder(0);composite.setExceptionResolvers(exceptionResolvers);return
这里有两个地方需要关注首先是addDefaultHandlerExceptionResolvers就是spring会默认添加三个异常解析器一个是ExceptionHandlerExceptionResolver这个处理的是程序中注解了ExceptionHandler的第二个DefaultHandlerExceptionResolver这个是处理一些通常的异常具体可查看官方文档。
第三个是较少用的ResponseStatusExceptionResolver
另一个要关注的是extendHandlerExceptionResolvers方法这个是留给子类重写扩展使用的。
此时我们大概知道HandlerExceptionResolverComposite类的resolveException方法可以解析异常那么我们打个断点放行程序到此处再观察
那么一切就变的似乎很合理了HandlerExceptionResolverComposite内部维护着异常解析器列表循环去解析解析成功就返回并且还看到了列表清单的第一个解析器就是ExceptionHandlerExceptionResolver于是到ExceptionHandlerExceptionResolver类中打上断点观察
doResolveHandlerMethodException(HttpServletRequest
getExceptionHandlerMethod(handlerMethod,
{exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}if
{exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}ServletWebRequest
response);ModelAndViewContainer
ModelAndViewContainer();ArrayListThrowable
{exceptions.add(exToExpose);Throwable
exToExpose.getCause();exToExpose
1];exceptions.toArray(arguments);
ArrayListarguments[arguments.length
handlerMethod;exceptionHandlerMethod.invokeAndHandle(webRequest,
(!exceptions.contains(invocationEx)
(mavContainer.isRequestHandled())
exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);顾名思义设置了返回值的处理器
那我们看看ExceptionHandlerExceptionResolver初始化经历了些啥首先实现了InitializingBean那么就直接先看afterProperties方法Bean生命周期执行的钩子函数
initExceptionHandlerAdviceCache方法
言简意赅就是获取所有标有ControllerAdvice注解的类并封装成ControllerAdviceBean随后又去根据这些类创建ExceptionHandlerMethodResolver类点击进去ExceptionHandlerMethodResolver的构造函数
现在真相几乎大告于天下这里先引入SpringBoot的异常处理机制ControllerAdviceExceptionHandler用起来很简单在处理类上添加ControllerAdvice注解、在类中方法上添加ExceptionHandler注解并标注捕获的类那么SpringBoot整个webServlet执行过程中产生的异常都会被这个异常捕获并且返回对应方法的返回值
所以我们后续处理无非就是从mapCache中寻找异常对应的方法因为addMapping方法已经将异常全部封装成exception-Method的map集合形式再一层层返回给dispatchServlet。
上述的源码跟踪下来ControllerAdviceExceptionHandler模式是不是有一些鸡肋完全可以定义一个全局的ExceptionHandler类内部封装自定义异常再配合EnableAutoConfiguration达到脚手架starter封装的效果这里我大概叙述一下思路
定义一个类实现RuntimeException类同时考虑到国际化的问题这里加入了枚举类BaseError并且框架常见异常和业务异常进行分类注册
枚举异常基类默认实现I18n接口并返回resources文件中定义异常文件的文件名
[外链图片转存失败,源站可能有防盗在这里插入!链机制,建描述](https://img-blog议将存csdnimg下cn/d84acd6585a248f29c1d52d1084bbfdf.png
既然作为脚手架使用那么系统中常见的异常我们可以封装一下了我们封装到国际化的Bundle中
这里用了reflections.getSubTypesOf方法返回类路径基础异常SysBaseEnum类及其子类实现封装成集合遍历并抽取其中的枚举类最终枚举集合将注册到exceptionHandler方法中进行捕获
Retention(RetentionPolicy.RUNTIME)
{}这里模仿ExceptionHandlerResolver去继承它
SangExceptionHandlerExceptionResolver
ExceptionHandlerExceptionResolver
InitializingBean核心代码封装自定义注解SangExceptionAdvice成为SangExceptionAdviceBean并重写doResolveHandlerMethodException方法
SangExceptionAdviceBean类封装时可以根据设定加入Predicate断言器配合ConfigurationProperties实现路径匹配捕获异常、全路径异常捕获等等功能改进点reflections.getSubTypesOf方法反射获取异常基类时有些许不合理后期慢慢调整也欢迎大家指教
代码半成品框架开源地址gitee地址欢迎大家fork多沟通一起学习一起进步
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback