全局异常处理的原理

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

相关推荐
步行cgn6 分钟前
OCP开闭原则:面向对象设计的核心原则
java·开发语言·开闭原则
曹牧8 分钟前
C#:模态对话框
开发语言·c#
落木萧萧82515 分钟前
MyBatis 启动的时候都在干什么:从 MappedStatement 说起
java·数据库·后端
en.en..16 分钟前
Linux wait()函数(预防僵尸进程)
java·大数据·开发语言
扬大平仔30 分钟前
小深:用 AgentScope Java 2.0 Harness 做私人助手(上)
java·开发语言
captain37637 分钟前
网络编程(1)
java·网络·ide·java-ee
yume_sibai38 分钟前
正则表达式完全指南(基础语法 + 实战应用 + 性能优化 + 最佳实践)
开发语言·正则表达式·c#
AbandonForce40 分钟前
简谈线程池
开发语言·c++·算法
李少兄40 分钟前
JavaScript 运算符完全指南
java·开发语言·javascript
广州灵眸科技有限公司42 分钟前
瑞芯微(EASY EAI)RV1126B display
开发语言·数据库·人工智能·科技·嵌入式硬件