`

基于注解的spring mvc 异常封装

阅读更多

在使用基于注解的spring mvc时,使用情况如下:

 

@Controller
@RequestMapping("/test")
public class TestController {

    @RequestMapping(value="/index/{userId}", params="username")
    public ModelAndView index(@PathVariable("userId") String userId,
                        @RequestParam("password") String password,
                        @RequestParam("id") int id, Map<String, Object> map) throws Exception {

        System.out.println(userId);
        System.out.println(password);
        System.out.println(id);
        JSONObject obj = new JSONObject();
        obj.put("data", "");
        obj.put("status", "");
        return new ModelAndView("index");
    }

}

 出现异常会有一下几种情况:

 

 1 参数不匹配, spring mvc有自己的异常处理,会调到一个也没,HttpStatus 400

 2 用户在调用的service中,自己抛出异常或者代码异常,这个最好统一捕捉,不要在每个Controller方法里进行捕捉

 

  看spring mvc的源码,可以知道spring mvc处理步骤如下:

  1 spring mvc的入口为:

 

    <servlet>
        <servlet-name>spring-mvc-servlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:/spring-mvc-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>spring-mvc-servlet</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>

 2 DispatcherServlet 是一个Servlet,DispatcherServlet首先处理请求时会调用doService方法:

 

 

	@Override
	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		if (logger.isDebugEnabled()) {
			String requestUri = urlPathHelper.getRequestUri(request);
			String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
			logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
					" processing " + request.getMethod() + " request for [" + requestUri + "]");
		}

		// Keep a snapshot of the request attributes in case of an include,
		// to be able to restore the original attributes after the include.
		Map<String, Object> attributesSnapshot = null;
		if (WebUtils.isIncludeRequest(request)) {
			logger.debug("Taking snapshot of request attributes before include");
			attributesSnapshot = new HashMap<String, Object>();
			Enumeration<?> attrNames = request.getAttributeNames();
			while (attrNames.hasMoreElements()) {
				String attrName = (String) attrNames.nextElement();
				if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
					attributesSnapshot.put(attrName, request.getAttribute(attrName));
				}
			}
		}

		// Make framework objects available to handlers and view objects.
		request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
		request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
		request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
		request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

		FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
		if (inputFlashMap != null) {
			request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
		}
		request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
		request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

		try {
                        //上面做了一系列的操作是想获取必要的资源,在这里分发了请求
			doDispatch(request, response);
		}
		finally {
			if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
				return;
			}
			// Restore the original attribute snapshot, in case of an include.
			if (attributesSnapshot != null) {
				restoreAttributesAfterInclude(request, attributesSnapshot);
			}
		}
	}

  doDispatch方法代码如下:

 

 

	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = processedRequest != request;

				// Determine handler for the current request.
				mappedHandler = getHandler(processedRequest, false);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (logger.isDebugEnabled()) {
						String requestUri = urlPathHelper.getRequestUri(request);
						logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
					}
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				try {
					// Actually invoke the handler.在此处真实的调用了我们的Controller
					mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
				}
				finally {
					if (asyncManager.isConcurrentHandlingStarted()) {
						return;
					}
				}

				applyDefaultViewName(request, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {//当出现错误的时候,捕捉到请求在下面的processDispatchResult处理异常信息
				dispatchException = ex;
			}
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);//处理异常信息
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Error err) {
			triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				return;
			}
			// Clean up any resources used by a multipart request.
			if (multipartRequestParsed) {
				cleanupMultipart(processedRequest);
			}
		}
	}

   processDispatchResult方法的代码为:

 

 

	private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {

		boolean errorView = false;

		if (exception != null) {
			if (exception instanceof ModelAndViewDefiningException) {
				logger.debug("ModelAndViewDefiningException encountered", exception);
				mv = ((ModelAndViewDefiningException) exception).getModelAndView();
			}
			else {//此处处理真正的异常
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
				mv = processHandlerException(request, response, handler, exception);
				errorView = (mv != null);
			}
		}

		// Did the handler return a view to render?
		if (mv != null && !mv.wasCleared()) {
			render(mv, request, response);
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		}
		else {
			if (logger.isDebugEnabled()) {
				logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
						"': assuming HandlerAdapter completed request handling");
			}
		}

		if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
			// Concurrent handling started during a forward
			return;
		}

		if (mappedHandler != null) {
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}


   processHandlerException方法如下,这里会选取HandlerExceptionResolver来处理对应的异常

 

	protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
			Object handler, Exception ex) throws Exception {

		// Check registered HandlerExceptionResolvers...
		ModelAndView exMv = null;
                //遍历HandlerExceptionResolver,获取合适来处理异常
		for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
			exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
			if (exMv != null) {
				break;
			}
		}
		if (exMv != null) {
			if (exMv.isEmpty()) {
				return null;
			}
			// We might still need view name translation for a plain error model...
			if (!exMv.hasView()) {
				exMv.setViewName(getDefaultViewName(request));
			}
			if (logger.isDebugEnabled()) {
				logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
			}
			WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
			return exMv;
		}

		throw ex;
	}

    spring mvc 在Dispatcher里容器onRefresh()方法的时候加载了HandlerExceptionResolver:

	@Override
	protected void onRefresh(ApplicationContext context) {
		initStrategies(context);
	}

	protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
		initHandlerMappings(context);
		initHandlerAdapters(context);
                //初始化HandlerExceptionResolvers
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);
		initFlashMapManager(context);
	}

	private void initHandlerExceptionResolvers(ApplicationContext context) {
		this.handlerExceptionResolvers = null;

		if (this.detectAllHandlerExceptionResolvers) {
			// Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
                        //从spring mvc的容器中获取类型为HandlerExceptionResolver的bean
			Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
					.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.handlerExceptionResolvers = new ArrayList<HandlerExceptionResolver>(matchingBeans.values());
				// We keep HandlerExceptionResolvers in sorted order.
				OrderComparator.sort(this.handlerExceptionResolvers);
			}
		}
		else {
			try {
				HandlerExceptionResolver her =
						context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
				this.handlerExceptionResolvers = Collections.singletonList(her);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, no HandlerExceptionResolver is fine too.
			}
		}

		// Ensure we have at least some HandlerExceptionResolvers, by registering
		// default HandlerExceptionResolvers if no other resolvers are found.
		if (this.handlerExceptionResolvers == null) {
			this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
			if (logger.isDebugEnabled()) {
				logger.debug("No HandlerExceptionResolvers found in servlet '" + getServletName() + "': using default");
			}
		}
	}

   由上面可知:spring mvc 从容器中获取了处理异常的HandlerExceptionResolver,spring mvc提供三种异常处理器:

DefaultHandlerExceptionResolver, ResponseStatusExceptionResolver, ExceptionHandlerExceptionResolver,用户可以在spring mvc的配置文件中配置自己的异常处理类:

    <bean id="diyExceptionHandler" class="com.malone.handler.MyExceptionHandler"/>

    <bean id="myDefaultHandlerExceptionResolver" class="com.malone.handler.MyDefaultHandlerExceptionResolver"/>

  为了统一异常处理,我们可以自定义一个HandlerExceptionResolver,所有的异常均由自定义的异常处理, 由此我们可以重写DispatcherServlet类的

processHandlerException()方法:

public class MyDispatcherServlet extends DispatcherServlet {

    private Map<String, HandlerExceptionResolver> matchingBeans = new HashMap<String, HandlerExceptionResolver>();

    private Map<String, HandlerExceptionResolver> getMatchingBeans () {
        if (matchingBeans.isEmpty()) {
            System.out.println(SpringUtils.getContext());
            synchronized (this) {
                matchingBeans = BeanFactoryUtils
                        .beansOfTypeIncludingAncestors(SpringUtils.getContext(), HandlerExceptionResolver.class, true, false);
            }
        }
        return matchingBeans;
    }

    @Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
        //把spring mvc容器缓存起来
        SpringUtils.setContext(context);
    }

    @Override
    protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
                                                   Object handler, Exception ex) throws Exception {
        //获取HandlerExceptionResolver
        Map<String, HandlerExceptionResolver> matchingBeans = this.getMatchingBeans();
        if (matchingBeans == null || matchingBeans.size() == 0) {//没有配置处理异常的Controller
            return new ModelAndView();
        }
        Map<String, HandlerExceptionResolver> newMatchingBeans = new HashMap<String, HandlerExceptionResolver>();
        for (Iterator<String> iterator = matchingBeans.keySet().iterator(); iterator.hasNext();) {//使用指定handlerResolver
            String key = iterator.next();
            if (Objects.equals(key, "diyExceptionHandler") || Objects.equals(key, "myDefaultHandlerExceptionResolver")) {
                newMatchingBeans.put(key, matchingBeans.get(key));
            }
        }
        // Check registered HandlerExceptionResolvers...
        ModelAndView exMv = null;
        for (HandlerExceptionResolver handlerExceptionResolver : new ArrayList<HandlerExceptionResolver>(newMatchingBeans.values())) {
            exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
            if (exMv != null) {
                break;
            }
        }
        if (exMv != null) {
            if (exMv.isEmpty()) {
                return null;
            }
            // We might still need view name translation for a plain error model...
            if (!exMv.hasView()) {
                exMv.setViewName(getDefaultViewName(request));
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
            }
            WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
            return exMv;
        }

        throw ex;
    }

}

   自定义了MyDispatcherServlet类,此类继承了DispatcherServlet,重写了一下方法:

  onRefresh():重写此方法的目的是在原来的代码基础上增加缓存spring mvc容器的方法

  processHandlerException ():重写此方法是为了修改获取HandlerExceptionResolver的逻辑,由上面代码可知,我们使用onRefresh()方法中缓存的spring mvc容器,

获取到自己注册的HandlerExceptionResolver,然后使用自己的注册HandlerExceptionResolver来处理所有异常

 

附完整的测试代码:

分享到:
评论

相关推荐

    基于注解的Spring mvc增删改DEMO

    Spring MVC提供异常处理注解,如@ControllerAdvice和@ExceptionHandler,以及数据校验注解,如@NotNull、@Size等。 总结,基于注解的Spring MVC简化了Web应用的开发,通过注解可以快速地定义路由、数据绑定、事务...

    学习Spring MVC,关于注解的Spring MVC,简单例子,关于控制器的Spring MVC,学习Spring,简单Spring MVC实例

    Spring MVC 是一个基于Java的轻量级Web应用框架,它为构建RESTful应用程序提供了强大的支持。在本教程中,我们将深入探讨Spring MVC的核心概念,特别是关于注解的使用以及简单的控制器实现。 首先,Spring MVC的...

    Spring@mvc.rar_java 注解_spring mvc

    `ModelAndView` 是Spring MVC中用于封装视图和模型数据的对象,而在现代Spring MVC应用中,更常使用`Model`接口来添加模型数据,然后返回视图名称来决定展示哪个视图。 9. **视图解析器(View Resolver)** 视图...

    Spring mvc5.0.3 所有jar包

    4. **ModelAndView对象**: 这是Spring MVC中用于封装模型数据和视图名的对象。在Controller方法中,可以返回一个ModelAndView对象,指定返回的视图和传递给视图的数据。 5. **视图解析**: Spring MVC支持多种视图...

    spring-MVC.zip_Java spring mvc_spring mvc_spring mvc

    Spring MVC 是一款基于Java的轻量级Web应用框架,它是Spring框架的重要组成部分,主要用于构建Web应用程序的后端控制器。在本实例中,我们有一个名为"spring-MVC.zip"的压缩包,里面包含了一个关于Spring MVC的开发...

    spring mvc 整合包

    这个"spring mvc 整合包"可能包含了Spring MVC的jar文件、配置示例、启动脚本或者示例代码,帮助开发者快速启动基于Spring MVC的Web项目。使用时,开发者需要根据自己的项目需求,进行相应的配置调整,比如数据库...

    Spring MVC 基础实例源码01

    Spring MVC 是一个基于Java的轻量级Web应用框架,它是Spring框架的重要组成部分,主要用于构建Web应用程序的后端控制器。这个"Spring MVC 基础实例源码01"的资源很可能是为了帮助初学者理解Spring MVC的核心概念和...

    spring mvc+mybatis封装

    - 在Spring MVC的Controller中通过@Autowired注解注入Service,然后调用Service中的方法执行数据库操作。 4. **封装**:封装主要体现在以下几个方面: - **Service层**:创建Service接口,包含业务逻辑方法,这些...

    Spring+Spring mvc+Hibernate+Bootstrap、企业级员工信息管理系统

    Spring mvc 返回数据格式采用统一的对象(JSONReturn)进行封装 09. 通过自定义处理器 ExceptionIntercept 实现 Spring mvc的全局异常捕获 10. 系统中包含了企业中采用的开发工具类的集合 11. AbstractDao 父类...

    spring mvc架包

    8. **异常处理**: Spring MVC 提供了 `@ExceptionHandler` 注解,允许在 Controller 中直接处理异常,提高异常处理的灵活性。 9. **JSON 支持**: 通过集成 Jackson 或者 Gson,Spring MVC 可以方便地进行 JSON 数据...

    Spring MVC 使用注解的示例讲解

    Spring MVC 是一款强大的Java Web开发框架,由Spring Software Foundation维护,它简化了构建基于模型-视图-控制器(MVC)架构的Web应用程序的过程。在本示例中,我们将深入探讨如何利用注解来增强Spring MVC的功能...

    Spring MVC学习框架

    Spring MVC 是一个基于 Java 的轻量级 Web 开发框架,它是 Spring 框架的重要组成部分。Spring 框架以其依赖注入(Dependency Injection, DI)和面向切面编程(Aspect-Oriented Programming, AOP)为核心,而 Spring...

    spring mvc经典入门案例

    Spring MVC 是一款基于 Java 的轻量级 Web 开发框架,它是 Spring 框架的重要组成部分。这个经典入门案例将引导你逐步了解并掌握 Spring MVC 的基本概念、配置以及实际应用。 1. **Spring MVC 概述** Spring MVC ...

    Spring MVC 第一个例子

    Spring MVC 是 Spring 框架的一个重要模块,它为构建基于Java的Web应用程序提供了一个强大的模型-视图-控制器(MVC)架构。Spring MVC 的设计允许开发者将业务逻辑、数据处理和用户界面分离,从而实现更好的可维护性...

    SixDay-Spring MVC(基于Spring MVC实现后台登陆系统验证)的源代码

    在本文中,我们将深入探讨一个基于Spring MVC实现的后台登录系统验证的源代码。Spring MVC是Java Web开发中的一个强大框架,它提供了模型-视图-控制器(MVC)架构模式,帮助开发者构建可维护、可扩展的Web应用。我们...

    基于Spring MVC+Spring+Mybatis+Mysql 客户关系管理系统 SSM毕业设计

    SSM框架是Java Web开发中常用的一种组合,由Spring MVC、Spring和Mybatis三个组件构成,用于构建高效、灵活的Web应用。这个基于SSM的客户关系管理系统(CRM)毕业设计,利用了Maven进行项目构建,确保了依赖管理的便捷...

    Spring Mvc实例

    Spring MVC 是一个基于Java的轻量级Web应用框架,它是Spring框架的重要组成部分,主要用于构建Web应用程序的后端控制器。在本实例中,我们将探讨如何利用Spring MVC的注解来简化前后台交互,以及如何实现简单视图...

    Java Web 案例实践,基于(spring,spring mvc,hibernate)框架实现的

    这些模块的实现将展示如何将业务逻辑封装在Service层,然后通过Spring MVC的Controller暴露为HTTP端点。同时,你还将学习到如何利用Hibernate进行数据持久化,包括增删改查操作。 为了更好地理解和学习这个案例,你...

Global site tag (gtag.js) - Google Analytics