SpringMVC从入门到精通(二)

6. 响应视图和结果数据

6.1 返回值类型

(1)返回字符串类型

  • controller方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址
	@RequestMapping("testString")
	public String testString(){
	    return "success";
	}

(2)返回值void

  • Servlet原始 API 可以作为控制器中方法的参数,所以在 controller方法形参上可以定义request和response,使用request或response指定响应结果:
@RequestMapping("testVoid")
public void testVoid(HttpServletRequest request, HttpServletResponse response) throws Exception{
    request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request,response);
}
@RequestMapping("testVoid1")
public void testVoid1(HttpServletRequest request, HttpServletResponse response) throws Exception{
     response.sendRedirect("/user/testVoid");
}

(3)返回值ModelAndView

  • ModelAndView是SpringMVC 为我们提供的一个对象,该对象也可以用作控制器方法的返回值。
  • 在SpringMVC的执行流程中,其中在处理器适配器选择相应的处理器(handler/自己编写的controller)时,会返回一个ModelAndView,我们在此处使用返回值为ModelAndView,它就会替代底层自己构建的ModelAndView
public ModelAndView testModelAndView(){
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("username","eric");
    modelAndView.setViewName("success");
    return modelAndView;
}

6.2 springmvc作用域传值

  • ModelAndView底层封装
    • 在 ModelAndView 中有一个属性 Model 实现了java.util下的LinkedHashMap ,当使用Map 或 Model 进行传值时,SpringMVC会将ModelAndView中的ModelMap或Model进行替换,将其替换为我们自定义的Map或Model
      在这里插入图片描述
  • 下列传值操作均将值保存到Request作用域中

(1)使用原生的Request域对象传值

/**
 * 不带返回值类型的controller方法
 * 使用原生的Request对象值传递
 */
@RequestMapping("testVoid")
public void testVoid(HttpServletRequest request, HttpServletResponse response) throws Exception{
    request.setAttribute("age",18);
    //使用原生的servlet进行转发
    request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request,response);
}

(2)使用Map集合传值

/**
 * 带返回值的方法
 * 使用Map集合进行值传递 将map对象作为方法的形式参数
 * @return
 */
@RequestMapping("hello")
public String sayHello(Map<String,Object> map){
    map.put("sname","铁蛋");
    map.put("sage",19);
    return "success";
}

(3)使用Model对象传值

/**
 * 使用Model对象传值
 * @param model
 * @return
 */
@RequestMapping("model")
public String testModel(Model model){
    model.addAttribute("address","西安");
    model.addAttribute("score",19);
    return "success";
}

(4)使用ModelAndView对象传值

/**
 * 使用ModelAndView进行值传递
 * @return
 */
@RequestMapping("testModelAndView")
public ModelAndView testModelAndView(){
    ModelAndView mv = new ModelAndView();
    //存储值
    mv.addObject("username","kobe");
    //设置响应视图
    mv.setViewName("success");
    return mv;
}

6.3 转发和重定向

(1)forward转发

  • controller方法在提供了String类型的返回值之后,默认就是请求转发
  • 它相当于 “request.getRequestDispatcher(“url”).forward(request,response)”。使用请求转发,既可以转发到jsp,也可以转发到其他的控制器方法。
  • 转发jsp:
@RequestMapping("testString")
public String testString(){
    //return "success";
    return "forward:/WEB-INF/pages/success.jsp";
}
  • 转发到控制器其他方法
@RequestMapping("testString")
public String testString(){
    return "forward:/user/model";
}

(2)Redirect重定向

  • contrller方法提供了一个 String 类型返回值之后,它需要在返回值里使用:redirect:
  • 重定向jsp:
@RequestMapping("testString")
public String testString(){
    return "redirect:/pages/success.jsp";
}
  • 重定向到控制器其他方法
@RequestMapping("testString")
public String testString(){
    return "redirect:/user/model";
}

6.4 json数据格式的请求与响应

(1)依赖

  • 引入下列依赖后,在Controller接收json格式请求的数据或响应json格式的数据时,使用相关注解SpringMVC会进行数据格式自动转换(pojo《==》json)
    • @RequestBody: 会自动的将前台传递的json格式的数据转换成pojo对象。
    • @ResponseBody: 会自动的将pojo对象以json格式的数据传递给前台。
<dependency>
	  <groupId>com.fasterxml.jackson.core</groupId>
	  <artifactId>jackson-databind</artifactId>
	  <version>2.9.0</version>
</dependency>
<dependency>
	  <groupId>com.fasterxml.jackson.core</groupId>
	  <artifactId>jackson-core</artifactId>
	  <version>2.9.0</version>
</dependency>
<dependency>
	  <groupId>com.fasterxml.jackson.core</groupId>
	  <artifactId>jackson-annotations</artifactId>
	  <version>2.9.0</version>
</dependency>

(2)@ResponseBody响应json数据原理

  • 作用:
    • 该注解用于将Controller的方法返回的对象,通过HttpMessageConverter接口转换为指定格式的数据如:json,xml等,通过Response响应给客户端。
  • 响应中文乱码解决
    • 配置字符集转换器
<mvc:annotation-driven >
    <!--设置响应输出字符集-->
    <mvc:message-converters>
        <bean class="org.springframework.http.converter.StringHttpMessageConverter">
            <property name="supportedMediaTypes">
                <list>
                    <value>text/html;charset=utf-8</value>
                </list>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

(3)示例

  • 以ajax为例,向服务器端发送json数据格式的请求
    • 发送的必须是json类型格式的数据(发送的data)
    • 若不对contextType不指定json格式数据,后端将会报错(415访问媒体格式不支持)
      在这里插入图片描述
  • 服务器端接收json格式数据,并响应json格式数据
    • 若不添加相应的json依赖,后端会报错(500,数据格式转换异常)
      在这里插入图片描述

7.SpringMVC 实现文件上传

7.1 文件上传三要素

  • 请求方式必须为POST,必须配置enctype
  • 在文件input的type必须为file,且其中的name属性值,要和后端接收问价的形参名称相同,否则需要使用@RequestParam进行转化接收参数
  • 接口参数是MultipartFile类型

7.2 文件上传依赖

<dependency>
	  <groupId>commons-fileupload</groupId>
	  <artifactId>commons-fileupload</artifactId>
	  <version>1.3.1</version>
</dependency>
<dependency>
	  <groupId>commons-io</groupId>
	  <artifactId>commons-io</artifactId>
	  <version>2.4</version>
</dependency>

7.2 文件上传示例(后端需要配置文件解析器)

(1)前端form表单

<%--    在文件input中的name属性值,要和后端接收问价的形参名称相同,否则需要使用@RequestParam进行转化--%>
<form action="/file/upload" method="post" enctype="multipart/form-data">
    <input type="file" name="file"/>
    <input type="submit" value="上传文件"/>
</form>

(2)后端controller方法接收

@Controller
@RequestMapping("file")
public class FileController {
    @RequestMapping("upload")
    public String upLoad(MultipartFile file) throws IOException {
//        获取文件名称
        String filename = file.getOriginalFilename();
//        上传文件
        file.transferTo(new File("d:\\",filename));
        return "success";
    }
}

(3)配置文件解析器

  • 配置文件上传解析器: 名称不要随便起,约定优于配置,在底层调用的一般是设置解析器实现的顶层接口
  • CommonsMultipartResolver 顶层接口是 MultipartResolver
<!--    设置文件上传解析器: 名称不要随便起,约定由于配置,在底层调用的一般是设置解析器实现的顶层接口-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="defaultEncoding" value="UTF-8"></property>
        <property name="maxUploadSize" value="2097152"></property>
    </bean>

8. SpringMVC 中的异常处理

8.1 springmvc默认处理异常的方式

  • Springmvc默认处理异常的方式和servlet处理异常的方式是一样的。都是为web.xml里面定义错误处理代码和错误处理页面。
  • 配置上述后,当访问发生错误时,根据发生错误的状态码,会自动跳转到配置的页面
  • 示例:在web.xml里面定义错误处理页面
<!--配置错误处理页面-->
<error-page>
  <error-code>400</error-code>
  <location>/error/400.jsp</location>	<!-- 发生错误后,跳转到的页面(父目录是WEB-INF)-->
</error-page>

<error-page>
  <error-code>404</error-code>
  <location>/error/404.jsp</location>
</error-page>

<error-page>
  <error-code>500</error-code>
  <location>/error/500.jsp</location>
</error-page>

8.2 @ExceptionHandler注解进行异常处理

  • 在Controller层某方法上使用@ExceptionHandler注解,当controller发生错误时,异常处理器会接手接下来的处理,在添加了@ExceptionHandler方法中,编写相关处理异常逻辑代码
  • 这种异常处理的方式解决了问题。但是异常处理的代码和控制器代码在一个controller里面定义,违反了代码编写的职责单一性。
    在这里插入图片描述

8.3 使用@controlleradvice注解

  • @controlleradvice 对所有添加了 @Controller 注解的类(处理器/我们平常编写的controller)进行了增强操作(不一定是处理异常,也可能是其他的增强操作,eg: 初始化数据等)
  • 定义一个类,专门进行异常处理(在该类上添加 @controlleradvice 注解)
  • 示例:
    • 异常处理类
@ControllerAdvice
public class ExeceptionAdvice {

//    处理运行时异常
    @ExceptionHandler(RuntimeException.class)
    public ModelAndView execeptionPut(Exception e){
        ModelAndView modelAndView=new ModelAndView();
        modelAndView.setViewName("/error/error");
        modelAndView.addObject("msg",e.toString());
        return modelAndView;
    }
}
  • 任何添加@Controller 注解的类都将被上述类增强
@Controller
@RequestMapping("exeception")
public class ExeceptionController {

    @RequestMapping("test01")
    public String test01(){
//        空指针
        String s=null;
        int length = s.length();
        int num=10/0;
        return "success";
    }
}

9. springmvc中的拦截器

9.1 拦截器的作用

        Spring MVC 的处理器拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理。
        用户可以自己定义一些拦截器来实现特定的功能。
        谈到拦截器,还要向大家提一个词——拦截器链(Interceptor Chain)。拦截器链就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。
        说到这里,可能大家脑海中有了一个疑问,这不是我们之前学的过滤器吗?是的它和过滤器是有几分相似,但是也有区别,接下来我们就来说说拦截器与过滤器的区别:
        过滤器是 servlet 规范中的一部分,任何 java web 工程都可以使用。
        拦截器是 SpringMVC 框架自己的,只有使用了 SpringMVC 框架的工程才能用。
        过滤器在 url-pattern 中配置了/*之后,可以对所有要访问的资源拦截。
        拦截器它是只会拦截访问的控制器方法(只会拦截接口),如果访问的是 jsp,html,css,image 或者 js 是不会进行拦截的。
        它也是 AOP 思想的具体应用。
        我们要想自定义拦截器, 要求必须实现:HandlerInterceptor 接口。

9.2 自定义拦截器示例

(1)编写一个实现HandlerInterceptor接口的实现类

//自定义拦截器
public class MyInterceptor implements HandlerInterceptor {
    /*
    * 预处理,controller方法执行前
    * return true放行,执行下一个拦截器,如果没有,执行controller中的方法
    * return false不放行
    * */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("MyInterceptor1执行了......前111(第一个拦截器)");
        //request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request, response);
        //true为放行,false为不放行
        return true;
    }

    /*
    * 后处理方法
    * controller方法执行后
    * success.jsp执行之前
    * */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("MyInterceptor1执行了......后111(第一个拦截器)");
        request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request, response);
    }

    /*
    * success.jsp页面执行后,该方法会执行
    * 这个页面不能再跳转其他页面了
    * */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("MyInterceptor1执行了......最后111(第一个拦截器)");
    }
}

(2)在spring配置文件中配置拦截器

<!--配置拦截器-->
<mvc:interceptors>
    <!--配置第一个拦截器-->
    <mvc:interceptor>
        <!--要拦截的具体的方法-->
        <mvc:mapping path="/user/*"/>
        <!--不要拦截的方法-->
        <!--<mvc:exclude-mapping path=""/>-->
        <!--配置拦截器对象(自己实现HandlerInterceptor接口的实现类对象)-->
        <bean class="org.westos.demo.interceptor.MyInterceptor"/>
</mvc:interceptor>

9.3 拦截器的细节

(1)从AOP角度考虑拦截器的执行与配置
在这里插入图片描述
(2)多个拦截器执行顺序

  • 拦截器链执行顺序,类似于栈结构(先进后出)
  • 当存在多个拦截器,外层的拦截器进去之后,一定会执行此外层的 afterCompletion 方法(内层拦截器中断之后也会执行外层的 afterCompletion 方法
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值