蒙圈的备忘录:SpringMVC笔记

SpringMVC

SpringMVC作 为 Spring 框 架 一 部 分 , 能 够 使 用 Spring 的 IoC 和 Aop 。 方 便 整 合Strtus,MyBatis,Hiberate,JPA 等其他框架。

一、maven依赖

<!--servlet-->
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>3.1.0</version>
  <scope>provided</scope>
</dependency>

<!--spring mvc-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>5.2.5.RELEASE</version>
</dependency>

二、web.xml文件配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--声明,注册springmvc的核心对象DispatcherServlet
        需要在tomcat服务器启动后,创建DispatcherServlet对象的实例。
        为什么要创建DispatcherServlet对象的实例呢?
        因为DispatcherServlet在他的创建过程中, 会同时创建springmvc容器对象,
        读取springmvc的配置文件,把这个配置文件中的对象都创建好, 当用户发起
        请求时就可以直接使用对象了。

        servlet的初始化会执行init()方法。 DispatcherServlet在init()中{
           //创建容器,读取配置文件
           WebApplicationContext ctx = new ClassPathXmlApplicationContext("springmvc.xml");
           //把容器对象放入到ServletContext中
           getServletContext().setAttribute(key, ctx);
        }
    -->
    <servlet>
        <servlet-name>myweb</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--springmvc创建容器对象时,读取的配置文件默认是/WEB-INF/<servlet-name>-servlet.xml .
            这种方式不灵活,所有一般对其进行自定义。
        -->
        <!--自定义springmvc读取的配置文件的位置-->
        <init-param>
            <!--springmvc的配置文件的位置的属性-->
            <param-name>contextConfigLocation</param-name>
            <!--指定自定义文件的位置-->
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>

        <!--在tomcat启动后,创建Servlet对象
            load-on-startup:表示tomcat启动后创建对象的顺序。它的值是整数,数值越小,
                            tomcat创建对象的时间越早。 大于等于0的整数。
        -->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>myweb</servlet-name>
        <!--
            使用框架的时候, url-pattern可以使用两种值
            1. 使用扩展名方式, 语法 *.xxxx , xxxx是自定义的扩展名。 常用的方式 *.do, *.action, *.mvc等等
               不能使用 *.jsp
               http://localhost:8080/myweb/some.do
               http://localhost:8080/myweb/other.do

            2.使用斜杠 "/"
        -->
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>

</web-app>

三、springmvc.xml文件配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!--声明组件扫描器-->
    <context:component-scan base-package="com.bjpowernode.controller" />

    <!--声明 springmvc框架中的视图解析器, 帮助开发人员设置视图文件的路径-->
    <bean  class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--前缀:视图文件的路径-->
        <property name="prefix" value="/WEB-INF/view/" />
        <!--后缀:视图文件的扩展名-->
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

四、基本Controller类

package com.bjpowernode.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;

import javax.xml.ws.RequestWrapper;

/**
 *  @Controller:创建处理器对象,对象放在springmvc容器中。
 *  位置:在类的上面
 *  和Spring中讲的@Service ,@Component
 *
 *  能处理请求的都是控制器(处理器): MyController能处理请求,
 *                         叫做后端控制器(back controller)
 *
 *  没有注解之前,需要实现各种不同的接口才能做控制器使用
 */
@Controller
public class MyController {
    /*
       处理用户提交的请求,springmvc中是使用方法来处理的。
       方法是自定义的, 可以有多种返回值, 多种参数,方法名称自定义
     */

    /**
     * 准备使用doSome方法处理some.do请求。
     * @RequestMapping: 请求映射,作用是把一个请求地址和一个方法绑定在一起。
     *                  一个请求指定一个方法处理。
     *       属性: 1. value 是一个String,表示请求的uri地址的(some.do)。
     *                value的值必须是唯一的, 不能重复。
     *       位置:1.在方法的上面,常用的。
     *            2.在类的上面
     *  说明: 使用RequestMapping修饰的方法叫做处理器方法或者控制器方法。
     *  使用@RequestMapping修饰的方法可以处理请求的,类似Servlet中的doGet, doPost
     *
     *  返回值:ModelAndView 表示本次请求的处理结果
     *   Model: 数据,请求处理完成后,要显示给用户的数据
     *   View: 视图, 比如jsp等等。
     */
    /*value值一个*/
    //@RequestMapping(value = "/some.do")
    /*value值多个*/
    //@RequestMapping(value = {"/some.do","/first.do"})
    public ModelAndView doSome(){  // doGet()--service请求处理
        //处理some.do请求了。 相当于service调用处理完成了。
        ModelAndView mv  = new ModelAndView();
        //添加数据, 框架在请求的最后把数据放入到request作用域。
        //request.setAttribute("msg","欢迎使用springmvc做web开发");
        mv.addObject("msg","欢迎使用springmvc做web开发");
        mv.addObject("fun","执行的是doSome方法");

        //指定视图, 指定视图的完整路径
        //框架对视图执行的forward操作, request.getRequestDispather("/show.jsp).forward(...)
        //mv.setViewName("/show.jsp");
        //mv.setViewName("/WEB-INF/view/show.jsp");
        //mv.setViewName("/WEB-INF/view/other.jsp");


        //当配置了视图解析器后,可以使用逻辑名称(文件名),指定视图
        //框架会使用视图解析器的前缀 + 逻辑名称 + 后缀 组成完成路径, 这里就是字符连接操作
        ///WEB-INF/view/ + show + .jsp
        mv.setViewName("show");

        //mv.setView( new RedirectView("/a.jsp"));

        //返回mv
        return mv;
    }


    @RequestMapping(value = {"/other.do","/second.do"})
    public ModelAndView doOther(){
        ModelAndView mv  = new ModelAndView();
        mv.addObject("msg","====欢迎使用springmvc做web开发====");
        mv.addObject("fun","执行的是doOther方法");
        mv.setViewName("other");
        return mv;
    }
}

五@RequestMapping注解

@RequestMapping: 请求映射,作用是把一个请求地址和一个方法绑定在一起。
一个请求指定一个方法处理。

一、常用属性:value 、method

1、属性:value 是一个String,表示请求的uri地址的(some.do)。
value的值必须是唯一的, 不能重复。 在使用时,推荐地址以“/”开头
位置: 1、在方法的上面,常用的。 2、在类的上面
value值一个:
@RequestMapping(value = “/some.do”)
value值多个:
@RequestMapping(value = {"/some.do","/first.do"})
@RequestMapping注解一般用在方法上,如果用在类上那value值的含义不同,类上的value : 表示所有请求地址的公共部分,叫做模块名称

2、属性: method, 表示请求的方式。 它的值RequestMethod类枚举值。
枚举值:GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
它们以“RequestMethod.”开头
如:指定some.do使用get请求方式
@RequestMapping(value = “/some.do”,method = RequestMethod.GET)
其中比较常用的是:GET、POST等
以上处理器方法只能处理 POST 方式提交的请求。客户端浏览器常用的请求方式,及其
提交方式有以下几种:
在这里插入图片描述
也就是说,只要指定了处理器方法匹配的请求提交方式为 POST,则相当于指定了请求发送的方式:要么使用表单请求,要么使用 AJAX 请求。其它请求方式被禁用。
当然,若不指定 method 属性,则无论是 GET 还是 POST 提交方式,均可匹配。即对于请求的提交方式无要求。
指定提交方式可以提升程序运行速度。

二、处理器方法的参数

处理器方法可以包含以下四类参数,这些参数会在系统调用时由系统自动赋值,即程序员可在方法内直接使用。
➢ HttpServletRequest
➢ HttpServletResponse
➢ HttpSession
➢ 请求中所携带的请求参数

public ModelAndView myMethod1(
            /*以下三个参数,如果有可以直接调用*/
            HttpServletRequest request,
            HttpServletResponse response,
            HttpSession session
    ) {
        return null;
    }

    public ModelAndView myMethod2(
            /*请求中所携带的请求参数*/
            /*逐个参数接收,要求参数名与前端传来的参数名称一致*/
            String name,
            Integer age
    ) {
        return null;
    }

    public ModelAndView myMethod3(
            /*逐个参数接收,如果名称不一致可以使用@RequestParam注解*/
                             /*请求中参数名和处理器方法的形参名不一样
                               @RequestParam: 逐个接收请求参数中, 解决请求中参数名形参名不一样的问题
                                    属性: 1. value 请求中的参数名称
                                          2. required 是一个boolean,默认是true
                                              true:表示请求中必须包含此参数。
                                    位置: 在处理器方法的形参定义的前面*/
            @RequestParam(value = "rname", required = false) String name,
            @RequestParam(value = "rage", required = false) Integer age
    ) {
        return null;
    }
    public ModelAndView myMethod4(
            /*处理器方法形参是java对象, 这个对象的属性名和请求中参数名一样的
              框架会创建形参的java对象, 给属性赋值。 请求中的参数是name,框架会调用setName()
              需要自行创建实体类
              */
                                         Student myStudent
    ) {
        return null;
    }

三、处理器方法的返回值

使用@Controller 注解的处理器的处理器方法,其返回值常用的有四种类型:
➢ 第一种:ModelAndView
➢ 第二种:String
➢ 第三种:无返回值 void
➢ 第四种:返回自定义类型对象
根据不同的情况,使用不同的返回值。

1、返回 ModelAndView

若处理器方法处理完后,需要跳转到其它资源,且又要在跳转的资源间传递数据,此时处理器方法返回 ModelAndView 比较好。当然,若要返回 ModelAndView,则处理器方法中需要定义 ModelAndView 对象。
在使用时,若该处理器方法只是进行跳转而不传递数据,或只是传递数据而并不向任何资源跳转(如对页面的 Ajax 异步响应),此时若返回 ModelAndView,则将总是有一部分多余:要么 Model 多余,要么 View 多余。即此时返回 ModelAndView 将不合适。
举例:

@RequestMapping(value = {"/other.do","/second.do"})
    public ModelAndView doOther(){
        ModelAndView mv  = new ModelAndView();
        mv.addObject("msg","====欢迎使用springmvc做web开发====");
        mv.addObject("fun","执行的是doOther方法");
        mv.setViewName("other");
        return mv;
    }
2、返回 String

处理器方法返回的字符串可以指定逻辑视图名,通过视图解析器解析可以将其转换为物理视图地址
在这里插入图片描述

@Controller
public class MyController {
    /*
    * 处理器方法返回String,表示逻辑视图名称,需要配置视图解析
    * */
    @RequestMapping(value = "/returnString-view.do")
    public String doReturnView(HttpServletRequest request,String name, Integer age) {
        System.out.println("dosome方法的,name=" + name + ", age=" + age);

        //手动添加数据到request作用域
        request.setAttribute("myname",name);
        request.setAttribute("myage",age);
        /*
        * show:逻辑视图名称
        * 框架对视图执行forward转发操作
        * */
        return "show";
    }

    //处理器方法返回 String,表示完整视图路径,此时不能配置视图解析器
    @RequestMapping(value = "/returnString-view2.do")
    public String doReturnView2(HttpServletRequest request,String name, Integer age) {
        System.out.println("---doReturnView2方法---,name=" + name + ", age=" + age);

        //手动添加数据到request作用域
        request.setAttribute("myname",name);
        request.setAttribute("myage",age);
        /*
         * 完整视图路径,项目中不能配置视图解析器
         * 框架对视图执行forward转发操作
         * */
        return "WEB-INF/view/show.jsp";
    }

}

3、返回 void(了解)

void:不能表示数据,也不能表示视图。在处理ajax的时候,可以使用void返回值。通过HttpServletResponse输出数据。响应ajax请求。ajax请求服务器端返回的就是数据,和视图无关。

//处理器方法返回void, 响应ajax请求
    //手工实现ajax,json数据: 代码有重复的 1. java对象转为json; 2. 通过HttpServletResponse输出json数据
    @RequestMapping(value = "/returnVoid-ajax.do")
    public void doReturnVoidAjax(HttpServletResponse response, String name, Integer age) throws IOException {
        System.out.println("===doReturnVoidAjax====, name="+name+"   age="+age);
       //处理ajax, 使用json做数据的格式
       //service调用完成了, 使用Student表示处理结果
        Student student  = new Student();
        student.setName("张飞同学");
        student.setAge(28);

        String json = "";
        //把结果的对象转为json格式的数据
        if( student != null){
            ObjectMapper om  = new ObjectMapper();
            json  = om.writeValueAsString(student);
            System.out.println("student转换的json===="+json);
        }

        //输出数据,响应ajax的请求
        response.setContentType("application/json;charset=utf-8");
        PrintWriter pw  = response.getWriter();
        pw.println(json);
        pw.flush();
        pw.close();

    }
4、返回对象 Object

处理器方法也可以返回 Object 对象。这个 Object 可以是 Integer,String,自定义对象,Map,List 等。但返回的对象不是作为逻辑视图出现的,而是作为直接在页面显示的数据出现的。
返回对象,需要使用@ResponseBody 注解,将转换后的 JSON 数据放入到响应体中。
一般都是用于处理ajax,这是一种局部刷新技术,一般和json配合使用。
以下是实现步骤,也是一道面试题:

1	加入处理json的工具库的依赖,springmvc默认使用的jackson

	pom.xml 加入jackson依赖

	<!--jackson依赖-->
    <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-databind</artifactId>
      <version>2.9.0</version>
    </dependency>

2	在springmvc配置文件之间加入 <mvc:annotation-driven> 注解驱动
    
    主配置文件 springmvc.xml 加入:
    
    <!--@ResponseBody注解驱动-->
    <mvc:annotation-driven/>
    
3	在处理器方法的上面加上 @ResponseBody

 /**
     * 处理器方法返回一个Student,通过框架转为json,响应ajax请求
     * @ResponseBody:
     *    作用:把处理器方法返回对象转为json后,通过HttpServletResponse输出给浏览器。
     *    位置:方法的定义上面。 和其它注解没有顺序的关系。
     * 返回对象框架的处理流程:
     *  1. 框架会把返回Student类型,调用框架的中ArrayList<HttpMessageConverter>中每个类的canWrite()方法
     *     检查那个HttpMessageConverter接口的实现类能处理Student类型的数据--MappingJackson2HttpMessageConverter
     *
     *  2.框架会调用实现类的write(), MappingJackson2HttpMessageConverter的write()方法
     *    把李四同学的student对象转为json, 调用Jackson的ObjectMapper实现转为json
     *    contentType: application/json;charset=utf-8
     *
     *  3.框架会调用@ResponseBody把2的结果数据输出到浏览器, ajax请求处理完成
     */
    @RequestMapping(value = "/returnStudentJson.do")
    @ResponseBody
    public Student doStudentJsonObject(String name, Integer age) {
        //调用service,获取请求结果数据 , Student对象表示结果数据
        Student student = new Student();
        student.setName("李四同学");
        student.setAge(20);
        return student; // 会被框架转为json

    }

    /**
     *  处理器方法返回List<Student>
     * 返回对象框架的处理流程:
     *  1. 框架会把返回List<Student>类型,调用框架的中ArrayList<HttpMessageConverter>中每个类的canWrite()方法
     *     检查那个HttpMessageConverter接口的实现类能处理Student类型的数据--MappingJackson2HttpMessageConverter
     *
     *  2.框架会调用实现类的write(), MappingJackson2HttpMessageConverter的write()方法
     *    把李四同学的student对象转为json, 调用Jackson的ObjectMapper实现转为json array
     *    contentType: application/json;charset=utf-8
     *
     *  3.框架会调用@ResponseBody把2的结果数据输出到浏览器, ajax请求处理完成
     */
    @RequestMapping(value = "/returnStudentJsonArray.do")
    @ResponseBody
    public List<Student> doStudentJsonObjectArray(String name, Integer age) {

        List<Student> list = new ArrayList<>();
        //调用service,获取请求结果数据 , Student对象表示结果数据
        Student student = new Student();
        student.setName("李四同学");
        student.setAge(20);
        list.add(student);

        student = new Student();
        student.setName("张三");
        student.setAge(28);
        list.add(student);


        return list;

    }

    /**
     * 处理器方法返回的是String , String表示数据的,不是视图。
     * 区分返回值String是数据,还是视图,看有没有@ResponseBody注解
     * 如果有@ResponseBody注解,返回String就是数据,反之就是视图
     *
     * 默认使用“text/plain;charset=ISO-8859-1”作为contentType,导致中文有乱码,
     * 解决方案:给RequestMapping增加一个属性 produces, 使用这个属性指定新的contentType.
     * 返回对象框架的处理流程:
     *  1. 框架会把返回String类型,调用框架的中ArrayList<HttpMessageConverter>中每个类的canWrite()方法
     *     检查那个HttpMessageConverter接口的实现类能处理String类型的数据--StringHttpMessageConverter
     *
     *  2.框架会调用实现类的write(), StringHttpMessageConverter的write()方法
     *    把字符按照指定的编码处理 text/plain;charset=ISO-8859-1
     *
     *  3.框架会调用@ResponseBody把2的结果数据输出到浏览器, ajax请求处理完成
     */
    @RequestMapping(value = "/returnStringData.do",produces = "text/plain;charset=utf-8")
    @ResponseBody
    public String doStringData(String name,Integer age){
        return "Hello SpringMVC 返回对象,表示数据";
    }

六、< url-pattern/ >

我的理解是没有加这个< url-pattern/ >标签是,所有的前端请求都是交给Tomcat服务器处理的。
加了< url-pattern/ >标签后,有一部分请求会交给我们的spring框架处理。
其中:
1、< url-pattern >*.do< /url-pattern >会将 *.do的请求交给spring框架处理,其他请求还是由spring处理。
2、< url-pattern >/< /url-pattern >会将所有请求都交给spring框架处理,当然这回导致一些问题
在web.xml文件中配置

http: //localhost:8080/05_url_pattern/index.jsp		tomcat
http: //localhost:8080/05_url_pattern/images/1.jpg	  tomcat
http: //localhost:8080/05_url_pattern/html/test.html	tomcat
http: //localhost:8080/05_url_pattern/some.do	springmvc框架(DispatcherServlet)
<servlet-mapping>
        <servlet-name>myweb</servlet-name>
        <!--
            使用框架的时候, url-pattern可以使用两种值
            1. 使用扩展名方式, 语法 *.xxxx , xxxx是自定义的扩展名。 常用的方式 *.do, *.action, *.mvc等等
               不能使用 *.jsp
               http://localhost:8080/myweb/some.do
               http://localhost:8080/myweb/other.do

            2.使用斜杠 "/"
              当你的项目中使用了  / ,它会替代 tomcat中的default。
              导致所有的静态资源都给DispatcherServlet处理, 默认情况下DispatcherServlet没有处理静态资源的能力。
              没有控制器对象能处理静态资源的访问。所以静态资源(html,js,图片,css)都是404.

              动态资源some.do是可以访问,的因为我们程序中有MyController控制器对象,能处理some.do请求。

        -->
        <!--<url-pattern>*.do</url-pattern>-->
        <url-pattern>/</url-pattern>
    </servlet-mapping>

解决方法一:在springmvc.xml文件中使用< mvc:default-servlet-handler > 和 < mvc:annotation-driven/ >
这两个标签需要配合使用

<!-- default-servlet-handler 和 @RequestMapping注解 有冲突, 需要加入annotation-driven 解决问题-->
    <mvc:annotation-driven />

    <!--第一种处理静态资源的方式:
        需要在springmvc配置文件加入 <mvc:default-servlet-handler>
        原理是: 加入这个标签后,框架会创健控制器对象DefaultServletHttpRequestHandler(类似我们自己创建的MyController).
        DefaultServletHttpRequestHandler这个对象可以把接收的请求转发给 tomcat的default这个servlet。
    -->
    <mvc:default-servlet-handler />
</beans>

解决方法二:在springmvc.xml文件中使用 < mvc:resources />和<mvc:annotation-driven /> 需要掌握
在 Spring3.0 版本后,Spring 定义了专门用于处理静态资源访问请求的处理器ResourceHttpRequestHandler。并且添加了< mvc:resources/ >标签,专门用于解决静态资源无法访问问题。需要在 springmvc 配置文件中添加如下形式的配置:

<!--mvc:resources和@RequestMapping有一定的冲突-->
    <mvc:annotation-driven />
<!--第二种处理静态资源的方式
        mvc:resources 加入后框架会创建 ResourceHttpRequestHandler这个处理器对象。
        让这个对象处理静态资源的访问,不依赖tomcat服务器。
        mapping:访问静态资源的uri地址, 使用通配符 **
        location:静态资源在你的项目中的目录位置。

        images/**:表示 images/p1.jpg  , images/user/logo.gif , images/order/history/list.png
    -->
    <mvc:resources mapping="/images/**" location="/images/" />
    <mvc:resources mapping="/html/**" location="/html/" />
    <mvc:resources mapping="/js/**" location="/js/" />
    <!--使用一个配置语句,指定多种静态资源的访问-->
    <!--为了方便,一般把静态资源统一放在 static 文件夹下-->
    <!--<mvc:resources mapping="/static/**" location="/static/" />-->

七、请求转发和重定向

当处理器对请求处理完毕后,向其它资源进行跳转时,有两种跳转方式:请求转发与重定向。而根据所要跳转的资源类型,又可分为两类:跳转到页面与跳转到其它处理器。
注意,对于请求转发的页面,可以是WEB-INF中页面;而重定向的页面,是不能为WEB-INF中页的。因为重定向相当于用户再次发出一次请求,而用户是不能直接访问 WEB-INF 中资源的。
在这里插入图片描述
SpringMVC 框架把原来 Servlet 中的请求转发和重定向操作进行了封装。现在可以使用简单的方式实现转发和重定向。
forward:表示转发,实现 request.getRequestDispatcher(“xx.jsp”).forward()
redirect:表示重定向,实现 response.sendRedirect(“xxx.jsp”)

1、请求转发

处理器方法返回 ModelAndView 时,需在 setViewName()指定的视图前添加 forward:,且此时的视图不再与视图解析器一同工作,这样可以在配置了解析器时指定不同位置的视图。
视图页面必须写出相对于项目根的路径。forward 操作不需要视图解析器。
处理器方法返回 String,在视图路径前面加入 forward: 视图完整路径。

2、请求重定向

在处理器方法返回的视图字符串的前面添加 redirect:,则可实现重定向跳转。

package com.bjpowernode.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.xml.ws.RequestWrapper;

/**
 * @RequestMapping:
 *    value : 所有请求地址的公共部分,叫做模块名称
 *    位置: 放在类的上面
 */
@Controller
public class MyController {


    /**
     * 处理器方法返回ModelAndView,实现转发forward
     * 语法: setViewName("forward:视图文件完整路径")
     * forward特点: 不和视图解析器一同使用,就当项目中没有视图解析器
     */
    @RequestMapping(value = "/doForward.do")
    public ModelAndView doSome(){
        //处理some.do请求了。 相当于service调用处理完成了。
        ModelAndView mv  = new ModelAndView();
        mv.addObject("msg","欢迎使用springmvc做web开发");
        mv.addObject("fun","执行的是doSome方法");
        //显示转发
        //mv.setViewName("forward:/WEB-INF/view/show.jsp");

        mv.setViewName("forward:/hello.jsp");
        return mv;
    }


    /**
     * 处理器方法返回ModelAndView,实现重定向redirect
     * 语法:setViewName("redirect:视图完整路径")
     * redirect特点: 不和视图解析器一同使用,就当项目中没有视图解析器
     *
     * 框架对重定向的操作:
     * 1.框架会把Model中的简单类型的数据,转为string使用,作为hello.jsp的get请求参数使用。
     *   目的是在 doRedirect.do 和 hello.jsp 两次请求之间传递数据
     *
     * 2.在目标hello.jsp页面可以使用参数集合对象 ${param}获取请求参数值
     *    ${param.myname}
     *
     * 3.重定向不能访问/WEB-INF资源
     */
    @RequestMapping(value = "/doRedirect.do")
    public ModelAndView doWithRedirect(String name,Integer age){
        //处理some.do请求了。 相当于service调用处理完成了。
        ModelAndView mv  = new ModelAndView();
        //数据放入到 request作用域
        mv.addObject("myname",name);
        mv.addObject("myage",age);
        //重定向
        //mv.setViewName("redirect:/hello.jsp");
        //http://localhost:8080/ch08_forard_redirect/hello.jsp?myname=lisi&myage=22

        //重定向不能访问/WEB-INF资源
        mv.setViewName("redirect:/hello.jsp");
        return mv;
    }
}

八、异常处理

springmvc框架采用的是统一,全局的异常处理。把controller中的所有异常处理都集中到一个地方,采用的是aop思想,把业务逻辑和异常处理代码分开。也叫解耦(ou)合。
使用两个注解:
1、@ExceptionHandler
使用注解@ExceptionHandler 可以将一个方法指定为异常处理方法。该注解只有一个可选属性 value,为一个 Class<?>数组,用于指定该注解的方法所要处理的异常类,即所要匹配的异常。
而被注解的方法,其返回值可以是 ModelAndView、String,或 void,方法名随意,方法参数可以是 Exception 及其子类对象、HttpServletRequest、HttpServletResponse 等。系统会自动为这些方法参数赋值。
对于异常处理注解的用法,也可以直接将异常处理方法注解于 Controller 之中。
2、ControllerAdvice

控制器增强(也就是说给控制器类增加功能–异常处理功能)
位置:在类的上面。
特点:必须让框架知道这个注解所在的包名,需要在springmvc配置文件声明组件扫描器。指定@ControllerAdvice所在的包名

异常处理主要步骤:

1 新建一个自定义异常类 MyUserException,再定义它的子类NameException,AgeException
public class MyUserException extends Exception {
    //这是重写Exception的无参构造方法
    public MyUserException() {
        super();
    }
    //这是重写Exception的有参(字符串参数)构造方法
    public MyUserException(String message) {
        super(message);
    }
}
/*================================================*/

//表示当用户的姓名有异常,抛出NameException
public class NameException extends MyUserException {
    public NameException() {
        super();
    }

    public NameException(String message) {
        super(message);
    }
}

/*================================================*/
//当年龄有问题时,抛出的异常
public class AgeException extends MyUserException {
    public AgeException() {
        super();
    }

    public AgeException(String message) {
        super(message);
    }
}

2 在 controller 抛出 NameException,AgeException
import com.bjpowernode.exception.AgeException;
import com.bjpowernode.exception.MyUserException;
import com.bjpowernode.exception.NameException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.xml.ws.RequestWrapper;

/**
 * @RequestMapping:
 *    value : 所有请求地址的公共部分,叫做模块名称
 *    位置: 放在类的上面
 */
@Controller
public class MyController {

    @RequestMapping(value = "/some.do")
    public ModelAndView doSome(String name,Integer age) throws MyUserException {
        //处理some.do请求了。 相当于service调用处理完成了。
        ModelAndView mv  = new ModelAndView(); 
            //根据请求参数抛出异常
            if (!"zs".equals(name)) {
                throw new NameException("姓名不正确!!!");
            }

            if (age == null || age > 80) {
                throw new AgeException("年龄比较大!!!");
            }
        mv.addObject("myname",name);
        mv.addObject("myage",age);
        mv.setViewName("show");
        return mv;
    }


}

3 创建一个普通类,作为 全局异常处理类
	1>	类上面加上 @ControllerAdvice
	2>	类中方法上面加上 @ExceptionHandler
package com.bjpowernode.handler;

import com.bjpowernode.exception.AgeException;
import com.bjpowernode.exception.NameException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

/**
 * @ControllerAdvice : 控制器增强(也就是说给控制器类增加功能--异常处理功能)
 *           位置:在类的上面。
 *  特点:必须让框架知道这个注解所在的包名,需要在springmvc配置文件声明组件扫描器。
 *  指定@ControllerAdvice所在的包名
 */
@ControllerAdvice
public class GlobalExceptionHandler {
    //定义方法,处理发生的异常
    /*
        处理异常的方法和控制器方法的定义一样, 可以有多个参数,可以有ModelAndView,
        String, void,对象类型的返回值

        形参:Exception,表示Controller中抛出的异常对象。
        通过形参可以获取发生的异常信息。

        @ExceptionHandler(异常的class):表示异常的类型,当发生此类型异常时,
        由当前方法处理
     */

    @ExceptionHandler(value = NameException.class)
    public ModelAndView doNameException(Exception exception){
        //处理NameException的异常。
        /*
           异常发生处理逻辑:
           1.需要把异常记录下来, 记录到数据库,日志文件。
             记录日志发生的时间,哪个方法发生的,异常错误内容。
           2.发送通知,把异常的信息通过邮件,短信,微信发送给相关人员。
           3.给用户友好的提示。
         */
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","姓名必须是zs,其它用户不能访问");
        mv.addObject("ex",exception);
        mv.setViewName("nameError");
        return mv;
    }


    //处理AgeException
    @ExceptionHandler(value = AgeException.class)
    public ModelAndView doAgeException(Exception exception){
        //处理AgeException的异常。
        /*
           异常发生处理逻辑:
           1.需要把异常记录下来, 记录到数据库,日志文件。
             记录日志发生的时间,哪个方法发生的,异常错误内容。
           2.发送通知,把异常的信息通过邮件,短信,微信发送给相关人员。
           3.给用户友好的提示。
         */
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","你的年龄不能大于80");
        mv.addObject("ex",exception);
        mv.setViewName("ageError");
        return mv;
    }

    //处理其它异常, NameException, AgeException以外,不知类型的异常
    @ExceptionHandler
    public ModelAndView doOtherException(Exception exception){
        //处理其它异常
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","你的年龄不能大于80");
        mv.addObject("ex",exception);
        mv.setViewName("defaultError");
        return mv;
    }
}

4 定义异常响应页面

在这里插入图片描述

5 定义 Spring 配置文件

在这里插入图片描述

九、拦截器

SpringMVC 中的 Interceptor 拦截器是非常重要和相当有用的,它的主要作用是拦截指定的用户请求,并进行相应的预处理与后处理。其拦截的时间点在“处理器映射器根据用户提交的请求映射出了所要执行的处理器类,并且也找到了要执行该处理器类的处理器适配器,在处理器适配器执行处理器之前”。当然,在处理器映射器映射出所要执行的处理器类时,已经将拦截器与处理器组合为了一个处理器执行链,并返回给了中央调度器。

1 拦截器是springmvc中的一种,需要实现HandlerInterceptor接口
2 拦截器和过滤器类似,功能方向侧重点不同。

  • 过滤器是用来过滤请求参数,设置编码字符集等。
  • 拦截器是拦截用户的请求,可以对多个Controller做拦截。

3 拦截器是全局的,可以对多个Controller做拦截。

  • 一个项目中可以有0个或多个拦截器,他们在一起拦截用户的请求。

4 拦截器常用在:用户登录处理,权限检查,记录日志。

拦截器的使用步骤:

1 定义普通类实现HandlerInterceptor接口,实现接口的三个方法

package com.bjpowernode.handler;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

//拦截器类:拦截用户的请求。
public class MyInterceptor implements HandlerInterceptor {

    private long btime = 0;
    /*
     * preHandle叫做预处理方法。
     *   重要:是整个项目的入口,门户。 当preHandle返回true 请求可以被处理。
     *        preHandle返回false,请求到此方法就截止。
     *
     * 参数:
     *  Object handler : 被拦截的控制器对象
     * 返回值boolean
     *   true:请求是通过了拦截器的验证,可以执行处理器方法。
         *   拦截器的MyInterceptor的preHandle()
             =====执行MyController中的doSome方法=====
             拦截器的MyInterceptor的postHandle()
             拦截器的MyInterceptor的afterCompletion()
         *
     *   false:请求没有通过拦截器的验证,请求到达拦截器就截止了。 请求没有被处理
     *      拦截器的MyInterceptor的preHandle()
     *
     *
     *  特点:
     *   1.方法在控制器方法(MyController的doSome)之前先执行的。
     *     用户的请求首先到达此方法
     *
     *   2.在这个 方法中可以获取请求的信息, 验证请求是否符合要求。
     *     可以验证用户是否登录, 验证用户是否有权限访问某个连接地址(url)。
     *      如果验证失败,可以截断请求,请求不能被处理。
     *      如果验证成功,可以放行请求,此时控制器方法才能执行。
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        btime = System.currentTimeMillis();
        System.out.println("拦截器的MyInterceptor的preHandle()");
        //计算的业务逻辑,根据计算结果,返回true或者false
        //给浏览器一个返回结果
        //request.getRequestDispatcher("/tips.jsp").forward(request,response);
        return true;
    }

    /*
       postHandle:后处理方法。
       参数:
        Object handler:被拦截的处理器对象MyController
        ModelAndView mv:处理器方法的返回值

        特点:
         1.在处理器方法之后执行的(MyController.doSome())
         2.能够获取到处理器方法的返回值ModelAndView,可以修改ModelAndView中的
         数据和视图,可以影响到最后的执行结果。
         3.主要是对原来的执行结果做二次修正,

         ModelAndView mv = MyController.doSome();
         postHandle(request,response,handler,mv);
     */
    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response,
                           Object handler, ModelAndView mv) throws Exception {
        System.out.println("拦截器的MyInterceptor的postHandle()");
        //对原来的doSome执行结果,需要调整。
        if( mv != null){
            //修改数据
            mv.addObject("mydate",new Date());
            //修改视图
            mv.setViewName("other");
        }
    }

    /*
      afterCompletion:最后执行的方法
      参数
        Object handler:被拦截器的处理器对象
        Exception ex:程序中发生的异常
      特点:
       1.在请求处理完成后执行的。框架中规定是当你的视图处理完成后,对视图执行了forward。就认为请求处理完成。
       2.一般做资源回收工作的, 程序请求过程中创建了一些对象,在这里可以删除,把占用的内存回收。
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                                Object handler, Exception ex) throws Exception {
        System.out.println("拦截器的MyInterceptor的afterCompletion()");

        long etime = System.currentTimeMillis();
        System.out.println("计算从preHandle到请求处理完成的时间:"+(etime - btime ));
    }
}

2 在springmvc文件中,声明拦截器,指定拦截请求的uri地址

<!--声明拦截器:拦截器可以有0个或者多个-->
<mvc:interceptors>

    <!--声明一个拦截器-->
    <mvc:interceptor>
        <!--
        指定拦截请求的uri地址
            path:就是uri地址,可以使用通配符 **
              **:表示任意字符,文件或者多级目录中的文件
        -->
        <mvc:mapping path="/**"/>
        <!--声明拦截器对象-->
        <bean class="com.zh.handler.MyInterceptor"/>
    </mvc:interceptor>
    
</mvc:interceptors>

<!--声明拦截器: 拦截器可以有0或多个
        在框架中保存多个拦截器是ArrayList,
        按照声明的先后顺序放入到ArrayList
    -->
    <mvc:interceptors>
        <!--声明第一个拦截器-->
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <!--声明拦截器对象-->
            <bean class="com.bjpowernode.handler.MyInterceptor" />
        </mvc:interceptor>
        <!--声明第二个拦截器-->
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.bjpowernode.handler.MyInterceptor2" />
        </mvc:interceptor>
        <!--声明第三个拦截器-->
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.bjpowernode.handler.MyInterceptor3" />
        </mvc:interceptor>
    </mvc:interceptors>

拦截器的执行顺序:

在这里插入图片描述
从图中可以看出,只要有一个 preHandle()方法返回 false,则上部的执行链将被断开,其后续的处理器方法与 postHandle()方法将无法执行。但,无论执行链执行情况怎样,只要方法栈中有方法,即执行链中只要有 preHandle()方法返回 true,就会执行方法栈中的afterCompletion()方法。最终都会给出响应。

假如有三个拦截器
1、
拦截器1:true
拦截器2:true
拦截器3:true
在这里插入图片描述
2、
拦截器1:false
拦截器2:true
拦截器3:true
在这里插入图片描述
3、
拦截器1:true
拦截器2:false
拦截器3:true
在这里插入图片描述
看到这里应该可以理解了吧

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值