全局异常处理的原理

Spring 全局异常处理

首先需要确定的是,并没有用到 动态代理 。

请求被全局异常处理的过程

DispatcherServlet.class

请求进入DispatcherServlet#doDispatch方法中,如果出现参数缺失,异常类型=NoResourceFoundException。

HandlerExceptionResolverComposite.class

异常信息一直传递到HandlerExceptionResolverComposite.class#resolveException中,此时会遍历resolvers进行处理,如下图:

ExceptionHandlerExceptionResolver.class

异常信息传递到ExceptionHandlerExceptionResolver.class#doResolveHandlerMethodException

java 复制代码
// 这里会寻找开发写的@ExceptionHandle(某种异常.class),异常的种类如果是exception的父类或者同样的类算作匹配并返回对应的异常处理方法
ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
...
// 调用开发写的全局异常处理方法
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, arguments);

有关内容在启动时的处理

D:\JavaKFHJ\repository\org\springframework\spring-webmvc\6.1.6\spring-webmvc-6.1.6.jar!\org\springframework\web\servlet\config\annotation\WebMvcConfigurationSupport.class#handlerExceptionResolver注册一个名为"handlerExceptionResolver"的bean,如下:

java 复制代码
@Bean
	public HandlerExceptionResolver handlerExceptionResolver(
			@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) {
		List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();
		configureHandlerExceptionResolvers(exceptionResolvers);
		if (exceptionResolvers.isEmpty()) {
		  // 经过这一步处理后exceptionResolvers的长度变成了3
			addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);
		}
		extendHandlerExceptionResolvers(exceptionResolvers);
		HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
		composite.setOrder(0);
		composite.setExceptionResolvers(exceptionResolvers);
		return composite;
	}

D:\JavaKFHJ\repository\org\springframework\spring-webmvc\6.1.6\spring-webmvc-6.1.6.jar!\org\springframework\web\servlet\config\annotation\WebMvcConfigurationSupport.class#addDefaultHandlerExceptionResolvers,在这个方法的代码中创建了3个HandlerExceptionResolver,如下:

java 复制代码
protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers,
			ContentNegotiationManager mvcContentNegotiationManager) {

		ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver();
		exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager);
		exceptionHandlerResolver.setMessageConverters(getMessageConverters());
		exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers());
		exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers());
		if (jackson2Present) {
			exceptionHandlerResolver.setResponseBodyAdvice(
					Collections.singletonList(new JsonViewResponseBodyAdvice()));
		}
		if (this.applicationContext != null) {
			exceptionHandlerResolver.setApplicationContext(this.applicationContext);
		}
		//从这里进入
		exceptionHandlerResolver.afterPropertiesSet();
		exceptionResolvers.add(exceptionHandlerResolver);

		ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver();
		responseStatusResolver.setMessageSource(this.applicationContext);
		exceptionResolvers.add(responseStatusResolver);

		exceptionResolvers.add(new DefaultHandlerExceptionResolver());
	}

以exceptionHandlerResolver.afterPropertiesSet();作为调用入口,调用D:\JavaKFHJ\repository\org\springframework\spring-webmvc\6.1.6\spring-webmvc-6.1.6.jar!\org\springframework\web\servlet\mvc\method\annotation\ExceptionHandlerExceptionResolver.class#initExceptionHandlerAdviceCache,如下:

java 复制代码
private void initExceptionHandlerAdviceCache() {
		if (getApplicationContext() == null) {
			return;
		}

     // 这里的adviceBeans 会找到包含开发自己创建的全局异常处理器类
		List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
		for (ControllerAdviceBean adviceBean : adviceBeans) {
			Class<?> beanType = adviceBean.getBeanType();
			if (beanType == null) {
				throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
			}
			ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
			if (resolver.hasExceptionMappings()) {
				this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
			}
			if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
				this.responseBodyAdvice.add(adviceBean);
			}
		}

		if (logger.isDebugEnabled()) {
			int handlerSize = this.exceptionHandlerAdviceCache.size();
			int adviceSize = this.responseBodyAdvice.size();
			if (handlerSize == 0 && adviceSize == 0) {
				logger.debug("ControllerAdvice beans: none");
			}
			else {
				logger.debug("ControllerAdvice beans: " +
						handlerSize + " @ExceptionHandler, " + adviceSize + " ResponseBodyAdvice");
			}
		}
	}

D:\JavaKFHJ\repository\org\springframework\spring-web\6.1.6\spring-web-6.1.6.jar!\org\springframework\web\method\ControllerAdviceBean.class#findAnnotatedBeans会找到由@ControllerAdvice标注的类并返回,如下:

java 复制代码
public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext context) {
		ListableBeanFactory beanFactory = context;
		if (context instanceof ConfigurableApplicationContext cac) {
			// Use internal BeanFactory for potential downcast to ConfigurableBeanFactory above
			// 从context中取出beanFactory
			beanFactory = cac.getBeanFactory();
		}
		List<ControllerAdviceBean> adviceBeans = new ArrayList<>();
		// 取出所有bean的名字列表
		for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, Object.class)) {
			if (!ScopedProxyUtils.isScopedTarget(name)) {
			// 如果这个bean上有@ControllerAdvice的注解
				ControllerAdvice controllerAdvice = beanFactory.findAnnotationOnBean(name, ControllerAdvice.class);
				if (controllerAdvice != null) {
					// Use the @ControllerAdvice annotation found by findAnnotationOnBean()
					// in order to avoid a subsequent lookup of the same annotation.
					adviceBeans.add(new ControllerAdviceBean(name, beanFactory, controllerAdvice));
				}
			}
		}
		OrderComparator.sort(adviceBeans);
		return adviceBeans;
	}

找到了开发自定义的全局异常处理器,有了这个类,开发写的由@ExceptionHandler注解的对于不同类型异常的处理方法也算已知了

相关推荐
jimy16 分钟前
polymorph.cpp里面的“析构函数、智能指针”问题
开发语言·c++
m0_380743879 分钟前
PHP 8.3 缓存清除不生效怎么排查
开发语言·php
朝朝辞暮i37 分钟前
C++ 第 6 课:switch —— 多状态选择
开发语言·c++·算法
小凡geo1 小时前
本地商家 GEO:用脚本一键生成 FAQPage 结构化数据,让 AI 切片更稳
开发语言·人工智能·python·microsoft·搜索引擎·ai
朝朝辞暮i1 小时前
C++ 第 4 课:if / else —— 让程序自己做决定
开发语言·c++
波力海苔夹心脆6751 小时前
C# 值类型与引用类型详解:存储位置、赋值机制、参数传递、相等比较、装箱拆箱与常见陷阱
开发语言·jvm·经验分享·笔记·c#·.net
蜗牛互联网1 小时前
WSL Containers GA:本地AI容器的生命周期、网络与治理验收
java·网络·人工智能·后端
m0_380743871 小时前
Qt中导航栏实现的详细指南
开发语言·c++
mhmh1232 小时前
Python 对象模型与数据模型:魔术方法、描述符与元类深度解析
开发语言·python
估值探索者2 小时前
【Python量化策略实战 #02】多因子合成mom和vol两因子打分合成
开发语言·c++·python·数据挖掘·c#