SpringMvc 你该知道如何在HandlerExceptionResolver中获取Model
在项目开发中,我们通常通过参数的形式注入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的更多相关文章
- 如何在Sql2008中获取表字段属性和注释?
如何在Sql2008中获取表字段属性和注释? select b.[value] from sys.columns a left join sys.extended_properties b on a. ...
- 关于Activity的getReferrer():如何在Activity中获取调用者?
http://blog.csdn.net/u013553529/article/details/53856800 关于Activity的getReferrer()之一:如何在Activity中获取调用 ...
- 如何在onCreate中获取View的高度和宽度
如何在onCreate中获取View的高度和宽度 原文链接:http://mp.weixin.qq.com/s?__biz=MzAwODE1NTI2MQ==&mid=2247483676&am ...
- 如何在Job中获取 IOC applicationcontext
如何在Job中获取 IOC applicationcontext https://segmentfault.com/q/1010000008002800 SpringBoot之整合Quartz调度框架 ...
- 如何在 WPF 中获取所有已经显式赋过值的依赖项属性
原文:如何在 WPF 中获取所有已经显式赋过值的依赖项属性 获取 WPF 的依赖项属性的值时,会依照优先级去各个级别获取.这样,无论你什么时候去获取依赖项属性,都至少是有一个有效值的.有什么方法可以获 ...
- asp.net mvc 如何在View中获取Url参数的值
如果url是 /home/index?id=3 直接Request就ok. 但是如果路由设定为:{controller}/{action}/{id} url是 /home/index/3 这时想在 ...
- jsp内置对象pageContext如何在Servlet中获取值
pageContext javax.servlet.jsp.PageContext 的实例,该对象代表该JSP 页面上下文,使用该对象可以访问页面中的共享数据.常用的方法有getServletCont ...
- js技术之如何在JS中获取input的值
在JavaScript中获取input元素value的值: 方法一:var variations_number = $("#input的id名").val(); 1 <!DO ...
- 如何在SqlServer中获取前端连接的IP地址,计算机名等信息
在一些需求中,可能我们需要知道连接到SqlServer的前端程序的一些系统信息,比如前端连接的计算机名称,IP地址,什么时候开始请求连接,什么时候结束连接等信息. 如果你对SqlServer的系统函数 ...
随机推荐
- Springboot:员工管理之国际化(十(3))
1:IDEA编码设置UTF-8 2:创建国际化文件 i18n\login.properties #默认语言 i18n\login_en_US.properties #英文语言 i18n\login_z ...
- 当td中文字过长时,显示为省略号
当表格中的文字过长时,可选择已省略号显示.这里是用js实现的.首先获取td中的文字长度(innerText.length),如果长度超过了设定的长度,则截取内容,加上省略号显示.示例代码如下: $(f ...
- Java 多线程实现方式二:实现 Runnable 接口
由于java是单继承,很多时候为了实现多线程 通过继承 Thread 类后,就不能再继承其他类了.为了方便可以通过实现 Runnable 接口来实现,和Tread 类似需要重写run 方法. 下面通过 ...
- git在push时候出现timeout的解决方法
1.先对照一下公钥是否正确:linux下生成的.ssh在~/.ssh,命令行直接cd ~/.ssh即可:2.本次出现的问题如下: 出现了连接不上的情况,传输协议用的是ssh,此时需要进行如下操作: 进 ...
- AtomineerUtils使用说明
AtomineerUtils使用说明 VS2015PluginCrackAtomineer 介绍 AtomineerUtils 是国外的一款用于生成源代码注释的一款 VS 插件工具. 这款插件,支持 ...
- 经过踩坑,搭建成功的Appium自动化测试环境
因为最近本人准备搞app自动化,所以就搭建环境过程记录下来(主要踩过好几个坑) 期间有点烦躁,后面调整了下心态还是成功弄好了. 一.Appium环境搭建准备软件 所需要到的软件如下: 1.安装JDK1 ...
- Android P HIDL demo代码编写 (原创)
之前的文章已经分析了HIDL服务的注册和调用,这篇文章来总结下一个HIDL的服务如何编写. 缩写HAL文件 首先要确认放置文件夹和接口的包名,因为这跟后面使用脚本生成一部分代码有关,一般默认的放在ha ...
- SSM整合案例:图书管理系统
目录 SSM整合案例:图书管理系统 1.搭建数据库环境 2.基本环境搭建 2.1.新建一个Maven项目,起名为:ssmbuild,添加web的支持 2.2.导入pom的相关依赖 2.3.Maven静 ...
- 动态代理学习(二)JDK动态代理源码分析
上篇文章我们学习了如何自己实现一个动态代理,这篇文章我们从源码角度来分析下JDK的动态代理 先看一个Demo: public class MyInvocationHandler implements ...
- Spring 中基于 AOP 的 @AspectJ注解实例
@AspectJ 作为通过 Java 5 注释注释的普通的 Java 类,它指的是声明 aspects 的一种风格.通过在你的基于架构的 XML 配置文件中包含以下元素,@AspectJ 支持是可用的 ...