视图渲染+Spring MVC进阶技术 -- 《Spring In Action》
视图解析
Spring MVC将控制器中请求处理的逻辑和视图中的渲染实现解耦(所编写的控制器方法没有直接产生浏览器中渲染所需要的HTML,这些方法只是将一些数据填充到模型中,然后将模型传递给用来渲染的视图)
视图解析能够将逻辑视图名转换为物理实现
视图解析器 | 描述 |
---|---|
BeanNameViewResolver | 将视图解析为Spring应用上下文的Bean,其中Bean的ID与视图的名字相同 |
ContentNegotiatingViewResolver | 根据客户端需要的内容类型来解析视图,委托给另一个能够产生对应内容类型的视图解析器 |
FreeMarkerViewResolver | 将视图解析为FreeMarker模板 |
InternalResourceViewResolver | 将视图解析为Web应用的内部资源(eg: JSP) |
JasperReportsViewResolver | 将视图解析为JasperReports定义 |
ResourceBundleViewResolver | 将视图解析为资源bundle(一般为属性文件) |
UrlBasedViewResolver | 直接根据视图的名称解析视图,视图的名称会匹配一个物理视图的定义 |
XmlViewResolver | 将视图解析为特定XML文件中的Bean定义,类似于BeanNameViewResolver |
XsltViewResolver | 将视图解析为XSLT转换后的结果 |
配置适用于JSP的视图解析器
InternalResourceViewResolver所采取的的方式是在视图名上添加前缀和后缀,进而确定一个Web应用中视图资源的物理路径。
例如:
视图名:home
通用实践将JSP文件放入Web应用的WEB-INF目录以防止对其直接访问。
配置InternalResourceView Resolver
使用Java类配置
@Bean public ViewResolver viewResovler(){ InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/WEB-INF/views/"); resolver.setSuffix(".jsp"); return resolver; }
使用XML配置
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/views/" p:suffix=".jsp"/>
通过配置将逻辑视图名解析为JSP文件物理地址
例如:
* home
解析为/WEB-INF/views/home.jsp
* productList
解析为/WEB-INF/views/productList.jsp
* books/detail
解析为/WEB-INF/views/books/detail.jsp
Spring MVC进阶技术
处理异常
当处理请求的时候,抛出异常该如何处理?如果发生了这样情况该如何给出客户端响应?
无论是否异常,Servlet请求的输出都是一个Servlet响应,如果在请求处理的时候出现异常,输出依然会是Servlet响应。异常必须以某种方式转换为响应
Spring提供多种方式将异常转换为响应:
- 特定的Spring异常会自动映射为指定的HTTP状态码;
- 异常上添加
@ResponseStatus
,将其映射为某一个HTTP状态码; - 在方法上添加
@ExceptionHandler
注解,使其用来处理异常;
处理异常的最简单方式:将其映射到HTTP状态码上,进而放到响应之中。
将异常映射为HTTP状态码
默认情况下,Spring会将自身的异常自动转换为合适的状态码
Spring异常 | HTTP状态码 |
---|---|
BindException | 400 - Bad Request |
ConversionNotSupportedException | 500 - Internal Server Error |
HttpMediaTypeNotAcceptableException | 406 - Not Acceptable |
HttpMediaTypeNotSupportedException | 415 - Unsupported Media Type |
HttpMessageNotReadableException | 400 - Bad Request |
HttpMessageNotWritableException | 500 - Internal Server Error |
HttpRequestMethodNotSupportedException | 405 - Method Not Allowed |
MethodArgumentNotValidException | 400 - Bad Request |
MissingServletRequestParameterException | 400 - Bad Request |
MissingServletRequestPartException | 400 - Bad Request |
NoSuchRequestHandlingMethodException | 404 - Not Found |
TypeMismatchException | 400 - Bad Request |
异常一般为Spring自身抛出,作为DispatcherServlet处理过程中或执行校验时出现问题的结果。
Spring可以通过@ResponseStatus
注解将异常映射为HTTP 状态码
- 使用
@ResponseStatus注解: 将异常映射为特定的状态码
@ResponseStatus(value=HttpStatus.NOT_FOUND, reason="Spittle Not Found") // 将异常状态映射为404 public class SpittleNotFoundException extends RuntimeException{ }
引入@ResposneStatus
注解之后,如果控制器方法抛出SpittleNotFoundException
异常的话,响应将会具有404状态码
- 抛出
SpittleNotFoundException
将异常映射为特定的状态码
@RequestMapping(value="/{spittleId}", method=RequestMethod.GET) public String spittle(@PathVariable("spittleId") long spittleId, Model model){ Spittle spittle = spittleRepository.findOne(spittleId); if(spittle == null){ throw new SpittleNotFoundException(); } model.addAttribute(spittle); return "spittle"; }
编写异常处理方法
之前通过异常映射为状态码但如果需要将响应中不仅要包含状态码还要有所产生的错误,则就不同将异常视为HTTP错误需要按照处理请求的方式来处理异常
- 传统的在处理请求的方法中直接处理异常
@RequestMapping(method=RequestMethod.POST) public String saveSpittle(SpittleForm form, Model model){ try{ spittleRepository.save(new Spittle(null, form.getMessage(), new Date(), form.getLongitude(), form.getLatitude())); return "redirect:/spittles"; }catch(DuplicateSpittleException e){ return "error/duplicate"; } }
- 异常处理方法
@ExceptionHandler(DuplicateSpittleException.class) public String handleDuplicateSpittle(){ return "error/duplicate"; }
handleDuplicateSpittle()
方法添加@ExceptionHandler
注解,当抛出DuplicateSpittleException异常时候,将委托该方法进行处理。
对于@ExceptionHandler
注解标注的方法能处理同一个控制器中所有处理器方法所抛出的异常。
为控制器添加通知
控制器类的特定切面能够运用到整个应用程序的所有控制器中。
控制器通知(Controller Advice)是任意带有@ControllerAdvice
注解的类,此类包含一个或多个以下类型的方法
@ExceptionHandler
@InitBinder
@ModelAttribute
@ControllerAdvice
实用的一个场景是将所有的@ExceptionHandler
方法收集到一个类中,这样所有控制器的异常就能在一个地方进行一致的处理。
使用@ControlerAdvice
为所有的控制器处理异常
@ControllerAdvice public class AppWideExceptionHandler{ // 定义控制器类 @ExceptionHandler(DuplicateSpittleException.class) public String duplicateSpittleHandler(){ // 定义异常处理方法 return "error/duplicate"; } }
将DuplicateSpittleException的处理方法用到整个应用程序的所有控制器上。现在,如果任意的控制器方法抛出了DuplicateSpittleException
不管这个方法位于哪个控制器中,都会调用这个duplicateSpittleHandler()
方法来处理异常。
跨重定向请求传递数据
对于重定向来说,模型无法用来传递数据。
- 重定向携带数据的方法
- 使用URL模板以路径变量或查询参数的形式传递数据
- 通过flash属性发送数据
通过URL模板重定向
- 传统方法
通过路径变量和查询参数传递数据
例如以路径变量的形式传递新创建Spitter的username。username的值是直接连接到重定向String上的。虽能正常运行但无法满足安全性要求
return "redirect:/spitter/{username}"
- 使用模板的方式来定义重定向URL
@RequestMapping(value="/register", method=POST) public String processRegistration(Spitter spitter, Model model){ spitterRepository.save(spitter); model.addAttribute("username", spitter.getUsername()); return "redirect:/spitter/{username}"; }
此时username作为占位符填充到URL模板中,不是直接连接到重定向String中,所以username中所有的不安全字符都会进行转义。允许用户输入任何想要的内容作为username,并将其附加到路径上。
模型中所有其他的原始类型值都可以添加到URL中作为查询参数。
@RequestMapping(value="/register", method=POST) public String processRegistration(Spitter spitter, Model model){ spitterRepository.save(spitter); model.addAttribute("username", spitter.getUsername()); model.addAttribute("spitterId", spitter.getId()); // 此时spitterId属性没有匹配重定向中URL的任何占位符,会自动以查询参数的形式附加到重定向URL上 return "redirect:/spitter/{username}"; }
eg: 如果username
属性是testxxx
且spitterId
是42
,则结果重定向URL路径是: /spitter/testxxx?spitterId=42
通过路径变量和查询参数的形式跨重定向传递数据是最简单直接的方式,但只限传输一些简单数据eg:String和数字值当发送复杂数据需要通过flash属性提供帮助的领域
使用flash属性
如果需要发送实际的Spitter对象。
如果发送Spitter对象ID,处理重定向方法还需要从数据库中查找才能得到Spitter对象。但是在重定向之前已经获取Spitter对象如何直接将该已经存在的对象直接发送给处理重定向的方法则需要flash
Spitter对象比String和int更为复杂,不能像路径变量和查询参数那么容易发送Spitter对象。只能将其设置为模型中的属性。
存在的另一种方案。将Spitter放到会话中,会话能够长期存在,且能够跨越多个请求。可以在重定向发生之前将Spitter放到会话中。并在重定向后,从会话中将其取出,最后在重定向后在会话中将其清理掉。
Spring提供RedirectAttributes
设置flash属性的方法。RedirectAttributes
提供Model的所有功能还有用来设置flash属性的方法
RedirectAttributes提供一组addFlashAttribute()方法来添加flash属性。
@RequestMapping(value="/register", method=POST) public String processRegistration(Spitter spitter, RedirectAttributes model){ spitterRepository.save(spitter); model.addAttribute("username", spitter.getUsername()); model.addFlashAttribute("spitter", spitter); return "redirect:/spitter/{username}"; }
调用addFlashAttribute()
方法,将spitter作为key,Spitter对象作为值。还可以不设置key参数,让key根据值的类型自行推断:model.addFlashAttribute(spitter)
在重定向执行之前,所有的flash属性都会复制到会话中。
在重定向执行之后,存在会话中的flash属性会被取出,并从会话转移到模型之中。处理重定向的方法能从模型中访问Spitter对象,像获取其他的模型对象一样。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具