Spring MVC 高级技术包括但不限于 web.xml 配置, 异常处理, 跨重定向请求传递数据
1,web.xml 文件的配置
- <!DOCTYPE web-app PUBLIC
- "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
- "http://java.sun.com/dtd/web-app_2_3.dtd">
- <web-app>
- <context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>classpath:spring-*.xml</param-value>
- </context-param>
- <listener>
- <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
- </listener>
- <servlet>
- <servlet-name>appServlet</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>classpath:springMvc.xml</param-value>
- </init-param>
- </servlet>
- <servlet-mapping>
- <servlet-name>appServlet</servlet-name>
- <url-pattern>/</url-pattern>
- </servlet-mapping>
- </web-app>
ContextLoaderListener 是根容器, DispatcherServlet 是子容器. 父容器中管理的 bean 可以被子容器引用, 反之, 不行. 它们都从各自的 xml 文件初始化自己的上下文
ContextLoaderListener 如果未指定 contextConfigLocation 参数, 则默认加载的配置文件为 / WEB-INF/applicationContext.xml
DispatcherServlet 如果未指定 contextConfigLocation 参数, 则根据 < servlet-name > 元素指定的名称, 在 / WEB-INF / 文件夹下寻找配置文件 appServlet-servlet.xml
2, 处理异常
Spring 提供了多种方式将异常转换为响应
特定的 Spring 异常会自动映射为指定的 HTTP 状态码
异常上可以添加 @ResponseStatus 注解, 将异常映射为某一个 HTTP 状态码
在方法上可以添加 @ExceptionHandler 注解, 使其用来处理异常
1) 特定的 Spring 异常会自动映射为指定的 HTTP 状态码
如果在控制器中抛出了异常, 该异常不在列表中, 又没有指定 HTTP 状态码, 则默认为状态码为 500
2) 异常上可以添加 @ResponseStatus 注解, 将异常映射为某一个 HTTP 状态码
- @Controller
- public class HelloController {
- @RequestMapping("/home")
- public String home(){
- System.out.println("执行 home");
- throw new MyException();
- // return "home"; // 返回一个字符串, 即逻辑视图名
- }
- }
- package com.cn.Exception;
- import org.springframework.http.HttpStatus;
- import org.springframework.web.bind.annotation.ResponseStatus;
- public class MyException extends RuntimeException{
- }
浏览器访问 http://localhost:8080/home
修改自定义异常
- package com.cn.Exception;
- import org.springframework.http.HttpStatus;
- import org.springframework.web.bind.annotation.ResponseStatus;
- @ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "抛出异常的原因")
- public class MyException extends RuntimeException{
- }
浏览器访问 http://localhost:8080/home
3) 在方法上可以添加 @ExceptionHandler 注解, 使其用来处理异常
该方式是按照处理请求的方式处理异常,@ExceptionHandler 注解的方法返回值为字符串表示逻辑视图名
- package com.cn.Exception;
- public class MyException2 extends RuntimeException{
- }
- @Controller
- public class HelloController {
- @RequestMapping("/home")
- public String home(){
- System.out.println("执行 home");
- throw new MyException2();
- // return "home"; // 返回一个字符串, 即逻辑视图名
- }
- @ExceptionHandler(MyException2.class)
- public String handleMException(){
- System.out.println("处理异常逻辑");
- return "fail";
- }
- }
访问 http://localhost:8080/home, 后台输出
浏览器页面
@ExceptionHandler 注解的方法可以处理同一个控制器中所有处理器方法所抛出的异常 (注解指定的异常), 注解定义如下
- @Target({ElementType.METHOD})
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- public @interface ExceptionHandler {
- Class<? extends Throwable>[] value() default {};
- }
spring 也支持为控制器添加通知, 那么该通知对于所有的控制器中所有的处理器方法抛出的异常都起作用, 实现如下
@ControllerAdvice 所标注的类会被组件扫描实例化, 交给容器管理. 最为实用的一个场景是将所有 @ExceptionHandler 标注的方法收集到一个类中, 这样所有的控制器异常就能在一个地方进行一致处理. 以下 HandleException 类定义表明在控制器中的处理器抛出 MyException2 类的异常, 就会被 handleMException 方法处理, 最终跳转 fail.jsp 页面
- package com.cn.advice;
- import com.cn.Exception.MyException2;
- import org.springframework.web.bind.annotation.ControllerAdvice;
- import org.springframework.web.bind.annotation.ExceptionHandler;
- @ControllerAdvice
- public class HandleException {
- @ExceptionHandler(MyException2.class)
- public String handleMException(){
- System.out.println("处理异常逻辑");
- return "fail";
- }
- }
3, 跨重定向请求传递数据
如果是 forward 转发请求, 那么处理器方法业务处理完成后, 该方法所指定的模型数据会复制到请求中, 作为请求的一部分, 转发到下一个处理器, 下一个处理器可以从请求中获取上一个处理的模型数据;
如果是 redirect 重新向, 原始请求就结束了, 并且会重新发起一个 GET 请求. 因此原始请求中的模型数据也就消失了, 在请求到达下一个处理器, 没有任何的模型数据, 需要模型数据必须自己处理.
针对重定向传递数据, 有两种方案:
使用 URL 模板以路径变量和 / 或查询参数的形式传递数据
使用 flash 属性传递数据
1) 使用 URL 模板以路径变量和 / 或查询参数的形式传递数据
- @Controller
- public class HelloController {
- @RequestMapping("/home2")
- public String home2(Model model){
- model.addAttribute("id","12324131343256");
- model.addAttribute("name","pick");
- return "redirect:home3/{id}"; // 含有 redirect 的字符串, 表示重定向到另一个处理器;
- // 如果含有 forward 的字符串, 表示转向到另一个处理器
- }
- @RequestMapping("/home3/{id}")
- public String home3(@PathVariable String id, Model model){
- System.out.println("传递的 ID="+id);
- model.addAttribute(new User());
- return "home";
- }
- }
浏览器访问 localhost:8080/home2, 抓包得访问的 url. 从以下可以看出, 处理器完成后重定向时, 模型中的数据填充到了路径变量中, 路径变量中没有的 key, 则以查询参数的形式赋在 url 之后, 组成了新的 url 访问.
2) 使用 flash 属性传递数据
重定向时候, 采用 1) 中的方式传递数据, 仅仅能够传递简单的数据, 不能传递对象. 如何将对象也能传递到下一个处理器呢? 可以将要传递到下一个处理器的数据放入用户会话中, 然后在下一个处理器从会话的取出来, 并删除会话中的该数据. 然而, spring 提供了将数据发送为 flash 属性的功能, 不需要我们管理这些数据, flash 属性会一致携带这些数据直到下一次请求才消失.
Spring 提供 RedirectAttributes 是 Model 接口的子接口, 除了提供 Model 的所有功能, 还提供了几个方法用来设置 flash 属性.
- @Controller
- public class HelloController {
- @RequestMapping("/home2")
- public String home2(RedirectAttributes model){
- model.addAttribute("id","12324131343256");
- model.addFlashAttribute("user",new User("liming","123456"));// 设置 flash 属性, 也可以 model.addFlashAttribute(new User("liming","123456"))
- // 则通过值得类型自行推断出 key 为 user
- return "redirect:home3/{id}";
- }
- @RequestMapping("/home3/{id}")
- public String home3(@PathVariable String id, RedirectAttributes model){
- System.out.println("传递的 ID="+id);
- if (model.containsAttribute("user")) {
- System.out.println("传递的对象:" + model.getFlashAttributes().get("user"));
- }
- return "home";
- }
- }
来源: https://www.cnblogs.com/shixiemayi/p/9569338.html