SpringMVC框架学习---SpringMVC之源码讲解

SpringMVC框架学习—SpringMVC之源码讲解

1.前端控制器DispatcherServlet的继承结构

在这里插入图片描述

  1. HttpServlet中含有doGet/doPost方法但是并没有做什么处理操作
  2. HttpServletBean中没有doGet/doPost方法
  3. 真正执行doGet/doPost方法的是在FrameworkServlet中执行的,他重写了HttpServlet中的doGet/doPost方法
  4. FrameworkServlet中的doPost方法中包含方法processRequest(request, response),这里面包含了抽象方法doService(request, response),这也是抽象方法
  5. 点击抽象方法doService(request, response)会找到他的实现类方法,在DispatcherServlet中重写doService方法,查看方法会发现处理请求的核心方法doDispatch,这个方法类似spring框架中的refresh方法

2.SpringMVC处理请求的大致流程

  1. 调⽤getHandler()获取到能够处理当前请求的执⾏链 HandlerExecutionChain(Handler+拦截器)
  2. 调⽤getHandlerAdapter();获取能够执⾏流程1中Handler的适配器,但是如何去getHandlerAdapter的?
  3. 适配器调⽤Handler执⾏ha.handle(总会返回⼀个ModelAndView对象)
  4. 调⽤processDispatchResult()⽅法完成视图渲染跳转

3.核⼼步骤getHandler⽅法剖析

  1. 取得处理当前请求的Controller,这里也称为Handler,即处理器。这里并不是直接返回Controller,而是返回HandlerExecutionChain请求处理链对象该对象封装了HandlerInteceptor

  2. 进入getHandler方法中发现,方法中在遍历handlerMapping(处理器映射器:负责解析请求路径找到对应的Handler,所以里面存储的)

    @Nullable
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    		if (this.handlerMappings != null) {
    			for (HandlerMapping mapping : this.handlerMappings) {
    				HandlerExecutionChain handler = mapping.getHandler(request);
    				if (handler != null) {
    					return handler;
    				}
    			}
    		}
    		return null;
    	}
    
  3. handlerMapping有两个,SpringMVC在容器启动,初始化容器的时候将0和1都初始化了

    1. 0是Spring在早起版本定义很多配置信息的时候使用的
    2. 1是使用注解开发的时候使用的
      在这里插入图片描述
  4. 遍历到第二个handlerMapping时,会发现有东西出现了,出现的是HandlerExecutionChain(处理器执行链),里面指向那个Controller的哪个方法和intercepter(拦截器)
    在这里插入图片描述

  5. 最后将HandlerExecutionChain对象返回,之后再看方法的请求参数HttpServletRequest request,里面有个属性requestDispatcherPath 存储的内容是 /demo/handle01 这个是请求的url路径,通过这个路径进行筛选找到方法

  6. handlerMapping里面的映射关系是什么时候创建的?

  7. Spring容器启动的时候,ioc容器在扫描controller对象的时候会扫描RequestMapping注解,扫描RequestMapping注解的时候就会建立urlhandler方法之间的映射

4.getHandlerAdapter适配器获取分析

  1. 获取处理请求的处理器适配器 HandlerAdapter
  2. 进入方法发现在遍历handlerAdapters,里面包含很多HandlerAdapter类对象,其中handlerAdapters有三个,分别是下图中的0,1,2
    在这里插入图片描述
  3. adapter.supports(handler)通过这行代码判断HandlerAdapter类对象(包含三种)是否支持当前的handler对象

5. SpringMVC九⼤组件初始化

  1. 九⼤组件都是定义了接⼝,接⼝其实就是定义了该组件的规范,⽐如ViewResolverHandlerAdapter等都是接⼝
    /** MultipartResolver used by this servlet. */
    // 多部件解析器
    @Nullable
    private MultipartResolver multipartResolver;
    /** LocaleResolver used by this servlet. */
    // 区域化 国际化解析器
    @Nullable
    private LocaleResolver localeResolver;
    /** ThemeResolver used by this servlet. */
    // 主题解析器
    @Nullable
    private ThemeResolver themeResolver;
    /** List of HandlerMappings used by this servlet. */
    // 处理器映射器组件
    @Nullable
    private List<HandlerMapping> handlerMappings;
    /** List of HandlerAdapters used by this servlet. */
    // 处理器适配器组件
    @Nullable
    private List<HandlerAdapter> handlerAdapters;
    /** List of HandlerExceptionResolvers used by this servlet. */
    // 异常解析器组件
    @Nullable
    private List<HandlerExceptionResolver> handlerExceptionResolvers;
    /** RequestToViewNameTranslator used by this servlet. */
    // 默认视图名转换器组件
    @Nullable
    private RequestToViewNameTranslator viewNameTranslator;
    /** FlashMapManager used by this servlet. */
    // flash属性管理组件
    @Nullable
    private FlashMapManager flashMapManager;
    /** List of ViewResolvers used by this servlet. */
    // 视图解析器
    @Nullable
    private List<ViewResolver> viewResolvers;
    

6. SpringMVC九⼤组件初始化细节

  1. DispatcherServlet当中有个方法onRefresh(主要是完成组件初始化的),它调用了initStrategies方法,是初始化策略方法

    protected void initStrategies(ApplicationContext context) {
    	// 多文件上传的组件
    	initMultipartResolver(context);
    	// 初始化本地语言环境
    	initLocaleResolver(context);
    	// 初始化模板处理器
    	initThemeResolver(context);
    	// 初始化HandlerMapping
    	initHandlerMappings(context);
    	// 初始化参数适配器
    	initHandlerAdapters(context);
    	// 初始化异常拦截器
    	initHandlerExceptionResolvers(context);
    	// 初始化视图预处理器
    	initRequestToViewNameTranslator(context);
    	// 初始化视图转换器
    	initViewResolvers(context);
    	// 初始化 FlashMap 管理器
    	initFlashMapManager(context);
    }
    
  2. onRefresh方法是怎样被调用的,调用链是怎样的

    1. DispatcherServletonRefresh方法中打上断点
      在这里插入图片描述
    2. onRefresh方法触发调用机制
      在这里插入图片描述
    3. 这个事件什么时候发布的
      在这里插入图片描述

7. SpringMVC九⼤组件初始化细节—initHandlerMappings

  1. 查看initHandlerMappings方法初始化过程
private void initHandlerMappings(ApplicationContext context) {
		this.handlerMappings = null;
		// detectAllHandlerMappings是DispatcherServlet的默认值=true
		if (this.detectAllHandlerMappings) {
			// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
			// 找到所有的HandlerMapping
			// 整个这部操作是到当前springmvc的容器以及springmvc的父容器中去查找所有实现HandlerMapping接口的类对应的对象,把他们加载出来
			Map<String, HandlerMapping> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.handlerMappings = new ArrayList<>(matchingBeans.values());
				// We keep HandlerMappings in sorted order.
				AnnotationAwareOrderComparator.sort(this.handlerMappings);
			}
		}
		else {
			try {
				// 否则在ioc中按照固定名称去找
				HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
				this.handlerMappings = Collections.singletonList(hm);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, we'll add a default HandlerMapping later.
			}
		}

		// Ensure we have at least one HandlerMapping, by registering
		// a default HandlerMapping if no other mappings are found.
		if (this.handlerMappings == null) {
			// 最后还为空则按照默认策略生成
			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
			if (logger.isTraceEnabled()) {
				logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
						"': using default strategies from DispatcherServlet.properties");
			}
		}
	}
  1. 如果前两种内容都不匹配,最后handlerMappings还为空则按照默认策略生成,进入getDefaultStrategies方法
  2. 以第二个参数作为策略,按照默认策略生成handlerMappings(处理器映射器)
@SuppressWarnings("unchecked")
	protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
		// 第二个参数的名字作为key
		String key = strategyInterface.getName();
		// 通过defaultStrategies对象的getProperty方法获取value,defaultStrategies是通过静态代码块获取的值
		String value = defaultStrategies.getProperty(key);
		if (value != null) {
			String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
			List<T> strategies = new ArrayList<>(classNames.length);
			for (String className : classNames) {
				try {
					Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
					Object strategy = createDefaultStrategy(context, clazz);
					strategies.add((T) strategy);
				}
				catch (ClassNotFoundException ex) {
					throw new BeanInitializationException(
							"Could not find DispatcherServlet's default strategy class [" + className +
							"] for interface [" + key + "]", ex);
				}
				catch (LinkageError err) {
					throw new BeanInitializationException(
							"Unresolvable class definition for DispatcherServlet's default strategy class [" +
							className + "] for interface [" + key + "]", err);
				}
			}
			return strategies;
		}
		else {
			return new LinkedList<>();
		}
	}
  1. defaultStrategies执行过程
static {
		// Load default strategy implementations from properties file.
		// This is currently strictly internal and not meant to be customized
		// by application developers.
		
		// DEFAULT_STRATEGIES_PATH这个值是 DispatcherServlet.properties 这个文件
		// DispatcherServlet.properties 这个文件记录了 springmvc默认加载的组件实现是哪个
		try {
			ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
			defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
		}
		catch (IOException ex) {
			throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
		}
	}

8. SpringMVC核心步骤Handler方法执行细节剖析

  1. doDispatch()方法执行到如下步骤
    在这里插入图片描述
  2. 打断点进入handle方法
    在这里插入图片描述
  3. 进入handleInternal方法
@Override
	protected ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
			
		ModelAndView mav;
		checkRequest(request);

		// Execute invokeHandlerMethod in synchronized block if required.
		// 判断当前是否需要支持在同一个session中只能线性地处理请求
		if (this.synchronizeOnSession) {
			// 获取当前请求的session对象
			HttpSession session = request.getSession(false);
			if (session != null) {
				// 为当前session生成一个唯一的可以用于锁定的key
				Object mutex = WebUtils.getSessionMutex(session);
				synchronized (mutex) {
					// 对HandlerMethod进行参数等的适配处理,并调用目标handler
					mav = invokeHandlerMethod(request, response, handlerMethod);
				}
			}
			else {
				// No HttpSession available -> no mutex necessary
				// 如果当前不存在session,则直接对HandlerMethod进行适配
				mav = invokeHandlerMethod(request, response, handlerMethod);
			}
		}
		else {
			// No synchronization on session demanded at all...
			// 如果当前不需要对session进行同步处理,则直接对HandlerMethod进行适配,进入步骤4
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}

		if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
			if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
				applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
			}
			else {
				prepareResponse(response);
			}
		}

		return mav;
	}
  1. 进入invokeHandlerMethod方法
@Nullable
    // handlerMethod:一个handler对象,一个方法
	protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		try {
			// 获取容器中全局配置的InitBinder和当前HandlerMethod所对应的Controller中配置的InitBinder,用于进行参数的绑定
			WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
			// 获取容器中全局配置的ModelAttribute和当前当前HandlerMethod所对应的Controller中配置的ModelAttribute,这些配置的方法将会在目标方法调用之前进行调用
			ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

			// 将handlerMethod封装为一个ServletInvocableHandlerMethod对象(可调用的方法)
			ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
			if (this.argumentResolvers != null) {
				// 设置当前容器中配置的所有ArgumentResolver,设置参数
				// argumentResolvers:参数解析器
				invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
			}
			if (this.returnValueHandlers != null) {
				// 设置当前容器中配置的所有ReturnValueHandler,设置返回值
				invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
			}
			// 将前面创建的WebDataBinderFactory设置到ServletInvocableHandlerMethod中
			invocableMethod.setDataBinderFactory(binderFactory);

			invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

			ModelAndViewContainer mavContainer = new ModelAndViewContainer();
			mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
			// 这里initModel()方法主要作用是调用前面获取到的@ModelAttribute标注的方法,
			// 从而达到@ModelAttribute标注的方法能够在目标Handler调用之前调用的目的
			modelFactory.initModel(webRequest, mavContainer, invocableMethod);
			mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

			AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
			asyncWebRequest.setTimeout(this.asyncRequestTimeout);

			WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
			asyncManager.setTaskExecutor(this.taskExecutor);
			asyncManager.setAsyncWebRequest(asyncWebRequest);
			asyncManager.registerCallableInterceptors(this.callableInterceptors);
			asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

			if (asyncManager.hasConcurrentResult()) {
				Object result = asyncManager.getConcurrentResult();
				mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
				asyncManager.clearConcurrentResult();
				LogFormatUtils.traceDebug(logger, traceOn -> {
					String formatted = LogFormatUtils.formatValue(result, !traceOn);
					return "Resume with async result [" + formatted + "]";
				});
				invocableMethod = invocableMethod.wrapConcurrentResult(result);
			}

			// 对请求参数进行处理,调用目标HandlerMethod,并且将返回值封装为一个ModelAndView对象
			// webRequest:请求参数
			// 说白了就是反射调用handle方法,反射调用的时候要准备参数,进入步骤5
			invocableMethod.invokeAndHandle(webRequest, mavContainer);
			if (asyncManager.isConcurrentHandlingStarted()) {
				return null;
			}

			// 对封装的ModelAndView进行处理,主要是判断当前请求是否进行了重定向,如果进行了重定向,
			// 还会判断是否需要将FlashAttributes封装到新的请求中
			return getModelAndView(mavContainer, modelFactory, webRequest);
		}
		finally {
			webRequest.requestCompleted();
		}
	}
  1. 进入invokeAndHandle方法
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {

		// 对目标handler的参数进行处理,并且调用目标handler,进入步骤6
		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
		// 设置相关的返回状态
		setResponseStatus(webRequest);
		if (returnValue == null) {
			if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
				disableContentCachingIfNecessary(webRequest);
				mavContainer.setRequestHandled(true);
				return;
			}
		}
		else if (StringUtils.hasText(getResponseStatusReason())) {
			mavContainer.setRequestHandled(true);
			return;
		}

		mavContainer.setRequestHandled(false);
		Assert.state(this.returnValueHandlers != null, "No return value handlers");
		try {
			this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		}
		catch (Exception ex) {
			if (logger.isTraceEnabled()) {
				logger.trace(formatErrorForReturnValue(returnValue), ex);
			}
			throw ex;
		}
	}
  1. 进入invokeForRequest方法
	@Nullable
	public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
		// 将request中的参数转换为当前handler的参数形式
		// 获取方法参数传入的值,过程见步骤7,8,9
		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
		if (logger.isTraceEnabled()) {
			logger.trace("Arguments: " + Arrays.toString(args));
		}
		// 这里doInvoke()方法主要是结合处理后的参数,使用反射对目标方法进行调用,过程见步骤10
		return doInvoke(args);
	}
  1. 进入getMethodArgumentValues方法
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {
		// 获取当前handler所声明的所有参数,主要包括参数名,参数类型,参数位置,所标注的注解等等属性
		MethodParameter[] parameters = getMethodParameters();
		if (ObjectUtils.isEmpty(parameters)) {
			return EMPTY_ARGS;
		}
		
		// 最终参数值存放的数组
		Object[] args = new Object[parameters.length];
		// 遍历所有的参数类型
		for (int i = 0; i < parameters.length; i++) {
			MethodParameter parameter = parameters[i];
			parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
			// providedArgs是调用方提供的参数,这里主要是判断这些参数中是否有当前类型,如果有,则直接使用调用方提供的参数,对于请求处理而言,默认情况下,
			// 调用方提供的参数都是长度为0的数组
			// 貌似有一个赋值的动作,通过方法传入的Object... providedArgs(参数)和遍历出来的parameter参数类型进行匹配,拿到参数,获取参数的值
			args[i] = findProvidedArgument(parameter, providedArgs);
			if (args[i] != null) {
				continue;
			}
			// 如果在调用方提供的参数中不能找到当前类型的参数值,则遍历Spring容器中所有的
			// ArgumentResolver,判断哪种类型的Resolver支持对当前参数的解析,这里的判断
			// 方式比较简单,比如RequestParamMethodArgumentResolver就是判断当前参数
			// 是否使用@RequestParam注解进行了标注
			if (!this.resolvers.supportsParameter(parameter)) {
				throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
			}
			try {
				// 如果能够找到对当前参数进行处理的ArgumentResolver,则调用其
				// resolveArgument()方法从request中获取对应的参数值,并且进行转换
				args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
			}
			catch (Exception ex) {
				// Leave stack trace for later, exception may actually be resolved and handled...
				if (logger.isDebugEnabled()) {
					String exMsg = ex.getMessage();
					if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
						logger.debug(formatArgumentError(parameter, exMsg));
					}
				}
				throw ex;
			}
		}
		return args;
	}
  1. 进入resolveArgument方法
	@Override
	@Nullable
	public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
		// 寿险拿到了一个	HandlerMethodArgumentResolver 的解析器
		HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
		if (resolver == null) {
			throw new IllegalArgumentException("Unsupported parameter type [" +
					parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
		}
		return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
	}
	@Override
	@Nullable
	public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

        // 对我们传入的参数进行封装
		NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
		MethodParameter nestedParameter = parameter.nestedIfOptional();
		
        // 对传入的字符串参数进行处理,可能有占位,所以要处理一下
		Object resolvedName = resolveStringValue(namedValueInfo.name);
		if (resolvedName == null) {
			throw new IllegalArgumentException(
					"Specified name must not resolve to null: [" + namedValueInfo.name + "]");
		}
        // 找一找传入的参数值是什么了,见第9步
		Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
		if (arg == null) {
			if (namedValueInfo.defaultValue != null) {
				arg = resolveStringValue(namedValueInfo.defaultValue);
			}
			else if (namedValueInfo.required && !nestedParameter.isOptional()) {
				handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
			}
			arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
		}
		else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
			arg = resolveStringValue(namedValueInfo.defaultValue);
		}

		if (binderFactory != null) {
			WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
			try {
				arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
			}
			catch (ConversionNotSupportedException ex) {
				throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
						namedValueInfo.name, parameter, ex.getCause());
			}
			catch (TypeMismatchException ex) {
				throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
						namedValueInfo.name, parameter, ex.getCause());

			}
		}

		handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);

		return arg;
	}
  1. 进入resolveName方法
	@Override
	@Nullable
	protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
		HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);

		if (servletRequest != null) {
			Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
			if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
				return mpArg;
			}
		}

		Object arg = null;
		MultipartRequest multipartRequest = request.getNativeRequest(MultipartRequest.class);
		if (multipartRequest != null) {
			List<MultipartFile> files = multipartRequest.getFiles(name);
			if (!files.isEmpty()) {
				arg = (files.size() == 1 ? files.get(0) : files);
			}
		}
		if (arg == null) {
		    // 从请求中拿到参数的值
			String[] paramValues = request.getParameterValues(name);
			if (paramValues != null) {
				arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
			}
		}
		return arg;
	} 
  1. 进入doInvoke方法
	@Nullable
	protected Object doInvoke(Object... args) throws Exception {
		ReflectionUtils.makeAccessible(getBridgedMethod());
		try {
		    // 通过反射调用完成方法的执行,通过getBean()拿到对应的controller
			return getBridgedMethod().invoke(getBean(), args);
		}
		catch (IllegalArgumentException ex) {
			assertTargetBean(getBridgedMethod(), getBean(), args);
			String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
			throw new IllegalStateException(formatInvokeError(text, args), ex);
		}
		catch (InvocationTargetException ex) {
			// Unwrap for HandlerExceptionResolvers ...
			Throwable targetException = ex.getTargetException();
			if (targetException instanceof RuntimeException) {
				throw (RuntimeException) targetException;
			}
			else if (targetException instanceof Error) {
				throw (Error) targetException;
			}
			else if (targetException instanceof Exception) {
				throw (Exception) targetException;
			}
			else {
				throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
			}
		}
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值