全局异常处理的原理

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注解的对于不同类型异常的处理方法也算已知了

相关推荐
平常心的技术小牛4 小时前
Qt-快速上手-QMenuBar
开发语言·qt
青 春 记 忆5 小时前
零基础入门Python11|Git实战:为任务管理器建立版本历史
开发语言·git·vscode·python·python3.11
2601_963870205 小时前
【计算机毕业设计】基于Spring Boot的专科医院医疗管理系统
java·spring boot·课程设计
Bingo_BIG5 小时前
Java Spring 批量修改,实体、接口、方法的定义
java·spring
画中有画6 小时前
软件架构中质量属性(性能、安全、可扩展性)的权衡设计
java·运维·安全
Shaoxi Zhang6 小时前
JAVA学习笔记035——对象和JSON格式
java·笔记·学习
赵丙双6 小时前
CountDownLatch 源码分析
java·aqs·countdownlatch
余额瞒着我当琳6 小时前
C++--深拷贝三件套 + swap + 写时拷贝 + vector 扩容 + reserve
java·开发语言·c++
thefool1122667 小时前
翻转二叉树
java