在项目开发中,我们通常通过参数的形式注入Model对象,如:

    @RequestMapping("/demo")
public String demo(Model model) {
model.addAttribute("message", "我是你的message!!!");
// HandlerMethodArgumentResolver
throw new IllegalArgumentException("你就错了!!");
}

直接通过返回String来指定需要返回的View,然后在页面上直接可以访问Model对象的值了。

为了全局统对异常处理,通常我们还有个全局的异常处理中心:

需要实现HandlerExceptionResolver接口,具体配置的话这里就不赘述了。在异常处理中心,统一返回异常消息,这里假设返回的是json信息:

BaseResponse responseBean = new BaseResponse(Configuration.Status.STATUS_FAIL, filterErrorMsg ? defaultErrorMsg : ex.getMessage());
try {
String message = mapper.writeValueAsString(responseBean);
response.reset();
response.setContentType(contentType);
response.getOutputStream().write(message.getBytes());
response.getOutputStream().flush();
} catch (Exception e) {
log.error(e);
}
return new ModelAndView();

如果页面上只需要返回统一的错误信息,那么这个方式非常适合,没毛病。但是如果想在页面上回显消息呢?比如管理员修改用户信息,此时用户也在修改用户信息。管理员提交修改后,用户再提交修改就发生了并发错误,目前我们的系统是采用统一的异常抛出处理。只要抛出ConcurrentException就表示发生了并发错误,需要用户刷新页面重试。

发生错误后,当然要把用户之前的信息回显出来了,要不然用辛苦写了那么多信息被丢弃了,体验多不好。在页面上只要获取Model的信息显示出来就好了。

但是SpringMvc在异常情况下,并没有提供获取Model对象的方法

public interface HandlerExceptionResolver {

    /**
* Try to resolve the given exception that got thrown during handler execution,
* returning a {@link ModelAndView} that represents a specific error page if appropriate.
* <p>The returned {@code ModelAndView} may be {@linkplain ModelAndView#isEmpty() empty}
* to indicate that the exception has been resolved successfully but that no view
* should be rendered, for instance by setting a status code.
* @param request current HTTP request
* @param response current HTTP response
* @param handler the executed handler, or {@code null} if none chosen at the
* time of the exception (for example, if multipart resolution failed)
* @param ex the exception that got thrown during handler execution
* @return a corresponding {@code ModelAndView} to forward to, or {@code null}
* for default processing
*/
ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex); }

因为数据是通过参数对象传入的,所以有handler也拿不到model对象的数据。

对于解决方法

1、AOP肯定是可以的,在Handler调用之前,把Model对象保存下来,当方法调用抛出异常后,将Model信息保存到Request对象上,这样在HandlerExceptionResolver中就可以获取到Model的信息了。

2、通过SpringMvc的HandlerMethodArgumentResolver来解决,当设置参数时,把Model对象保存到Request上,这样在HandlerExceptionResolver也可以移获取到了。

下面来说说第二中实现方式:

首先项目是集成了SpringBoot的,在WebMvcConfigurerAdapter中,有addArgumentResolvers方法

    @Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
// argumentResolvers.add(new MyArgumentsResolver());
// argumentResolvers.add(0, new CacheableModelMethodProcessor());
}

通过此方法可以自定义参数处理,但是仅限于自定义的参数,比如你自己添加了Student类型的参数。从注释中可以得知,对于SpringMvc内置提供的参数是无法覆盖的。

Add resolvers to support custom controller method argument types. 

This does not override the built-in support for resolving handler method arguments. To customize the built-in support for argument resolution, configure RequestMappingHandlerAdapter directly. 

This implementation is empty.

要覆盖默认的参数处理,需要通过RequestMappingHandlerAdapter进行处理。

首先,自定义一个HandlerMethodArgumentResolver,专门对Model对象进行处理:

public class CacheableModelMethodProcessor implements HandlerMethodArgumentResolver {

    public static final String KEY_MODEL = "com.cml.springboot.framework.argument.CacheableModelMethodProcessor.KEY_MODEL";

    @Override
public boolean supportsParameter(MethodParameter parameter) {
return Model.class.isAssignableFrom(parameter.getParameterType());
} @Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
ModelMap map = mavContainer.getModel();
webRequest.setAttribute(KEY_MODEL, map, NativeWebRequest.SCOPE_REQUEST);
return map;
} }

每次处理后,将Model对象保存到Request中。

其次,注册此HandlerMethodArgumentResolver,在Bean初始化完毕后,进行注册:

@Component
public class CustomModelArgumentResolverConfiguration { @Autowired
private RequestMappingHandlerAdapter requestMappingHandlerAdapter; /**
* 覆盖系统默认的处理器
*/
@PostConstruct
public void afterProperties() {
List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>(requestMappingHandlerAdapter.getArgumentResolvers());
argumentResolvers.add(0, new CacheableModelMethodProcessor());
requestMappingHandlerAdapter.setArgumentResolvers(argumentResolvers);
} }

@Configuration
public class CustomModelArgumentResolverConfiguration { @Bean
public RequestMappingHandlerAdapter adapter(RequestMappingHandlerAdapter adapter) {
List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>(adapter.getArgumentResolvers());
argumentResolvers.add(0, new CacheableModelMethodProcessor());
adapter.setArgumentResolvers(argumentResolvers);
return adapter;
}
}

以上都能实现覆盖默认的HandlerMethodArgumentResolver,推荐使用第一种方法。

注入自定义的HandlerMethodArgumentResolver后,只要在Controller中添加Model对象的参数,这样在HandlerExceptionResolver中就可以从Request中获取Model对象了:

request.getAttribute(CacheableModelMethodProcessor.KEY_MODEL)

至于ModelAndView参数,也是同理。

以上集成后就可以在HandlerExceptionResolver中获取到Model,在返回ModelAndView中设置此model,这样在页面上就可以获取到要回显的信息了。

当然可能有疑问了,既然能获取到Request对象,我直接从Request中获取参数不就好了吗?当然,这样是可以的,但是如果在Controller中你有自定义消息呢?比如我就单纯的添加了处理消息:

    @RequestMapping("/demo")
public String demo(Model model) {
model.addAttribute("message", "我是你的message!!!");
// HandlerMethodArgumentResolver
throw new IllegalArgumentException("你就错了!!");
}

这中情况无法从Request中获取到了。

当然,数据不一定要存到Model对象中,可以放到Request,或ThreadLocal中…各种方法都有,只要能实现。都是可以的。这里只是提供了一种觉得简便的方法。

至于在Spring项目中而不是SpringBoot使用,原理是相同的,只要根据步骤实现即可。


SpringBootLean 是对springboot学习与研究项目,是根据实际项目的形式对进行配置与处理,欢迎star与fork。

[oschina 地址]

http://git.oschina.net/cmlbeliever/SpringBootLearning

[github 地址]

https://github.com/cmlbeliever/SpringBootLearning

SpringMvc 你该知道如何在HandlerExceptionResolver中获取Model的更多相关文章

  1. 如何在Sql2008中获取表字段属性和注释?

    如何在Sql2008中获取表字段属性和注释? select b.[value] from sys.columns a left join sys.extended_properties b on a. ...

  2. 关于Activity的getReferrer():如何在Activity中获取调用者?

    http://blog.csdn.net/u013553529/article/details/53856800 关于Activity的getReferrer()之一:如何在Activity中获取调用 ...

  3. 如何在onCreate中获取View的高度和宽度

    如何在onCreate中获取View的高度和宽度 原文链接:http://mp.weixin.qq.com/s?__biz=MzAwODE1NTI2MQ==&mid=2247483676&am ...

  4. 如何在Job中获取 IOC applicationcontext

    如何在Job中获取 IOC applicationcontext https://segmentfault.com/q/1010000008002800 SpringBoot之整合Quartz调度框架 ...

  5. 如何在 WPF 中获取所有已经显式赋过值的依赖项属性

    原文:如何在 WPF 中获取所有已经显式赋过值的依赖项属性 获取 WPF 的依赖项属性的值时,会依照优先级去各个级别获取.这样,无论你什么时候去获取依赖项属性,都至少是有一个有效值的.有什么方法可以获 ...

  6. asp.net mvc 如何在View中获取Url参数的值

    如果url是 /home/index?id=3 直接Request就ok. 但是如果路由设定为:{controller}/{action}/{id} url是 /home/index/3   这时想在 ...

  7. jsp内置对象pageContext如何在Servlet中获取值

    pageContext javax.servlet.jsp.PageContext 的实例,该对象代表该JSP 页面上下文,使用该对象可以访问页面中的共享数据.常用的方法有getServletCont ...

  8. js技术之如何在JS中获取input的值

    在JavaScript中获取input元素value的值: 方法一:var variations_number = $("#input的id名").val(); 1 <!DO ...

  9. 如何在SqlServer中获取前端连接的IP地址,计算机名等信息

    在一些需求中,可能我们需要知道连接到SqlServer的前端程序的一些系统信息,比如前端连接的计算机名称,IP地址,什么时候开始请求连接,什么时候结束连接等信息. 如果你对SqlServer的系统函数 ...

随机推荐

  1. 地点下来框的实现(php)

    效果图: 样式(bootstrap): class="selectpicker show-tick form-control",就是多了个form-contro就行了 概念: 这里 ...

  2. 点击表头取下标&js时间转时间戳

    1.Date.parse(new Date("2017-7-31")); 2.$("th").eq(this.cellIndex);  // 3.end($ar ...

  3. 更加安全的密钥生成方法Diffie-Hellman

    更加安全的密钥生成方法Diffie-Hellman 之前我们谈到了密钥配送的问题,这个世界是如此的危险, 一不小心通信线路就会被监听,那么我们怎么在这种不安全的线路中传递密钥呢? 这里我们介绍一下Di ...

  4. Spring5参考指南: BeanWrapper和PropertyEditor

    文章目录 BeanWrapper PropertyEditor BeanWrapper 通常来说一个Bean包含一个默认的无参构造函数,和属性的get,set方法. org.springframewo ...

  5. 【Linux常见命令】netstat命令

    netstat - Print network connections, routing tables, interface statistics, masquerade connections, a ...

  6. Express 文档(常见问题)

    常见问题 我该如何构建我的应用程序? 这个问题没有明确的答案,答案取决于你的应用程序规模和所涉及的团队,为了尽可能灵活,Express在结构方面没有做出任何假设. 在你喜欢的任何目录结构中,路由和其他 ...

  7. windows右键没有新建选项的解决办法

    1 以管理员身份运行cmd 2 cmd /k reg add "HKEY_CLASSES_ROOT\Directory\Background\shellex\ContextMenuHandl ...

  8. TreeMap分析(中)

    通过上篇文章,大家已经能够理解红黑树的基础数据结构,那么这篇文章就来分析下,在红黑树中插入一个结点后,内部数据结构发生了哪些变化. TreeMap插入某个结点的源码分析 /** * 插入节点,并平衡红 ...

  9. 翻译 - Kafka Streams 介绍(一)

    2019独角兽企业重金招聘Python工程师标准>>> 资料 [原文地址](http://kafka.apache.org/11/documentation/streams/) 正文 ...

  10. 【批处理】TXT文件批量转HTML文件工具

    说到批量转html文件,相信喜欢看小说或经常制作电子书和教程的朋友应该很熟悉.因为,我们每次都会面临成千上万的txt文件,要将其转换为能正确显示的html文件是很麻烦的.当然,现在有很多的软件也可以实 ...