在使用基于注解的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 返回数据格式采用统一的对象(JSONReturn)进行封装 09. 通过自定义处理器 ExceptionIntercept 实现 Spring mvc的全局异常捕获 10. 系统中包含了企业中采用的开发工具类的集合 11. AbstractDao 父类...
Spring MVC教程 教程学习: 本教程完整地讲解了Spring MVC的每个知识点,包括独立运行的Demo示例,欢迎一起交流学习。...Spring MVC请求参数封装 Spring MVC 基本类型封装 Spring MVC Post中文乱码 @RequestPara
一个简单的基于Spring MVC的简单登陆的例子,在这个例子中,把表单提交的用户名和密码封装到实体类中传递到controller中,项目代码使用注解模式。表单中的用户名和密码完全相同才能登陆成功。
mybatis未进行二次封装,原滋原味,简单sql采用注解,复杂sql采用Mapper.xml配置 使用了layui的弹出层、菜单、文件上传、富文本编辑、日历、选项卡、数据表格等 表单数据采用bootstrapValidator校验,简单快捷方便...
(1) 创建一个Order类来封装上述订单信息,其中各个属性的名称和数据类型是:产品编号(ProductID,Integer),用户编号(UserID,Integer),交易日期(TransactionDate,Date),价格(Price,Double),数量(Quantity,...
采用springMVC+mybatis+mysql,使用myeclipse10开发,注解,无封装,方便二次开发,初学者也能读懂的代码,每个类都有对应的注释,功能完善
eclipse 直接导出的项目 Spring MVC 的一个DEMO jiaolongzhi作品 采用 Spring 3.0 MVC 框架 JSTL+EL 语句输出界面 ...一种是 Spring 封装的JDBC和 操作 (teacher表) 一种是 proxool 提供的方式(student表)
第8章:介绍了Spring所提供的DAO封装层,这包括Spring DAO的异常体系、数据访问模板等内容。 第9章:介绍了Spring事务管理的工作机制,通过XML、注解等方式进行事务管理配置,同时还讲解了JTA事务配置知识。 ...
本文将详细介绍在Spring Boot框架中如何...Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka和Consul等服务发现组件配合使用,以实现动态服务路由和负载均衡。
struts2将请求在Action中封装为Map并将配置文件放到web-info中还可以自定义配置文件位置就是不将struts.xml放到src下但还是不如spring mvc 的封装来得方便
第8章:介绍了Spring所提供的DAO封装层,这包括Spring DAO的异常体系、数据访问模板等内容。 第9章:介绍了Spring事务管理的工作机制,通过XML、注解等方式进行事务管理配置,同时还讲解了JTA事务配置知识。 ...
MyBatis 是一个优秀的基于 java 的持久层框架, 内部封装了 jdbc,开发者只需要关注 sql 语句 本身, 而不需要处理加载驱动、创建连接、创建 statement 、关闭连接, 资源等繁杂 的过程。 MyBatis 通过 xml 或注解两...
其中注解格式清晰,hibernate进行了代码封装,对开发效率有了提高,对异常进行了封装。freemarker也有优化,参考common包下。对日期工具类有各种情况的代码说明。参考utils下的DateUtils.java类。 slf4j日志文件有...
MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。 ###mybatis MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架。...
Spring MVC属于Spring Framework的后续产品,已经融合在Spring Web Flow里面,它原生支持的Spring特性,让开发变得非常简单规范。Spring MVC 分离了控制器、模型对象、分派器以及处理程序对象的角色,这种分离让...
jwx是开源的java公众号开发MVC框架,基于spring配置文件和微信消息或事件注解,通过微信上下文处理一个或多个微信公众号服务请求。目的主要有两个,其一生封装微信请求xml消息为java实体对象,将返回对象转换为xml...
mybatis实战教程mybatis in action之六与Spring MVC 的集成 mybatis实战教程mybatis in action之七实现mybatis分页源码下载 mybatis实战教程mybatis in action之八mybatis 动态sql语句 mybatis实战教程mybatis in ...
在spring mvc中如何实现 前后端数据的互通是通过Ajax+Json技术实现的。springmvc框架封装了原生mvc,视图层向业务层传递数据时需经过控制层。 4、单线程和多线程的区别 多线程即同时存在多个线程运行,比只有一个...
spbt-myb-plus是一款基于SpringBoot+MyBatis-Plus的快速开发脚手架,拥有完整的权限管理功能,可对接Vue前端,开箱即用。 项目演示 技术选型 技术 版本 说明 SpringBoot 2.7.5 容器+MVC框架 sa-token 1.32.0 认证和...