springmvc教程

需求: 用户在页面发起一个请求, 请求交给springmvc的控制器对象,
       并显示请求的处理结果(在结果页面显示一个欢迎语句)。


实现步骤:
1. 新建web maven工程
2. 加入依赖
   spring-webmvc依赖,间接把spring的依赖都加入到项目
   jsp,servlet依赖

3.重点: 在web.xml中注册springmvc框架的核心对象DispatcherServlet
   1)DispatcherServlet叫做中央调度器, 是一个servlet, 它的父类是继承HttpServlet
   2)DispatcherServlet页叫做前端控制器(front controller)
   3)DispatcherServlet负责接收用户提交的请求, 调用其它的控制器对象,
                      并把请求的处理结果显示给用户

4.创建一个发起请求的页面 index.jsp

5.创建控制器(处理器)类
  1)在类的上面加入@Controller注解,创建对象,并放入到springmvc容器中
  2)在类中的方法上面加入@RequestMapping注解。

6.创建一个作为结果的jsp,显示请求的处理结果。

7.创建springmvc的配置文件(和spring的配置文件一样)
  1)声明组件扫描器, 指定@Contorller注解所在的包名
  2)声明视图解析器。帮助处理视图的。

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);
        }



        启动tomcat报错,读取这个文件 /WEB-INF/springmvc-servlet.xml(/WEB-INF/myweb-servlet.xml)
        springmvc创建容器对象时,读取的配置文件默认是/WEB-INF/<servlet-name>-servlet.xml .
    -->
    <servlet>
        <servlet-name>myweb</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

        <!--自定义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>

创建控制类

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是一样的
 *  可以使用value指定名称,但是一般不用
 *
 *  能处理请求的都是控制器(处理器): MyController能处理请求,
 *                         叫做后端控制器(back controller)
 *
 *  没有注解之前,需要实现各种不同的接口才能做控制器使用
 */
@Controller
public class MyController {
    /*
       处理用户提交的请求,springmvc中是使用方法来处理的。
       方法是自定义的, 可以有多种返回值, 多种参数,方法名称自定义
     */

    /**
     * 准备使用doSome方法处理some.do请求。
     * 告诉框架使用这个方法来处理some.do请求
     * @RequestMapping: 请求映射,作用是把一个请求地址和一个方法绑定在一起。
     *                  一个请求指定一个方法处理。
     *       属性: 1. value 是一个String,表示请求的uri地址的(some.do)。
     *                value的值必须是唯一的, 不能重复。 在使用时,推荐地址以“/”开头
     *       位置:1.在方法的上面,常用的。
     *            2.在类的上面
     *  说明: 使用RequestMapping修饰的方法叫做处理器方法或者控制器方法。
     *  使用@RequestMapping修饰的方法可以处理请求的,类似Servlet中的doGet, doPost
     *
     *  返回值:ModelAndView 表示本次请求的处理结果
     *  这个是框架提供的类
     *   Model: 数据,请求处理完成后,要显示给用户的数据
     *   View: 视图, 比如jsp等等。
     */
    @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里面
        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;
        
        //声明:我们把数据和视图放到mv里面了,这个mv返回值会在后期做setAttribute,forward处理
        //执行dosome的时候我们把视图和数据放到mv里,框架会在后期帮我们做setAttribute,forward操作,不		//需要我们管
    }


    @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;
    }
}

springmvc处理请求的流程

springmvc请求的处理流程

 1)发起some.do
 2)tomcat(web.xml--url-pattern知道 *.do的请求给DispatcherServlet)
 3)DispatcherServlet(根据springmvc.xml配置知道 some.do---doSome())
 4)DispatcherServlet把some.do转发给MyController.doSome()方法
 5)然后由doSome方法来处理请求
 6)框架执行doSome()把得到ModelAndView进行处理, 转发到show.jsp

springmvc执行过程源代码分析

springmvc执行过程源代码分析
1. tomcat启动,创建容器的过程
   通过load-on-start标签指定的1,创建DisaptcherServlet对象, 
   DisaptcherServlet它的父类是继承HttpServlet的, 它是一个serlvet, 在被创建时,会执行init()方法。
   在init()方法中
   //创建容器,读取配置文件
    WebApplicationContext ctx = new ClassPathXmlApplicationContext("springmvc.xml");
    //把容器对象放入到ServletContext中
    getServletContext().setAttribute(key, ctx);

  上面创建容器作用: 创建@controller注解所在的类的对象, 创建MyController对象,
   这个对象放入到 springmvc的容器中, 容器是map , 类似 map.put("myController",MyController对象)

2.请求的处理过程
  1)执行servlet的service()
       protected void service(HttpServletRequest request, HttpServletResponse response)

       protected void doService(HttpServletRequest request, HttpServletResponse response)

      DispatcherServlet.doDispatch(request, response){

          调用MyController的.doSome()方法
      }

   doDispatch:springmvc中DispatcherServlet的核心方法, 所有的请求都在这个方法中完成的。

spring配置文件

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

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

RequestMapping注解详解

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

    /**
     * @RequestMapping : 请求映射
     *             属性: method, 表示请求的方式。 它的值RequestMethod类枚举值。
     *					通过这个属性可以指定你需要使用什么请求方式来访问
     *                    例如表示get请求方式, RequestMethod.GET
     *                    post方式, RequestMethod.POST
     *
     *  你不用get方式,错误是:
     *  HTTP Status 405 - Request method 'GET' not supported
     */
    //指定some.do使用get请求方式
    @RequestMapping(value = "/some.do",method = RequestMethod.GET)
    public ModelAndView doSome(){  // doGet()--service请求处理
        //处理some.do请求了。 相当于service调用处理完成了。
        ModelAndView mv  = new ModelAndView();
        mv.addObject("msg","欢迎使用springmvc做web开发");
        mv.addObject("fun","执行的是doSome方法");
        mv.setViewName("show");
        return mv;
    }


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

    //不指定请求方式,没有限制
    @RequestMapping(value = "/first.do")
    public ModelAndView doFirst(HttpServletRequest request,
                                HttpServletResponse response,
                                HttpSession session){
        ModelAndView mv  = new ModelAndView();
        mv.addObject("msg","====欢迎使用springmvc做web开发====" + request.getParameter("name"));
        mv.addObject("fun","执行的是doFirst方法");
        mv.setViewName("other");
        return mv;
    }
}

接收请求参数

逐个接收请求参数
RequestParam注解
对象接收参数
/**
 * @RequestMapping:
 *    value : 所有请求地址的公共部分,叫做模块名称
 *    位置: 放在类的上面
 */
@Controller
public class MyController {


    /**
     * 逐个接收请求参数:
     *   要求: 处理器(控制器)方法的形参名和请求中参数名必须一致。
     *          同名的请求参数赋值给同名的形参
     * 框架接收请求参数
     *   1. 使用request对象接收请求参数
     *      String strName = request.getParameter("name");
     *      String strAge = request.getParameter("age");
     *   2. springmvc框架通过 DispatcherServlet 调用 MyController的doSome()方法
     *      调用方法时,按名称对应,把接收的参数赋值给形参
     *      doSome(strName,Integer.valueOf(strAge))
     *      框架会提供类型转换的功能,能把String转为 int ,long , float, double等类型。
     *
     *		这里需要注意的是doSome方法是由框架来执行的,所以参数是由框架解析出来后再调用doSome方法传过		*	   去的
     *
     *  400状态码是客户端错误, 表示提交请求参数过程中,发生了问题。
     *  以下面的例子为例,接收一个整型的age参数,如果传过来的参数是一个空字符串,这时框架也会把它转成整型	   *  数。因此会报400错误,为了程序更加健壮,我们可以把基本数据类型都定义成包装数据类型
     */
    @RequestMapping(value = "/receiveproperty.do")
    public ModelAndView doSome(String name, Integer age){
        System.out.println("doSome, name="+name+"   age="+age);
        //可以在方法中直接使用 name , age
        //处理some.do请求了。 相当于service调用处理完成了。
        ModelAndView mv  = new ModelAndView();
        mv.addObject("myname",name);
        mv.addObject("myage",Integer.valueOf(age));
        //show是视图文件的逻辑名称(文件名称)
        mv.setViewName("show");
        return mv;
    }

    /**
     * 请求中参数名和处理器方法的形参名不一样
     * @RequestParam: 逐个接收请求参数中, 解决请求中参数名形参名不一样的问题
     *      属性: 1. value 请求中的参数名称
     *            2. required 是一个boolean,默认是true
     *                true:表示请求中必须包含此参数。
     *      位置: 在处理器方法的形参定义的前面
     */
    @RequestMapping(value = "/receiveparam.do")
    public ModelAndView receiveParam(@RequestParam(value = "rname",required = false) String name,
                                     @RequestParam(value = "rage",required = false) Integer age){
        System.out.println("doSome, name="+name+"   age="+age);
        //可以在方法中直接使用 name , age
        //处理some.do请求了。 相当于service调用处理完成了。
        ModelAndView mv  = new ModelAndView();
        mv.addObject("myname",name);
        mv.addObject("myage",age);
        //show是视图文件的逻辑名称(文件名称)
        mv.setViewName("show");
        return mv;
    }


    /**
     * 处理器方法形参是java对象, 这个对象的属性名和请求中参数名一样的
     * 框架会创建形参的java对象, 给属性赋值。 请求中的参数是name,框架会调用setName()
     * 和set注入一样
     * @return
     */
    @RequestMapping(value = "/receiveobject.do")
    public ModelAndView receiveParam( Student myStudent){
        System.out.println("receiveParam, name="+myStudent.getName()+"   age="+myStudent.getAge());
        //可以在方法中直接使用 name , age
        //处理some.do请求了。 相当于service调用处理完成了。
        ModelAndView mv  = new ModelAndView();
        mv.addObject("myname",myStudent.getName());
        mv.addObject("myage",myStudent.getAge());
        mv.addObject("mystudent",myStudent);
        //show是视图文件的逻辑名称(文件名称)
        mv.setViewName("show");
        return mv;
    }

}

注意:

在提交请求参数时,get请求方式中文没有乱码。

使用post方式提交请求,中文有乱码,需要使用过滤器处理乱码的问题。

过滤器可以自定义,也可使用框架中提供的过滤器 CharacterEncodingFilter

CharacterEncodingFilter类是在spring-web包下的

在web.xml文件下配置

<!--注册声明过滤器,解决post请求乱码的问题-->
    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<!--需要指定属性值-->
        <!--设置项目中使用的字符编码-->
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
        <!--强制请求对象(HttpServletRequest)使用encoding编码的值-->
        <init-param>
            <param-name>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <!--强制应答对象(HttpServletResponse)使用encoding编码的值-->
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <!--
           /*:表示强制所有的请求先通过过滤器处理。
        -->
        <url-pattern>/*</url-pattern>
    </filter-mapping>

filter过滤器核心源码

String encoding = this.getEncoding();
        if (encoding != null) {
            if (this.isForceRequestEncoding() || request.getCharacterEncoding() == null) {
                request.setCharacterEncoding(encoding);
            }

            if (this.isForceResponseEncoding()) {
                response.setCharacterEncoding(encoding);
            }
        }

所以CharacterEncodingFilter的底层也是执行了setCharacterEncoding("utf-8")

返回值

处理器方法的返回值表示请求的处理结果
1.ModelAndView: 有数据和视图,对视图执行forward。
2.String:表示视图,可以逻辑名称,也可以是完整视图路径
3.void: 不能表示数据,也不能表示视图。
  在处理ajax的时候,可以使用void返回值。 通过HttpServletResponse输出数据。响应ajax请求。
  ajax请求服务器端返回的就是数据, 和视图无关。
  返回void是可以通过response来输出数据的
4.Object: 例如String , Integer , Map,List, Student等等都是对象,
  对象有属性, 属性就是数据。 所以返回Object表示数据, 和视图无关。
  可以使用对象表示的数据,响应ajax请求。

  现在做ajax, 主要使用json的数据格式。 实现步骤:
   1.加入处理json的工具库的依赖, springmvc默认使用的jackson。
   2.在sprigmvc配置文件之间加入 <mvc:annotation-driven> 注解驱动。
     json  = om.writeValueAsString(student);  ---> jackson库
     (实现把一个Java对象转成json)
     json = JSON.toJSONString(Object obj)  ---> fastjson库
   3.在处理器方法的上面加入@ResponseBody注解
       response.setContentType("application/json;charset=utf-8");
       PrintWriter pw  = response.getWriter();
       pw.println(json);
       把json字符串输出到浏览器

  springmvc处理器方法返回Object, 可以转为json输出到浏览器,响应ajax的内部原理
  1. <mvc:annotation-driven> 注解驱动。
     注解驱动实现的功能是 完成java对象到json,xml, text,二进制等数据格式的转换。
     <mvc:annotation-driven>在加入到springmvc配置文件后, 会自动创建HttpMessageConverter接口
     的7个实现类对象, 包括 MappingJackson2HttpMessageConverter (使用jackson工具库中的ObjectMapper实现java对象转为json字符串)

	注解驱动是通过HttpMessageConverter接口来完成数据格式的转换的
     HttpMessageConverter接口:消息转换器。
     功能:定义了java转为json,xml等数据格式的方法。 这个接口有很多的实现类。
           这些实现类完成 java对象到json, java对象到xml,java对象到二进制数据的转换

     下面的两个方法是控制器类把结果输出给浏览器时使用的:
     boolean canWrite(Class<?> var1, @Nullable MediaType var2);
     void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3)


     例如处理器方法
     @RequestMapping(value = "/returnString.do")
     public Student doReturnView2(HttpServletRequest request,String name, Integer age){
             Student student = new Student();
             student.setName("lisi");
             student.setAge(20);
             return student;
     }

     1)canWrite作用检查处理器方法的返回值,能不能转为var2表示的数据格式。
       检查student(lisi,20)能不能转为var2表示的数据格式。如果检查能转为json,canWrite返回true
       MediaType:表示数格式的, 例如json, xml等等(默认是先转成json)

     2)write:把处理器方法的返回值对象,调用jackson中的ObjectMapper转为json字符串。
        json  = om.writeValueAsString(student);


 2. @ResponseBody注解
   放在处理器方法的上面, 通过HttpServletResponse输出数据,响应ajax请求的。
           PrintWriter pw  = response.getWriter();
           pw.println(json);
           pw.flush();
           pw.close();
返回String
/**
 * @RequestMapping:
 *    value : 所有请求地址的公共部分,叫做模块名称
 *    位置: 放在类的上面
 */
@Controller
public class MyController {


    /**
     * 处理器方法返回String--表示逻辑视图名称,需要配置视图解析器
     */
    @RequestMapping(value = "/returnString-view.do")
    public String doReturnView(HttpServletRequest request,String name, Integer age){
        System.out.println("doReturnView, 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转发操作
        // /WEB-INF/view//WEB-INF/view/show.jsp.jsp
        // /WEB-INF/view/WEB-INF/view/show.jsp.jsp
        return "/WEB-INF/view/show.jsp";
    }

    //处理器方法返回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();

    }


    /**
     * 处理器方法返回一个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就是数据,反之就是视图
     *
     * 浏览器在接收到这个数据后会使用ISO-8859-1的编码去解析数据,所以会乱码
     * 默认使用“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 返回对象,表示数据";
    }

}

/*用了框架后就只需要关心处理器方法返回的对象是什么就行了,框架会自动把这个对象转换成json格式响应给客户端
  你只需要关心业务功能就可以了,关心怎样拿到最终的结果,其他的辅助功能框架都实现了*/

在框架中返回对象代表着json数据,代表的是ajax请求处理

加注解驱动的区别

没有加入注解驱动标签时的状态
org.springframework.http.converter.ByteArrayHttpMessageConverter 
org.springframework.http.converter.StringHttpMessageConverter
org.springframework.http.converter.xml.SourceHttpMessageConverter
org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter


加入注解驱动标签时的状态
org.springframework.http.converter.ByteArrayHttpMessageConverter
org.springframework.http.converter.StringHttpMessageConverter
org.springframework.http.converter.ResourceHttpMessageConverter
org.springframework.http.converter.ResourceRegionHttpMessageConverter
org.springframework.http.converter.xml.SourceHttpMessageConverter 
org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter 
org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter

只有加上注解驱动后,springmvc才会帮我们创建MappingJackson2HttpMessageConverter的对象,这样它才能将Java对象转换成json格式的字符串

中央调度器DispatcherServlet的url-pattern

发起的请求是由哪些服务器程序处理的。

http://localhost:8080/ch05_url_pattern/index.jsp :tomcat(jsp会转为servlet)
http://localhost:8080/ch05_url_pattern/js/jquery-3.4.1.js : tomcat
http://localhost:8080/ch05_url_pattern/images/p1.jpg : tomcat
http://localhost:8080/ch05_url_pattern/html/test.html: tomcat
http://localhost:8080/ch05_url_pattern/some.do :  DispatcherServlet(springmvc框架处理的)


tomcat本身能处理静态资源的访问, 像html, 图片, js文件都是静态资源


tomcat的web.xml文件有一个servlet 名称是 default , 在服务器启动时创建的。
 <servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

	  <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/</url-pattern>  表示静态资源和未映射的请求都这个default处理
    </servlet-mapping>


default这个servlet作用: 
The default servlet for all web applications, that serves static  
resources.  It processes all requests that are not mapped to other  
servlets with servlet mappings (defined either here or in your own   
web.xml file).

1.处理静态资源
2.处理未映射到其它servlet的请求。
在url-pattern中使用/作为路径

结论:如果直接使用了/作为路径,那么静态资源都将访问不到

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

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

解决访问不到静态资源的方案

方法一:

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

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

<!--这种方式依赖于tomcat服务器,因为底层还是把请求转发给tomcat的default的servlet-->

方法二

<!--第二种处理静态资源的方式
        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/" />

    <!--mvc:resources和@RequestMapping有一定的冲突-->
    <mvc:annotation-driven />

    <!--使用一个配置语句,指定多种静态资源的访问-->
    <!--<mvc:resources mapping="/static/**" location="/static/" />-->

什么时候需要配置静态资源

只有当你给url-pattern使用了/作为路径才需要配置静态资源

绝对路径和相对路径

在jsp , html中使用的地址, 都是在前端页面中的地址,都是相对地址

地址分类:
 1.绝对地址 , 带有协议名称的是绝对地址,  http://www.baidu.com , ftp://202.122.23.1
 2.相对地址, 没有协议开头的, 例如 user/some.do  , /user/some.do
              相对地址不能独立使用,必须有一个参考地址。 通过参考地址+相对地址本身才能指定资源。

				  张三同学, 1班有张三, 2班也有张三

 3.参考地址
    1) 在你的页面中的,访问地址不加 "/"

	 访问的是: http://localhost:8080/ch06_path/index.jsp
      路径: http://localhost:8080/ch06_path/
		资源: index.jsp

    在index.jsp发起 user/some.do请求,访问地址变为 http://localhost:8080/ch06_path/user/some.do
	   当你的地址 没有斜杠开头,例如 user/some.do , 当你点击链接时, 访问地址是当前页面的地址
		加上链接的地址。
      http://localhost:8080/ch06_path/ + user/some.do


     -------------------------------------------------------------
	  index.jsp  访问 user/some.do  , 返回后现在的地址: http://localhost:8080/ch06_path/user/some.do

	  http://localhost:8080/ch06_path/user/some.do
	  路径:	  http://localhost:8080/ch06_path/user/
	  资源:   some.do

	  在index.jsp在 user/some.do ,就变为 http://localhost:8080/ch06_path/user/user/some.do

	  解决方案:
	   1.加入${pageContext.request.contextPath}
		2.加入一个base标签, 是html语言中的标签。 表示当前页面中访问地址的基地址。
		  你的页面中所有 没有“/”开头的地址,都是以base标签中的地址为参考地址
        使用base中的地址 + user/some.do 组成访问地址
        <base href="地址" />


   2)在你的页面中的,访问地址加 "/"
      访问的是: http://localhost:8080/ch06_path/index.jsp
      路径: http://localhost:8080/ch06_path/
		资源: index.jsp

		点击 /user/some.do, 访问地址变为 http://localhost:8080/user/some.do
		参考地址是 你的服务器地址, 也就是 http://localhost:8080


		如果你的资源不能访问: 加入${pageContext.request.contextPath}
		<a href="${pageContext.request.contextPath}/user/some.do">发起user/some.do的get请求</a>


index.jsp--addStudent.jsp---student/addStudent.do( service的方法,调用dao的方法)--result.jsp

注意:地址不加/的时候如果参考的是当前页面的地址有时候是会出问题的,例如有使用到请求转发的时候,请求转发是一次请求的。

@RequestMapping(value = "/user/some.do")
    public ModelAndView doSome(){  // doGet()--service请求处理
        //处理some.do请求了。 相当于service调用处理完成了。
        ModelAndView mv  = new ModelAndView();
        mv.addObject("msg","欢迎使用springmvc做web开发");
        mv.addObject("fun","执行的是doSome方法");
        mv.setViewName("/index.jsp");
        return mv;
//上面这段代码是从index.jsp页面点击过来,而又响应index.jsp页面回去,但此时使用的是请求转发,因此响应回去后它的地址依然是/user/some.do,而此时再点击就会变成/user/user/some.do了,因为它参考的是/user这个地址

base标签的优化

当我们在使用base标签时最好不要把代码写死,这样很难维护

<%
    String basePath = request.getScheme() + "://" +
            request.getServerName() + ":" + request.getServerPort() +
            request.getContextPath() + "/";
%>
<html>
<head>
    <title>Title</title>
    <base href="<%=basePath%>" />
</head>

SSM整合

SSM框架实际上对应着三层

整合思路:

SSM整合开发。
SSM: SpringMVC + Spring + MyBatis.

SpringMVC:视图层,界面层,负责接收请求,显示处理结果的。
Spring:业务层,管理service,dao,工具类对象的。
MyBatis:持久层, 访问数据库的

用户发起请求--SpringMVC接收请求--请求接收到了,调用Spring中的Service对象处理业务--业务需要数据库,使用MyBatis处理数据--数据处理完后交给service--service再交给springmvc--springmvc把结果返回给用户

SSM整合也叫做SSI (IBatis也就是mybatis的前身), 整合中有容器。
1.第一个容器SpringMVC容器, 管理Controller控制器对象的。
2.第二个容器Spring容器,管理Service,Dao,工具类对象的
我们要做的把使用的对象交给合适的容器创建,管理。 把Controller还有web开发的相关对象
交给springmvc容器, 这些web用的对象写在springmvc配置文件中

service,dao对象定义在spring的配置文件中,让spring管理这些对象。

controller对象要怎么访问到service对象呢?如果spring和springmvc是两个独立的容器那肯定是做不到的。

springmvc容器和spring容器是有关系的,关系已经确定好了
springmvc容器是spring容器的子容器, 类似java中的继承。 子可以访问父的内容
在子容器中的Controller可以访问父容器中的Service对象, 就可以实现controller使用service对象

它们的关系本来就已经确定好了,不需要程序员去做任何操作

实现步骤:
0.使用springdb的mysql库, 表使用student(id auto_increment, name, age)
1.新建maven web项目
2.加入依赖
  springmvc,spring,mybatis三个框架的依赖,jackson依赖,mysql驱动,druid连接池
  jsp,servlet依赖

3.写web.xml
  1)注册DispatcherServlet ,目的:1.创建springmvc容器对象,才能创建Controller类对象。
                                2.创建的是Servlet,才能接受用户的请求。

  2)注册spring的监听器:ContextLoaderListener,目的: 创建spring的容器对象,才能创建service,dao等对象。

  3)注册字符集过滤器,解决post请求乱码的问题


4.创建包, Controller包, service ,dao,实体类包名创建好

5.写springmvc,spring,mybatis的配置文件
 1)springmvc配置文件
 2)spring配置文件
 3)mybatis主配置文件
 4)数据库的属性配置文件

6.写代码, dao接口和mapper文件, service和实现类,controller, 实体类。
7.写jsp页面

mysql的版本较高时需要指定UTC(时区)这些信息
第一步:pom文件加依赖
<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <spring.version>5.2.7.RELEASE</spring.version>
<!--    <slf4j.version>1.7.6</slf4j.version>-->
<!--    <log4j.version>1.2.12</log4j.version>-->
    <mysql.version>5.1.49</mysql.version>
    <mybatis.version>3.4.5</mybatis.version>
  </properties>

  <dependencies>
    <!-- spring -->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.5</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.2.14</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>5.2.7.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>${mysql.version}</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.5</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.0</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
    <!-- log start -->
<!--    <dependency>-->
<!--      <groupId>log4j</groupId>-->
<!--      <artifactId>log4j</artifactId>-->
<!--      <version>${log4j.version}</version>-->
<!--    </dependency>-->
<!--    <dependency>-->
<!--      <groupId>org.slf4j</groupId>-->
<!--      <artifactId>slf4j-api</artifactId>-->
<!--      <version>${slf4j.version}</version>-->
<!--    </dependency>-->
<!--    <dependency>-->
<!--      <groupId>org.slf4j</groupId>-->
<!--      <artifactId>slf4j-log4j12</artifactId>-->
<!--      <version>${slf4j.version}</version>-->
<!--    </dependency>-->
    <!-- log end -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>${mybatis.version}</version>
    </dependency>
    <dependency>
      <groupId>javax.annotation</groupId>
      <artifactId>javax.annotation-api</artifactId>
      <version>1.3.2</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.2</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-databind</artifactId>
      <version>2.9.0</version>
    </dependency>
  </dependencies>

  <build>
    <finalName>${project.artifactId}</finalName>

    <resources>
      <resource>
        <directory>src/main/java</directory>
        <includes>
          <include>**/*.properties</include>
          <include>**/*.xml</include>
        </includes>
        <filtering>false</filtering>
      </resource>
      <resource>
        <directory>src/main/resources</directory>
        <includes>
          <include>**/*.properties</include>
          <include>**/*.xml</include>
        </includes>
        <filtering>false</filtering>
      </resource>
    </resources>

    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.2</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
          <encoding>UTF-8</encoding>
        </configuration>
      </plugin>

      <!-- 配置Tomcat插件 -->
      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.2</version>
        <configuration>
          <path>/</path>
          <port>9080</port>
        </configuration>
      </plugin>
    </plugins>
  </build>
第二步:编写web.xml文件
<!--配置中央调度器-->
    <servlet>
        <servlet-name>myweb</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--配置初始化参数,加载springmvc.xml配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <!--配置服务器启动加载-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>myweb</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>

    <!--配置过滤器,解决中文乱码问题-->
    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!--配置监听器,用于启动服务器时加载容器-->
    <!--该监听器只能加载 WEB-INF 目录中名称为 applicationContext.xml 的配置文件-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!--手动指定spring配置文件的位置-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>
第三步:写springmvc配置文件
<!--开启注解驱动扫描,创建控制器对象-->
    <context:component-scan base-package="edu.gdpi.controller" />

    <!--配置视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--jsp文件所在路径-->
        <property name="prefix" value="/WEB-INF/pages/" />
        <!--文件的后缀名-->
        <property name="suffix" value=".jsp" />
    </bean>

    <!--加入注解驱动,处理ajax,静态资源都需要用到-->
    <mvc:annotation-driven />
    <!--
        1.响应ajax请求,返回json
        2.解决静态资源访问路径
    -->
第四步:写spring配置文件
<!--创建druid连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="url" value="jdbc:mysql://localhost:3306/ssm?serverTimezone=GMT%2B8"/>
        <property name="username" value="root" />
        <property name="password" value="sc5170" />
    </bean>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:mybatis.xml"/>
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <property name="basePackage" value="edu.gdpi.dao"/>
    </bean>

    <!-- 开启注解扫描,要扫描的是service和dao层的注解,
要忽略web层注解,因为web层让SpringMVC框架去管理 -->
    <context:component-scan base-package="edu.gdpi.service" />

    <!--事务配置,注解的配置-->
    <tx:annotation-driven />
第五步:写mybatis配置文件
<settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

    <typeAliases>
        <package name="edu.gdpi.pojo"/>
    </typeAliases>

    <mappers>
        <package name="edu.gdpi.dao"/>
    </mappers>

mybatis连接数据库的配置文件

jdbc.url=jdbc:mysql://localhost:3306/jdbc?serverTimezone=GMT%2B8
jdbc.username=root
jdbc.password=sc5170

剩下的就是些写代码了,和平时的差不多

请求重定向和转发

forward:表示转发
redirect:表示重定向

forward和redirect都是关键字, 有一个共同的特点不和视图解析器一同工作

扩展:

forward和redirect他们都可以访问 视图文件,比如某个jsp ,html
 forward:/hello.jsp  forward:/main.html

forward和redirect他们都可以访问其它的controller
 forward:/some.do , redirect:/other.do

处理器方法可以返回ModelAndView, String , void 都可以使用forward,redirect

请求转发

/**
     * 处理器方法返回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:/WEB-INF/view/show.jsp");
        return mv;
    }

异常处理

异常处理:
springmvc框架采用的是统一,全局的异常处理。
把controller中的所有异常处理都集中到一个地方。由这一个地方来处理所有的异常 采用的是aop的思想。把业务逻辑和异常处理代码分开。解耦合。

使用两个注解
1.@ExceptionHandler
2.@ControllerAdvice

异常处理的步骤

异常处理步骤:
1.新建maven web项目
2.加入依赖
3.新建一个自定义异常类 MyUserException , 再定义它的子类NameException ,AgeException
4.在controller抛出NameException , AgeException
5.创建一个普通类,作用全局异常处理类
  1)在类的上面加入@ControllerAdvice
  2) 在类中定义方法,方法的上面加入@ExceptionHandler
6.创建处理异常的视图页面
7.创建springmvc的配置文件
 1)组件扫描器 ,扫描@Controller注解
 2)组件扫描器,扫描@ControllerAdvice所在的包名
 3)声明注解驱动

第一步:创建controller类

@RequestMapping(value = "/some.do")
    public ModelAndView doSome(String name,Integer age) throws MyUserException {
        //处理some.do请求了。 相当于service调用处理完成了。
        ModelAndView mv  = new ModelAndView();

        /**
         * 不用再try catch了,要把异常抛出去,让框架来集中处理这些异常
         * 1.我们需要定义一个普通类,加上两个注解给它
         * @Controller注解
         * @ExceptionHandler注解
         */
        //try {
            //根据请求参数抛出异常
            if (!"zs".equals(name)) {
                throw new NameException("姓名不正确!!!");
            }

            if (age == null || age > 80) {
                throw new AgeException("年龄比较大!!!");
            }

        //}catch(Exception e){
        //   e.printStackTrace();
        //}

        mv.addObject("myname",name);
        mv.addObject("myage",age);
        mv.setViewName("show");
        return mv;
    }

第二步:编写一个普通类,需要在类上加上@ControllerAdvice注解,和在方法上加@ExceptionHandler注解

/**
 * @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以外,不知类型的异常
    //没有value属性的ExceptionHandler只能有一个,如果抛出的异常和所有指定异常都不匹配,那么最后就由这个异常来处理
    @ExceptionHandler
    public ModelAndView doOtherException(Exception exception){
        //处理其它异常
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","你的年龄不能大于80");
        mv.addObject("ex",exception);
        mv.setViewName("defaultError");
        return mv;
    }
}

第三步:编写springmvc配置文件

<!--声明组件扫描器-->
    <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>
    
    
    <!--处理需要的两步-->
    <context:component-scan base-package="com.bjpowernode.handler" />
    <mvc:annotation-driven />

第四步:编写jsp页面显示异常信息...

拦截器

1)拦截器是springmvc中的一种对象,需要实现HandlerInterceptor接口。
2)拦截器和过滤器类似,功能方向侧重点不同。过滤器是用来过滤请求参数,设置编码字符集等工作。
    拦截器是拦截用户的请求,做请求做判断处理的。
3)拦截器是全局的,可以对多个Controller做拦截。 
   一个项目中可以有0个或多个拦截器, 他们在一起拦截用户的请求。
	拦截器常用在:用户登录处理,权限检查, 记录日志。

拦截器的使用步骤:
 1.定义类实现HandlerInterceptor接口
 2.在springmvc配置文件中,声明拦截器, 让框架知道拦截器的存在。

拦截器的执行时间:
  1)在请求处理之前, 也就是controller类中的方法执行之前先被拦截。
  2)在控制器方法执行之后也会执行拦截器。
  3)在请求处理完成后也会执行拦截器。


拦截器:看做是多个Controller中公用的功能,集中到拦截器统一处理。使用的aop的思想

HandlerInterceptor接口在5.2.5中它的方法都已经是默认方法了,也就是说需要用哪一个就重写哪一个
不需要全部重写了,但在之前的版本中这些方法需要全部重写

定义拦截器的实现类

//拦截器类:拦截用户的请求。
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 ));
    }
}

声明拦截器

 <!--声明拦截器: 拦截器可以有0或多个
        在框架中保存多个拦截器是ArrayList,
        按照声明的先后顺序放入到ArrayList
    -->
    <mvc:interceptors>
        <!--声明第一个拦截器-->
        <mvc:interceptor>
            <!--指定拦截的请求uri地址
                path:就是uri地址,可以使用通配符 **
                      ** : 表示任意的字符,文件或者多级目录和目录中的文件
                http://localhost:8080/myweb/user/listUser.do
                http://localhost:8080/myweb/student/addStudent.do
            -->
            <mvc:mapping path="/**"/>
            <!--声明拦截器对象-->
            <bean class="com.bjpowernode.handler.MyInterceptor" />
        </mvc:interceptor>
    </mvc:interceptors>

拦截器执行链

多个拦截器:
第一个拦截器preHandle=true , 第二个拦截器preHandle=true 

111111-拦截器的MyInterceptor的preHandle()
22222-拦截器的MyInterceptor的preHandle()
=====执行MyController中的doSome方法=====
22222-拦截器的MyInterceptor的postHandle()
111111-拦截器的MyInterceptor的postHandle()
22222-拦截器的MyInterceptor的afterCompletion()
111111-拦截器的MyInterceptor的afterCompletion()

---------------------------------------------------
第一个拦截器preHandle=true , 第二个拦截器preHandle=false

111111-拦截器的MyInterceptor的preHandle()
22222-拦截器的MyInterceptor的preHandle()
111111-拦截器的MyInterceptor的afterCompletion()

----------------------------------------------------------
第一个拦截器preHandle=false , 第二个拦截器preHandle=true|false

111111-拦截器的MyInterceptor的preHandle()

多拦截器时,任意一个拦截器返回假,请求方法都不会执行
拦截器和过滤器的区别
1.过滤器是servlet中的对象,  拦截器是框架中的对象
2.过滤器实现Filter接口的对象, 拦截器是实现HandlerInterceptor
3.过滤器是用来设置request,response的参数,属性的,侧重对数据过滤的。
  拦截器是用来验证请求的,能截断请求。
4.过滤器是在拦截器之前先执行的。
5.过滤器是tomcat服务器创建的对象
  拦截器是springmvc容器中创建的对象
6.过滤器是一个执行时间点。
  拦截器有三个执行时间点
7.过滤器可以处理jsp,js,html等等
  拦截器是侧重拦截对Controller的对象。 如果你的请求不能被DispatcherServlet接收, 这个请求不会执行拦截器内容
8.拦截器拦截普通类方法执行,过滤器过滤servlet请求响应

问题

SpringMVC中已经配置了解决中文乱码问题的过滤器后,还会出现中文乱码现象

首先在web.xml中配置过滤器,如下:

  <!--配置解决中文乱码问题的过滤器-->
  <filter>
    <filter-name>characterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

在springmvc中配置过滤器后还需要配置Tomcat,两种情况,一种是在Maven中配置Tomcat,一种本地引入tomcat服务器。

1.Maven中配置Tomcat

在配置中加入<uriEncoding>UTF-8</uriEncoding>

<plugin>
    <groupId>org.apache.tomcat.maven</groupId>
    <artifactId>tomcat7-maven-plugin</artifactId>
    <version>2.2</version>
    <configuration>
        <port>8888</port>
        <uriEncoding>UTF-8</uriEncoding>
    </configuration>
</plugin>

2.本地引入tomcat服务器

在tomcat目录下的\conf\server.xml文件找到Connector标签,配置以下这段代码

<Connector port="8080" protocol="HTTP/1.1" URIEncoding="utf-8" connectionTimeout="20000" redirectPort="8443" /> 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值