3.SpringMVC

SpringMVC

  1. SpringMVC概述:
  • Spring 为展现层提SpringMVC运行流程:

    • 所有请求,前端控制器(DispatcherServlet)收到请求,调用doDispatch进行处理

    • 根据HandlerMapping中保存的请求映射信息找到,处理当前请求的,处理器执行链(包含拦截器)

    • 根据当前处理器找到他的HandlerAdapter(适配器)

    • 拦截器的preHandle先执行

    • 适配器执行目标方法,并返回ModelAndView

    • ModelAttribute注解标注的方法提前运行

    • 执行目标方法的时候(确定目标方法用的参数)

      • 有注解
      • 没注解:
    • 看是否Model、Map以及其他的

    • 如果是自定义类型 1)、从隐含模型中看有没有,如果有就从隐含模型中拿 2)、如果没有,再看是否SessionAttributes标注的属性,如果是从Session中拿,如果拿不到会抛异常 3)、都不是,就利用反射创建对象

    • 拦截器的postHandle执行

    • 处理结果;(页面渲染流程)

      • 如果有异常使用异常解析器处理异常;处理完后还会返回ModelAndView
      • 调用render进行页面渲染
      • 1)、视图解析器根据视图名得到视图对象
      • 2)、视图对象调用render方法;
      • 3)、执行拦截器的afterCompletion;供的基于MVC 设计理念的优秀的 Web 框架,是目前最主流MVC 框架之一
  • Spring3.0 后全面超越 Struts2,成为最优秀的 MVC 框架

  • Spring MVC 通过一套 MVC 注解,让 POJO 成为处理请求的控制器,而无须实现任何接口。

  • 支持 REST 风格URL 请求

  • 采用了松散耦合可插拔组件结构,比其他 MVC 框架更具扩展性和灵活性

  1. HelloWorld
  • 导包

    • SpringMVC是Spring的web模块,所有模块的运行都是依赖核心模块(IOC模块)

      • 核心容器模块

        在这里插入图片描述

  • web模块

在这里插入图片描述

  • 配置

    • web.xml配置

        <!--SpringMvc思想是有一个前端控制器能拦截所有请求,并智能派发;
        		这个前端控制器是一个servlet,应该在web.xml中配置这个servlet来拦截所有请求  -->
        		<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
      	<servlet>
      		<servlet-name>springDispatcherServlet</servlet-name>
      		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      		<init-param>
      		<!--执行SpringMVC配置文件位置  -->
      			<param-name>contextConfigLocation</param-name>
      			<param-value>classpath:springmvc.xml</param-value>
      		</init-param>
      		<!--servlet启动加载,servlet原本是第一次访问创建对象;
      		load-on-startup:服务器启动的时候创建对象,值越小,优先级越高,越先创建对象  -->
      		<load-on-startup>1</load-on-startup>
      	</servlet>
      
      	<!-- Map all requests to the DispatcherServlet for handling -->
      	<servlet-mapping>
      		<servlet-name>springDispatcherServlet</servlet-name>
      		<!--/和/*都是拦截所有请求,
      		/会拦截所有请求,但是不会拦截*.jsp,能保证jsp访问正常
      		/*的范围更大,还会拦截到*.jsp这些请求,一旦拦截jsp页面就不能显示了  -->
      		<url-pattern>/</url-pattern>
      	</servlet-mapping>
      
    • SpringMVC.xml

      <!--扫描所有组件  -->
      	<context:component-scan base-package="com.atguigu"></context:component-scan>
      	
      	<!--配置一个视图解析器,能帮我们拼接页面地址  -->
      	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
      		<property name="prefix" value="/WEB-INF/pages/"></property>
      		<property name="suffix" value=".jsp"></property>
      	</bean>
      
    • Controller.java

      /**
       * 
       * 告诉springMVC这是一个处理器,可以处理请求
       * @Controller:标识哪个组件是控制器
       * @author 22007
       *
       */
      @Controller
      public class MyFirstController {
      	
      	/**
      	 *  /代表从当前项目下开始,处理当前项目下的hello请求
      	 */
      	@RequestMapping("/hello")
      	public String firstRequest(){
      		System.out.println("请求收到了。。。正在处理中");
      		return "success";
      	}
      	
      }
      
  • 测试

  1. helloWolrd细节
  • 运行流程

    • 客户端点击链接会发送http://localhost:8080/springmvc/hello
    • 来到tomcat服务器;
    • SpringMVC的前端控制器收到所有请求;
    • 来看请求地址和@RequestMapping标注的哪个匹配,来找到到底使用哪个类的哪个方法
    • 前端控制器找到了目标处理器类和目标方法,直接利用返回执行目标方法
    • 方法执行完成以后会有一个返回值,SpringMVC认为这个返回值就是要去的页面地址
    • 拿到方法返回值以后,用视图解析器进行拼串得到完整的页面地址
    • 拿到页面地址,前端控制器帮我们转发到页面
  • @RequestMapping

    • 这个注解告诉springMVC ,这个方法用来处理什么请求
    • 这个 / 是可以省略,即使省略了,也是默认从当前项目下开始
    • 习惯加上/比较好,如/hello
  • 如果不指定springMVC配置文件的位置,也会默认去找一个文件,该文件就在/WEB-INF下创建一个名叫前端控制器名-servlet.xml

在这里插入图片描述

  • 拦截地址的问题

    • 服务器的大web.xml中有一个DefaultServlet是url-pattern=/
    • 小web.xml是大web.xml的子类,会把大web.xml相同的部分覆盖掉
    • 我们的配置中前端控制器url-pattern=/,静态资源会来到DispatcherServlet(前端控制器)看哪个方法的RequestMapping是这个index.html
    • 为什么jsp又能访问,因为我们没有覆盖服务器中的JspServlet的配置
    • /* 直接就是拦截所有请求了,我们写/也是为了迎合后来Rest风格的URL地址
  1. @RequestMapping注解
  • RequestMapping注解写在类上时,为当前类的所有方法的请求地址指定一个基准路径

    //为当前类的所有方法的ing求地址指定一个基准路径
    @RequestMapping("/haha")
    @Controller
    public class MyFirstController {
    	/**
    	 *  /代表从当前项目下开始,处理当前项目下的hello请求
    	 */
    	@RequestMapping("/hello")
    	public String firstRequest(){
    		System.out.println("请求收到了。。。正在处理中");
    		return "success";
    	}
    }
    
  • value:指定该方法的请求地址

  • method:限定请求方式

    • HTTP协议中的所有请求方式:【GET】, HEAD, 【POST】, PUT, PATCH, DELETE, OPTIONS, TRACE
    • method=RequestMethod.POST:只接受这种类型的请求,默认是什么都可以;
  • params:规定请求参数,params 和 headers支持简单的表达式:

  • param1: 表示请求必须包含名为 param1 的请求参数

    eg:params={“username”}:发送请求的时候必须带上一个名为username的参数;没带都会404

  • !param1: 表示请求不能包含名为 param1 的请求参数

    eg:params={"!username"},发送请求的时候必须不携带上一个名为username的参数;带了都会404

  • param1 != value1: 表示请求包含名为 param1 的请求参数,但其值不能为 value1

eg:params={“username!=123”},发送请求的时候;携带的username值必须不是123(不带username或者username不是123)

  • {“param1=value1”, “param2”}: 请求必须包含名为 param1 和param2 的两个请求参数,且 param1 参数的值必须为 value1

    eg:params={“username!=123”,“pwd”,"!age"}请求参数必须满足以上规则:请求的username不能是123,必须有pwd的值,不能有age

  • headers:规定请求头;也和params一样能写简单的表达式

  • consumes:只接受内容类型是哪种的请求,规定请求头中的Content-Type

  • produces:告诉浏览器返回的内容类型是什么,给响应头中加上Content-Type:text/html;charset=utf-8

	/**
	 * RequestMapping的其他属性
	 * method属性设置请求方式
	 * 
	 */
	@RequestMapping(value="hello2",method=RequestMethod.GET,params={"username"},headers={"User-Agent=Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0"})
	public String secondRequest(){
		System.out.println("secondRequest");
		return "success";
	}
  1. @RequestMapping模糊匹配功能
  • URL地址可以写模糊的通配符:

  • ?:能替代任意一个字符

  • *:能替代任意多个字符,和一层路径

  • **:能替代多层路径

  • @Controller
    public class RequestMappingTest {
        
        @RequestMapping("/antTest01")
        public String antTest01(){
            System.out.println("antTest01...");
            return "success";
        }
        
        /**
         * ?匹配一个字符,0个多个都不行;
         *      模糊和精确多个匹配情况下,精确优先
         *
         * @return
         */
        @RequestMapping("/antTest0?")
        public String antTest02(){
            System.out.println("antTest02...");
            return "success";
        }
        
        /**
         *   *匹配任意多个字符
         * @return
         */
        @RequestMapping("/antTest0*")
        public String antTest03(){
            System.out.println("antTest03...");
            return "success";
        }
        
        /**
         *  *:匹配一层路径
         * @return
         */
        @RequestMapping("/a/*/antTest01")
        public String antTest04(){
            System.out.println("antTest04...");
            return "success";
        }
        
        @RequestMapping("/a/**/antTest01")
        public String antTest05(){
            System.out.println("antTest05...");
            return "success";
        }
        
        //路径上可以有占位符:  占位符 语法就是可以在任意路径的地方写一个{变量名}
        //   /user/admin    /user/leifengyang
        // 路径上的占位符只能占一层路径
        @RequestMapping("/user/{id}")
        public String pathVariableTest(@PathVariable("id")String id){
            System.out.println("路径上的占位符的值"+id);
            return "success";
        }
    
  1. Rest
  • Rest:系统希望以非常简洁的URL地址来发请求; 怎样表示对一个资源的增删改查用请求方式来区分

    • 原来写的

      • /getBook?id=1 :查询图书
      • /deleteBook?id=1:删除1号图书
      • /updateBook?id=1:更新1号图书
      • /addBook :添加图书
    • Rest风格的

      • Rest推荐; url地址这么起名; /资源名/资源标识符
      • /book/1 :GET-----查询1号图书
      • /book/1 :PUT------更新1号图书
      • /book/1 :DELETE-----删除1号图书
      • /book :POST-----添加图书系统的URL地址就这么来设计即可;
      • 简洁的URL提交请求,以请求方式区分对资源操作;
      • 问题:从页面上只能发起两种请求,GET、POST;其他的请求方式没法使用;
  • REST 介绍

    • REST:即 Representational State Transfer。(资源)表现层状态转化。是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用

    • 资源(Resources网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的存在。可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定URI 。要获取这个资源,访问它的URI就可以,因此 URI 即为每一个资源的独一无二的识别符**。

    • **表现层(Representation):**把资源具体呈现出来的形式,叫做它的表现层(Representation)。比如,文本可以用 txt 格式表现,也可以用 HTML 格式、XML 格式、JSON 格式表现,甚至可以采用二进制格式。

      状态转化(State Transfer):每发出一个请求,就代表了客户端和服务器的一次交互过程。HTTP协议,是一个无状态协议,即所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生**“状态转化State Transfer)。而这种转化是建立在表现层之上的,所以就是 “表现层状态转化**”**。**具体说,就是 HTTP 协议里面,四个表示操作方式的动词:**GET、POSTPUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,**POST 用来新建资源,PUT **用来更新资源,DELETE 用来删除资源。

  1. 使用Rest来构建一个增删改查系统;
  • index.jsp

    页面地址:
    <!-- 发起图书的增删改查请求;使用Rest风格的URL地址;
    请求url  请求方式 表示含义
    /book/1 GET:   查询1号图书
    /book/1 DELETE:删除1号图书
    /book/1 PUT:   更新1号图书
    /book   POST:  添加1号图书
    从页面发起PUT、DELETE形式的请求
     -->
    从页面发起PUT、DELETE形式的请求?Spring提供了对Rest风格的支持
    1)、SpringMVC中有一个Filter;他可以把普通的请求转化为规定形式的请求;配置这个filter;
    <filter>
            <filter-name>HiddenHttpMethodFilter</filter-name>
            <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>HiddenHttpMethodFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    2)、如何发其他形式请求?
        按照以下要求;1、创建一个post类型的表单 2、表单项中携带一个_method的参数,3、这个_method的值就是DELETE、PUT
     -->
    <a href="book/1">查询图书</a><br/>
    <form action="book" method="post">
        <input type="submit" value="添加1号图书"/>
    </form><br/>
    <!-- 发送DELETE请求 -->
    <form action="book/1" method="post">
        <input name="_method" value="delete"/>
        <input type="submit" value="删除1号图书"/>
    </form><br/>
    <!-- 发送PUT请求 -->
    <form action="book/1" method="post">
        <input name="_method" value="put"/>
        <input type="submit" value="更新1号图书"/>
    </form><br/>
    
    
  • 处理程序

    package com.atguigu.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    
    @Controller
    public class BookController {
        /**
         * 处理查询图书请求
         * @param id
         * @return
         */
        @RequestMapping(value="/book/{bid}",method=RequestMethod.GET)
        public String getBook(@PathVariable("bid")Integer id) {
            System.out.println("查询到了"+id+"号图书");
            return "success";
        }
    
        /**
         * 图书删除
         * @param id
         * @return
         */
        @RequestMapping(value="/book/{bid}",method=RequestMethod.DELETE)
        public String deleteBook(@PathVariable("bid")Integer id) {
            System.out.println("删除了"+id+"号图书");
            return "success";
        }
    
        /**
         * 图书更新
         * @return
         */
        @RequestMapping(value="/book/{bid}",method=RequestMethod.PUT)
        public String updateBook(@PathVariable("bid")Integer id) {
            System.out.println("更新了"+id+"号图书");
            return "success";
        }
    
        @RequestMapping(value="/book",method=RequestMethod.POST)
        public String addBook() {
            System.out.println("添加了新的图书");
            return "success";
        }
    
    }
    
  • web.xml

    <filter>
            <filter-name>HiddenHttpMethodFilter</filter-name>
            <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>HiddenHttpMethodFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    
  • 源码:

    @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
                throws ServletException, IOException {
         //获取表单上_method带来的值(delete\put)
            String paramValue = request.getParameter(this.methodParam);
         
         //判断如过表单是一个post而且_method有值
            if ("POST".equals(request.getMethod()) && StringUtils.hasLength(paramValue)) {
              //转为PUT、DELETE
                String method = paramValue.toUpperCase(Locale.ENGLISH);
              //重写了request.getMethod();
                HttpServletRequest wrapper = new HttpMethodRequestWrapper(request, method);
    
              //wrapper.getMethod()===PUT;
                filterChain.doFilter(wrapper, response);
            }
            else {
                 //直接放行
                filterChain.doFilter(request, response);
            }
        }
    
  • 注意:高版本Tomcat;Rest支持有点问题

    在这里插入图片描述

解决方案:

在这里插入图片描述

  1. 请求参数
  • package com.atguigu.controller;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    import javax.servlet.ServletInputStream;
    import javax.servlet.ServletOutputStream;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.CookieValue;
    import org.springframework.web.bind.annotation.RequestHeader;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    import com.atguigu.book.Book;
    
    @Controller
    public class HelloController {
    
        /**
         * request.getParameter("").....
         *
         * @return
         */
        @RequestMapping("/hello")
        public String handle01() {
            System.out.println("handle01...");
            return "success";
        }
    
        /**
         * SpringMVC如何获取请求带来的各种信息 默认方式获取请求参数: 直接给方法入参上写一个和请求参数名相同的变量。这个变量就来接收请求参数的值;
         * 带:有值,没带:null;
         *
         * @RequestParam:获取请求参数的;参数默认是必须带的;
         * @RequestParam("user")String username username =
         *                             request.getParameter("user")
         *
         *
         * @RequestParam("user")
         * @PathVariable("user")
         *                       /book/【{user}pathvariable】?【user=admin(requestparam)
         *                       】
         *
         *                       value:指定要获取的参数的key required:这个参数是否必须的
         *                       defaultValue:默认值。没带默认是null;
         *
         *
         * @RequestHeader:获取请求头中某个key的值; request.getHeader("User-Agent");
         * @RequestHeader("User-Agent")String userAgent userAgent =
         *                                    request.getHeader("User-Agent")
         *                                    如果请求头中没有这个值就会报错; value() required()
         *                                    defaultValue()
         *
         * @CookieValue:获取某个cookie的值; 以前的操作获取某个cookie; Cookie[] cookies =
         *                            request.getCookies(); for(Cookie c:cookies){
         *                            if(c.getName().equals("JSESSIONID")){ String
         *                            cv = c.getValue(); } }
         * value()
         * required()
         * defaultValue()
         */
        @RequestMapping("/handle01")
        public String handle02(
                @RequestParam(value = "user", required = false, defaultValue = "你没带") String username,
                @RequestHeader(value = "AHAHA", required = false, defaultValue = "她也没带") String userAgent,
                @CookieValue(value="JSESSIONID",required=false)String jid) {
            System.out.println("这个变量的值:" + username);
            System.out.println("请求头中浏览器的信息:" + userAgent);
            System.out.println("cookie中的jid的值"+jid);
            return "success";
        }
    
        /**
         * 如果我们的请求参数是一个POJO;
         * SpringMVC会自动的为这个POJO进行赋值?
         * 1)、将POJO中的每一个属性,从request参数中尝试获取出来,并封装即可;
         * 2)、还可以级联封装;属性的属性
         * 3)、请求参数的参数名和对象中的属性名一一对应就行
         *
         *
         * 提交的数据可能有乱码:
         * 请求乱码:
         *         GET请求:改server.xml;在8080端口处URIEncoding="UTF-8"
         *         POST请求:
         *             在第一次获取请求参数之前设置
         *             request.setCharacterEncoding("UTF-8");
         *             自己写一个filter;SpringMVC有这个filter
         *
         * 响应乱码:
         *         response.setContentType("text/html;charset=utf-8")
         * @param book
         * @return
         */
        @RequestMapping("/book")
        public String addBook(Book book){
            System.out.println("我要保存的图书:"+book);
            return "success";
        }
    
        /**
         * SpringMVC可以直接在参数上写原生API;
         *
         * HttpServletRequest
         * HttpServletResponse
         * HttpSession
         *
         *
         * java.security.Principal
         * Locale:国际化有关的区域信息对象
         * InputStream:
         *         ServletInputStream inputStream = request.getInputStream();
         * OutputStream:
         *         ServletOutputStream outputStream = response.getOutputStream();
         * Reader:
         *         BufferedReader reader = request.getReader();
         * Writer:
         *         PrintWriter writer = response.getWriter();
         *
         * @throws IOException
    
         *
         *
         */
        @RequestMapping("/handle03")
        public String handle03(HttpSession session,
                HttpServletRequest request,HttpServletResponse response) throws IOException {
            request.setAttribute("reqParam", "我是请求域中的");
            session.setAttribute("sessionParam", "额我是Session域中的");
    
            return "success";
        }
    
    }
    
  • 解决字符编码

    <!--get请求编码在servlet.xml中的8080端口的那个connector中设置-->
    <!-- 配置一个字符编码的Filter;一定注意:字符编码filter一般都在其他Filter之前; -->
    	<filter>
    		<filter-name>CharacterEncodingFilter</filter-name>
    		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    		<!-- encoding:指定解决POST请求乱码 -->
    		<init-param>
    			<param-name>encoding</param-name>
    			<param-value>UTF-8</param-value>
    		</init-param>
    		<init-param>
    			<!-- forceEncoding:顺手解决响应乱码;response.setCharacterEncoding(this.encoding); -->
    			<param-name>forceEncoding</param-name>	
    			<param-value>true</param-value>
    		</init-param>
    		
    	</filter>
    	<filter-mapping>
    		<filter-name>CharacterEncodingFilter</filter-name>
    		<url-pattern>/*</url-pattern>
    	</filter-mapping>
    
  1. 数据输出
/**
 * SpringMVC出过在方法上传入原生API数据外,还可以
 * 1)、可以在方法处出入Map、或者Model或者ModelMap
 *  Map,Model,ModelMap:最终都是BindingAwareModelMap在工作;
 *      相当于给BindingAwareModelMap中保存的东西都会被放在请求域中;
 *
 *      Map(interface(jdk))      Model(interface(spring)) 
 *          ||                          //
 *          ||                         //
 *          \/                        //
 *      ModelMap(clas)               //
 *                  \\              //
 *                   \\            //
 *                  ExtendedModelMap
 *                          ||
 *                          \/
 *                  BindingAwareModelMap
 *                   * 2)、方法的返回值可以变为ModelAndView类型;
 * 			既包含视图信息(页面地址)也包含模型数据(给页面带的数据);
 * 			而且数据是放在请求域中;
 * 			request、session、application;
 * 			
 * 3)、SpringMVC提供了一种可以临时给Session域中保存数据的方式;
 * 	使用一个注解	@SessionAttributes(只能标在类上)
 * 	@SessionAttributes(value="msg"):
 * 		给BindingAwareModelMap中保存的数据,或者ModelAndView中的数据,
 * 		同时给session中放一份;
 * 		value指定保存数据时要给session中放的数据的key;
 * 
 * 	value={"msg"}:只要保存的是这种key的数据,给Session中放一份
 * 	types={String.class}:只要保存的是这种类型的数据,给Session中也放一份
 * 
 *  后来推荐@SessionAttributes就别用了,可能会引发异常;
 * 			给session中放数据请使用原生API;
 * @author 22007
 *
 */

@SessionAttributes(value={"msg","haha"},types={String.class})
@Controller
public class OutputController {
	
	@RequestMapping("/hello01")
	public String test01(Map<String,Object> map){
		map.put("msg1", "你好");
		return "success";
	}
	
	@RequestMapping("/hello02")
	public String test02(Model model){
		model.addAttribute("msg1", "你好那么好");
		return "success";
	}
	
	@RequestMapping("/hello03")
	public String test03(ModelMap modelMap){
		modelMap.addAttribute("msg1", "你好那么好");
		return "success";
	}
	
	/**
	 * 返回值时ModelAndView可以为页面携带数据
	 * 
	 */
	@RequestMapping("/hello04")
	public ModelAndView test04(){
        //之前的返回值我们就叫视图名;视图名视图解析器是会帮我们最终拼串得到页面的真实地址;
        //ModelAndView mv = new ModelAndView("success");
        ModelAndView mv = new ModelAndView();
        mv.setViewName("success");
        mv.addObject("msg", "你好哦!");
        return mv;
	}
}
  1. ModelAttribute:使用场景:
  • 1)页面:

在这里插入图片描述

  • dao:全字段更新。没带的字段会在数据库中更新为null;

  • /**
     * 测试ModelAttribute注解;
     * 使用场景:书城的图书修改为例;
     * 1)页面端;
     *      显示要修改的图书的信息,图书的所有字段都在
     * 2)servlet收到修改请求,调用dao;
     *      String sql="update bs_book set title=?,
     *                  author=?,price=?,
     *                  sales=?,stock=?,img_path=?
     *              where id=?";
     * 3)实际场景?
     *      并不是全字段修改;只会修改部分字段,以修改用户信息为例;
     *      username  password  address;
     *      1)、不修改的字段可以在页面进行展示但是不要提供修改输入框;
     *      2)、为了简单,Controller直接在参数位置来写Book对象
     *      3)、SpringMVC为我们自动封装book;(没有带的值是null)
     *      4)、如果接下来调用了一个全字段更新的dao操作;会将其他的字段可能变为null;
     *          sql = "update bs_book set"
     *          if(book.getBookName()){
     *              sql +="bookName=?,"
     *          }
     *          if(book.getPrice()){
     *              sql +="price=?"
     *          }
     *
     * 4)、如何能保证全字段更新的时候,只更新了页面携带的数据;
     *      1)、修改dao;代价大?
     *      2)、Book对象是如何封装的?
     *          1)、SpringMVC创建一个book对象,每个属性都有默认值,bookName就是null;
     *              1、让SpringMVC别创建book对象,直接从数据库中先取出一个id=100的book对象的信息
     *              2、Book [id=100, bookName=西游记, author=张三, stock=12, sales=32, price=98.98]
     *
     *          2)、将请求中所有与book对应的属性一一设置过来;
     *              3、使用刚才从数据库取出的book对象,给它 的里面设置值;(请求参数带了哪些值就覆盖之前的值)
     *              4、带了的字段就改为携带的值,没带的字段就保持之前的值
     *          3)、调用全字段更新就有问题;
     *              5、将之前从数据库中查到的对象,并且封装了请求参数的对象。进行保存;
     *
     * @author lfy
     */
    @Controller
    public class ModelAttributeTestController {
        
        private Object o1;
        private Object o2;
        
        private Object b1;
        private Object b2;
        
        //bookDao.update(book);
        //Book [id=100, bookName=null, author=张三, stock=12, sales=32, price=98.98]
        /**
         *      String sql="update bs_book set bookName=?,
                        author=?,price=?,
                        sales=?,stock=?,img_path=?
                    where id=?";
         */
        /**
         * 可以告诉SpringMVC不要new这个book了我刚才保存了一个book;
         * 哪个就是从数据库中查询出来的;用我这个book?@ModelAttribute("haha")
         *
         * 
         * 同都是BindingAwareModelMap
         * @param book
         * @return
         */
        @RequestMapping("/updateBook")
        public String updateBook(@ModelAttribute("haha")Book book,Map<String, Object> model){
            o2 = model;
            b2  = book;
            Object haha = model.get("haha");
            //System.out.println("传入的model:"+model.getClass());
            System.out.println("o1==o2?"+(o1 == o2));
            System.out.println("b1==b2?"+(b1 == b2)+"-->"+(b2 == haha));
            
            System.out.println("页面要提交过来的图书信息:"+book);
            return "success";
        }
        
        /**
         * 1)、SpringMVC要封装请求参数的Book对象不应该是自己new出来的。
         *      而应该是【从数据库中】拿到的准备好的对象
         * 2)、再来使用这个对象封装请求参数
         *
         * @ModelAttribute:
         *      参数:取出刚才保存的数据
         *      方法位置:这个方法就会提前于目标方法先运行;
         *          1)我们可以在这里提前查出数据库中图书的信息
         *          2)将这个图书信息保存起来(方便下一个方法还能使用)
         *
         * 参数的map:BindingAwareModelMap
         */
        @ModelAttribute
        public void hahaMyModelAttribute(Map<String, Object> map){
            
            Book book = new Book(100, "西游记", "吴承恩", 98, 10, 98.98);
            System.out.println("数据库中查到的图书信息是:"+book);
            map.put("haha", book);
            b1 = book;
            o1 = map;
            System.out.println("modelAttribute方法...查询了图书并给你保存起来了...他用的map的类型:"+map.getClass());
        }
    }
    
    
  1. SpringMVC源码
  • DispatcherServlet继承树

在这里插入图片描述

  • doDispatch()方法

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
            HttpServletRequest processedRequest = request;
            HandlerExecutionChain mappedHandler = null;
            boolean multipartRequestParsed = false;
            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
            try {
                ModelAndView mv = null;
                Exception dispatchException = null;
                try {
                   //1、检查是否文件上传请求
                    processedRequest = checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                    // Determine handler for the current request.
                   //2、根据当前的请求地址找到那个类能来处理;
                    mappedHandler = getHandler(processedRequest);
    
                   //3、如果没有找到哪个处理器(控制器)能处理这个请求就404,或者抛异常
                    if (mappedHandler == null || mappedHandler.getHandler() == null) {
                        noHandlerFound(processedRequest, response);
                        return;
                    }
    
                    // Determine handler adapter for the current request.
                   //4、拿到能执行这个类的所有方法的适配器;(反射工具AnnotationMethodHandlerAdapter)
                    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
                    // Process last-modified header, if supported by the handler.
                    String method = request.getMethod();
                    boolean isGet = "GET".equals(method);
                    if (isGet || "HEAD".equals(method)) {
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if (logger.isDebugEnabled()) {
                            String requestUri = urlPathHelper.getRequestUri(request);
                            logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
                        }
                        if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }
                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }
                    try {
                        // Actually invoke the handler.处理(控制)器的方法被调用
                        //控制器(Controller),处理器(Handler)
                        //5、适配器来执行目标方法;将目标方法执行完成后的返回值作为视图名,设置保存到ModelAndView中
                        //目标方法无论怎么写,最终适配器执行完成以后都会将执行后的信息封装成ModelAndView
                        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    }
                    finally {
                        if (asyncManager.isConcurrentHandlingStarted()) {
                            return;
                        }
                    }
                    applyDefaultViewName(request, mv);//如果没有视图名设置一个默认的视图名;
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                }
                catch (Exception ex) {
                    dispatchException = ex;
                }
                //转发到目标页面;
                //6、根据方法最终执行完成后封装的ModelAndView;转发到对应页面,而且ModelAndView中的数据可以从请求域中获取
                processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
            }
            catch (Exception ex) {
                triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
            }
            catch (Error err) {
                triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
            }
            finally {
                if (asyncManager.isConcurrentHandlingStarted()) {
                    // Instead of postHandle and afterCompletion
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                    return;
                }
                // Clean up any resources used by a multipart request.
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    
  1. 视图解析器
  • forward与redirect

    @Controller
    public class HelloController {
    	
    	@RequestMapping("/hello")
    	public String hello(){
    		return "success";
    	}
    	
    	/**
    	 * forward转发到一个页面
    	 *  /hello.jsp,就是指转发到当前项目下的hello.jsp
    	 *  一定要加上/,如果不加/就是相对路径,容易出现问题
    	 *  forward:/hello.jsp
    	 *  forward:不会有视图解析器拼串
    	 */
    	
    	@RequestMapping("/handle01")
    	public String handle01(){
    		System.out.println("handle01");
    		return "forward:/handle.jsp";
    	}
    	
    	@RequestMapping("/handle02")
    	public String handle02(){
    		System.out.println("handle02");
    		return "forward:/handle01";
    	}
    	
    	/**
    	 * 重定向到hello.jsp页面
    	 * 转发 :forword:转发的路径
    	 * 重定向:redirect重定向的路径,相当于
    	 * 	response.sendRedirect("/hello.jsp")
    	 * 原生的servlet重定向/需要加上项目名才能成功
    	 * 而视图解析器会为/hello.jsp自动拼接上项目名 
    	 */
    	@RequestMapping("/handle03")
    	public String handle03(){
    		System.out.println("handle03");
    		return "redirect:/index.jsp";
    	}
    	
    	@RequestMapping("/handle04")
    	public String handle04(){
    		System.out.println("handle04");
    		return "redirect:/handle03";
    	}
    	
    }
    
  • 自定义视图解析器

    • View

      /**
       * 自定义视图
       * @author lfy
       *
       */
      public class MyView implements View{
      
      	/**
      	 * 返回的数据的内容类型
      	 */
      	@Override
      	public String getContentType() {
      		// TODO Auto-generated method stub
      		return "text/html";
      	}
      
      	@Override
      	public void render(Map<String, ?> model, HttpServletRequest request,
      			HttpServletResponse response) throws Exception {
      		// TODO Auto-generated method stub
      		System.out.println("之前保存的数据:"+model);
      		response.setContentType("text/html");
      		List<String> vn = (List<String>) model.get("video");
      		response.getWriter().write("哈哈<h1>即将展现精彩内容</h1>");
      		for (String string : vn) {
      			response.getWriter().write("<a>下载"+string+".avi</a><br/>");
      		}
      		response.getWriter().write(""
      				+ "<script>"
      				+ "var aEle = document.getElementsByTagName('a');"
      				+ "aEle.οnclick=function(){"
      				+ "alert('想下载吗?交学费')"
      				+ "}"
      				+ "</script>");
      	}
      
      }
      
    • MyMeiNVViewResolver

      public class MyMeiNVViewResolver implements ViewResolver,Ordered{
      
      	private Integer order = 0;
      	
      	@Override
      	public View resolveViewName(String viewName, Locale locale)
      			throws Exception {
      		// TODO Auto-generated method stub
      		//根据视图名返回视图对象
      		/**
      		 * 	meinv:/gaoqing  meinv:/dama
      			forward:/login.jsp
      		 */
      		if(viewName.startsWith("meinv:")){
      			return new MyView();
      		}else{
      			//如果不能处理返回null即可
      			return null;
      		}
      	}
      
      	/**
      	 * 
      	 */
      	@Override
      	public int getOrder() {
      		// TODO Auto-generated method stub
      		return order;
      	}
      	
      	//改变视图解析器的优先级
      	public void setOrder(Integer order){
      		this.order = order;
      	}
      
      }
      
    • MyViewResovlerController

      @Controller
      public class MyViewResovlerController {
      	
      	@RequestMapping("/handleplus")
      	public String handleplus(Model model){
      		//meinv:/gaoqing  meinv:/dama
      		//forward:/login.jsp
      		List<String> vname = new ArrayList<String>();
      		List<String> imgname = new ArrayList<String>();
      		vname.add("佟老师");
      		vname.add("飞哥");
      		imgname.add("萌萌");
      		
      		model.addAttribute("video", vname);
      		model.addAttribute("imgs", imgname);
      		
      		return "meinv:/gaoqing";
      	}
      
      }
      
    • springmvc配置器

      	<!--自定义的视图解析器    value="1"数字越小优先级越高-->
      	<bean class="com.atguigu.view.MyMeiNVViewResolver">
      		<property name="order" value="1"></property>
      	</bean>
      
  • jstlView支持便捷国际化

    • 导包导入了jstl的时候会自动创建为一个jstlView;可以快速方便的支持国际化功能;

    • 可以支持快速国际化;

      javaWeb国际化步骤;

      • 得到一个Locale对象;
      • 使用ResourceBundle绑定国际化资源文件;
      • 使用ResourceBundle.getString(“key”);获取到国际化配置文件中的值;
      • web页面的国际化,fmt标签库来做; fmt:setLocale <fmt:setBundle > fmt:message

      有了JstlView以后;

      • 让Spring管理国际化资源就行

            <!--让SpringMVC管理国际化资源文件;配置一个资源文件管理器  -->
            <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
                <!--  basename指定基础名-->
                <property name="basename" value="i18n"></property>
            </bean>
        
      • 直接去页面使用fmt:message;

        <fmt:message key="welcomeinfo"/>
        </h1>
        <form action="">
            <fmt:message key="username"/>:<input /><br/>
            <fmt:message key="password"/>:<input /><br/>
            <input type="submit" value='<fmt:message key="loginBtn"/>'/>
        </form>
        

        i18n_zh_CN.properties

        welcomeinfo=\u6B22\u8FCE\u6765\u5230\u5C1A\u7845\u8C37
        username=\u7528\u6237\u540D
        password=\u5BC6\u7801
        loginBtn=\u767B\u9646
        

        i18n_en_US.properties

        welcomeinfo=WELCOME TO ATGUIGU
        username=USERNAME
        password=PASSWORD
        loginBtn=LOGIN
        
  • mvc:view-controller将请求映射到一个页面

<!-- 发送一个请求("toLoginPage");直接来到web-inf下的login页面;mvc名称空间下有一个请求映射标签 -->
	<!-- path="":指定哪个请求 
	    view-name:指定映射给哪个视图;
	    走了SpringMVC的整个流程;视图解析。。。。
	    其他的请求就不好使了?
	 -->
	<mvc:view-controller path="/toLoginPage" view-name="login"/>
	<!-- 开启mvc注解驱动模式;开启了mvc的开挂模式 -->
	<mvc:annotation-driven></mvc:annotation-driven>
  1. ResultFulCRUD
  • 利用SpringMVC做一个CRUD(增删改查)符合Rest风格的;

    • C:Create:创建
    • R:Retrieve:查询
    • U:Update:更新
    • D:Delete:删除
    • 数据库:保存数据;使用Map,List保存数据之类
  • web.xml配置

    	<servlet>
    		<servlet-name>springDispatcherServlet</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>
    		<load-on-startup>1</load-on-startup>
    	</servlet>
    
    	<!-- Map all requests to the DispatcherServlet for handling -->
    	<servlet-mapping>
    		<servlet-name>springDispatcherServlet</servlet-name>
    		<url-pattern>/</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>forceEncoding</param-name>
    			<param-value>true</param-value>
    		</init-param>
    	</filter>
    	<filter-mapping>
    		<filter-name>CharacterEncodingFilter</filter-name>
    		<url-pattern>/*</url-pattern>
    	</filter-mapping>
    	
    	<!--支持Rest风格转换的filter  -->
    	<filter>
    		<filter-name>HiddenHttpMethodFilter</filter-name>
    		<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    	</filter>
    	<filter-mapping>
    		<filter-name>HiddenHttpMethodFilter</filter-name>
    		<url-pattern>/*</url-pattern>
    	</filter-mapping>
    </web-app>
    
  • springmvc.xml

        <!--进行包扫描  -->
    	<context:component-scan base-package="com.atguigu"></context:component-scan>
    	
    	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    		<property name="prefix" value="/WEB-INF/pages/"></property>
    		<property name="suffix" value=".jsp"></property>
    	</bean>
    	<!--接触前端控制器对静态资源的拦截  -->
    	<!--告诉SpringMVC ,自己的映射的请求就自己处理,不能处理的请求直接交给tomcat  -->
    	<mvc:default-servlet-handler/>
    	
    	<mvc:annotation-driven></mvc:annotation-driven>
    
  • Controller

    @Controller
    public class EmployeeController {
    	
    	@Autowired
    	EmployeeDao employeeDao;
    	
    	@Autowired
    	DepartmentDao departmentDao;  
    	
    	/**
    	 * 查询所有员工
    	 * @return
    	 */
    	@RequestMapping("/emps")
    	public String getEmps(Model model){
    		Collection<Employee> all=employeeDao.getAll();
    		model.addAttribute("emps", all);
    		return "list";
    	}
    	
    	/**
    	 * 处理去添加员工页面的
    	 */
    	@RequestMapping("/toaddpage")
    	public String toAddPage(Model model){
    		//1.先取出所有部门
    		Collection<Department> departments=departmentDao.getDepartments();
    		//2.添加到model中
    		model.addAttribute("depts", departments);
    		model.addAttribute("employee", new Employee(null,"lisi","201@qq.com",1,departmentDao.getDepartment(105)));
    		return "add";
    	}
    	
    	/**
    	 * 添加员工数据
    	 * 
    	 */
    	@RequestMapping(value="/emp",method=RequestMethod.POST)
    	public String addEmp(Employee employee){
    		System.out.println(employee);
    		employeeDao.save(employee);
    		//返回列表页面,直接重定向查询所有员工请求
    		return "redirect:/emps";
    	}
    	
    	/**
    	 * 查询员工,来到页面回显
    	 */
    	@RequestMapping(value="/emp/{id}",method=RequestMethod.GET)
    	public String getEmp(@PathVariable("id")Integer id,Model model){
    		//1.查出员工信息
    		Employee employee=employeeDao.get(id);
    		//2.放在请求域中
    		model.addAttribute("employee",employee);
    		//3.查出部门信息放在隐含模型中
    		Collection<Department> departments=departmentDao.getDepartments();
    		model.addAttribute("depts", departments);
    		return "edit";
    	}
    	
    	/**
    	 * 删除用户信息
    	 * @return
    	 */
    	@RequestMapping(value="/emp/{id}",method=RequestMethod.DELETE)
    	public String deleteEmp(@PathVariable("id")Integer id){
    		employeeDao.delete(id);
    		return "redirect:/emps";
    	}
    	
    	/**
    	 * 员工数据修改
    	 * @param employee
    	 * @return
    	 */
    	
    	@RequestMapping(value="/emp/${id}",method=RequestMethod.PUT)
    	public String updateEmp(Employee employee){
    		System.out.println("要修改的员工:"+employee);
    		employeeDao.save(employee);
    		return "redirect:/emps";
    	}
    	
    	
    	@ModelAttribute
    	public void myModelAttribute(
    			@RequestParam(value="id",required=false)Integer id,Model model){
    		if(id!=null){
    			Employee employee=employeeDao.get(id);
    			model.addAttribute("employee", employee);
    			System.out.println("要修改的员工:"+employee);
    		}
    		System.out.println("haha");
    	}
    	
    }
    
  • index.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!-- 访问项目就要展示员工列表页面 -->
    <jsp:forward page="/emps"></jsp:forward>
    
  • add.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
    <h1>添加员工</h1>
    <%-- <form action="">
    	lastName:<input type="text" name="lastName"/><br/>
    	email:<input type="text" name="email"><br/>
    	gender:
    	男:<input type="radio" name="gender" value="1"/>
    	女:<input type="radio" name="gender" value="0"/><br/>
    	部门:
    	<select name="department.id">
    		<c:forEach items="${depts}" var="deptItem">
    			<!--标签体中的是在页面的提示选项信息,value才是真正提交的值  -->
    			<option value="${deptItem.id}">${deptItem.departmentName}
    		</c:forEach>	
    	</select>
    	<br/>
    	<input type="submit" value="提交"/>
    </form> --%>
    
    <!--表单标签:
    1. SpringMVC认为,表单数据中的每一项最终都是要回显的,
    path指定的是一个属性,这个属性是从隐含魔性(请求域中取出的某个对象的属性)
    path指定的每一个属性,请求域中必须有一个对象,拥有这个属性。
    而这个对象就是请求域中名为command的对象-->
    <!--modelAttribute="":
    1.以前我们表单标签会从请求域中获取一个command的对象 ,把这个对象每一个属性对应的值赋值上
    2.现在可以用这个属性告诉SpringMVC不要去取command的值了,我放了一个modelAttribute指定的对象-->
    
    <%request.setAttribute("ctp", request.getContextPath());%>
    
    <form:form action="${ctp}/emp" modelAttribute="employee">
    	<!--path就是原来html-input的name项 
    	作用:1.当作原生的name项
    	2.自动回显隐含魔性中某个对象对应的这个属性的值
    	 -->
    	lastName:<form:input path="lastName"/>
    	email:<form:input path="email"/>
    	gender:男<form:radiobutton path="gender" value="1"/>
    	女<form:radiobutton path="gender" value="1"/><br/>
    	<!--
    	items指定要遍历的集合,自动遍历,遍历出的每一个元素是一个department对象
    	itemLabel="属性",指定遍历出的这个对象的哪个属性是作为option标签体的值
    	itemValue="属性",指定刚才遍历出来的对象的哪个属性要作为提交的值
    	  -->
    	dept:<form:select path="department.id" items="${depts}" itemLabel="departmentName" itemValue="id"></form:select>
    	<input type="submit" value="提交">
    </form:form>
    
    </body>
    </html>
    
  • list.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <%
    	pageContext.setAttribute("ctp", request.getContextPath());
    %>
    <script type="text/javascript" src="${ctp}/scripts/jquery-1.9.1.min.js"></script>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
    
    <h1>员工列表</h1>
    <table border="1" cellpadding="5" cellspacing="5">
    	<tr>
    		<th>ID</th>
    		<th>latName</th>
    		<th>email</th>
    		<th>gender</th>
    		<th>departmentName</th>
    		<th>EDIT</th>
    		<th>DELETE</th>
    	</tr>
    	<c:forEach items="${emps}" var="emp">
    		<tr>
    		<td>${emp.id}</td>
    		<td>${emp.lastName}</td>
    		<td>${emp.email}</td>
    		<td>${emp.gender==0?"女":"男"}</td>
    		<td>${emp.department.departmentName}</td>
    		<td>
    			<a href="${ctp}/emp/${emp.id}">edit</a>
    		</td>
    		<td>
    			<a href="${ctp}/emp/${emp.id}" class="delBtn">delete</a>
    		</td>
    		</tr>
    	</c:forEach>
    </table>
    <a href="${ctp}/toaddpage">添加员工</a>
    <form id="deleteForm" action="${ctp}/emp/${emp.id}" method="post">
    	<input type="hidden" name="_method" value="DELETE">
    </form>
    <script type="text/javascript">
    	$(function(){
    		$(".delBtn").click(function(){
    			//1.改变表单的action指向
    			$("#deleteForm").attr("action",this.href);
    			//2.提交表单
    			$("#deleteForm").submit();
    			return false;
    			
    		});
    	});
    </script>
    </body>
    </html>
    
  • editjsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
    
    <%pageContext.setAttribute("ctp", request.getContextPath());%>
    <h1>员工修改页面</h1>
    <form:form action="${ctp}/emp/${employee.id}"  modelAttribute="employee" method="post">
    	<!--path就是原来html-input的name项 
    	作用:1.当作原生的name项
    	2.自动回显隐含魔性中某个对象对应的这个属性的值
    	 -->
    	  <input type="hidden" name="" value="PUT"> 
    	 <input type="hidden" name="id" value="${employee.id}">
    	lastName:<form:input path="lastName"/>
    	email:<form:input path="email"/>
    	gender:男<form:radiobutton path="gender" value="1"/>
    	女<form:radiobutton path="gender" value="1"/><br/>
    	<!--
    	items指定要遍历的集合,自动遍历,遍历出的每一个元素是一个department对象
    	itemLabel="属性",指定遍历出的这个对象的哪个属性是作为option标签体的值
    	itemValue="属性",指定刚才遍历出来的对象的哪个属性要作为提交的值
    	  -->
    	dept:<form:select path="department.id" items="${depts}" itemLabel="departmentName" itemValue="id"></form:select>
    	<input type="submit" value="提交"><br/>
    </form:form>
    ${ctp}/emp/${employee.id}
    </body>
    </html>
    
  1. 数据绑定
  • 数据绑定流程

    • Spring MVC 主框架将 ServletRequest 对象及目标方法的入参实例传递给 WebDataBinderFactory 实例,以创建 DataBinder 实例对象
    • DataBinder 调用装配在 Spring MVC 上下文中的 ConversionService 组件进行数据类型转换、数据格式化工作。将 Servlet 中的请求信息填充到入参对象中
    • 调用 Validator 组件对已经绑定了请求消息的入参对象进行数据合法性校验,并最终生成数据绑定结果 BindingData 对象
    • Spring MVC 抽取 BindingResult 中的入参对象和校验错误对象,将它们赋给处理方法的响应入参
  • 数据绑定步骤

    • 自定义的转换器

      /**
       * 两个返回
       * 
       * S:Source T:Target 将s转为t
       * 
       * @author lfy
       * 
       */
      public class MyStringToEmployeeConverter implements Converter<String, Employee> {
      
      	@Autowired
      	DepartmentDao departmentDao;
      	
      	/**
      	 * 自定义的转换规则
      	 */
      	@Override
      	public Employee convert(String source) {
      		// TODO Auto-generated method stub
      		// empAdmin-admin@qq.com-1-101
      		System.out.println("页面提交的将要转换的字符串" + source);
      		Employee employee = new Employee();
      		if (source.contains("-")) {
      			String[] split = source.split("-");
      			employee.setLastName(split[0]);
      			employee.setEmail(split[1]);
      			employee.setGender(Integer.parseInt(split[2]));
      			employee.setDepartment(departmentDao.getDepartment(Integer.parseInt(split[3])));
      		}
      		return employee;
      	}
      }
      
      
    • Springmvc.xml配置

      	    <!-- 告诉SpringMVC别用默认的ConversionService,
                  而用我自定义的ConversionService、可能有我们自定义的Converter; -->
          <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
              <!--converters转换器中添加我们自定义的类型转换器  -->
              <property name="converters">
                  <set>
                      <bean class="com.atguigu.component.MyStringToEmployeeConverter"></bean>
                  </set>
              </property>
          </bean>
          <!-- conversion-service="conversionService":使用我们自己配置的类型转换组件 -->
          <mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
      
    • 控制器

      	/**
      	 * 发送的请求是什么?
      	 * 
      	 * quickadd?empinfo=empAdmin-admin@qq.com-1-101
      	 * 
      	 * @RequestParam("empinfo")Employee employee; Employee employee =
      	 *                                  request.getParameter("empinfo");
      	 * 
      	 *                                  可以通过写一个自定义类型的转换器让其工作;
      	 * @param employee
      	 * @return
      	 */
      	@RequestMapping("/quickadd")
      	public String quickAdd(@RequestParam("empinfo") Employee employee) {
      		System.out.println("封装:" + employee);
      		employeeDao.save(employee);
      		return "redirect:/emps";
      	}
      
    • 页面

      <form action="${ctp }/quickadd">
      		<!--将员工的所有信息都写上,自动封装对象  -->
      		<input name="empinfo" value="empAdmin-admin@qq.com-1-101"/>
      		<input type="submit" value="快速添加"/>
      </form>
      
  1. 数据格式化
  • 日期格式化

    • 在bean中添加注解

      	@DateTimeFormat(pattern="yyyy-MM-dd")
      	private Date birth;
      
    • springmvc.xml中设置格式化器

          <!--以后使用自定义类型转换器的时候用org.springframework.format.support.FormattingConversionServiceFactoryBean
          就具有类型转换和格式功能  -->
          <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
              <!--converters转换器中添加我们自定义的类型转换器  -->
              <property name="converters">
                  <set>
                      <bean class="com.atguigu.component.MyStringToEmployeeConverter"></bean>
                  </set>
              </property>
          </bean>
      
  • 数字格式化同理

    	//为了显示方便
    	@NumberFormat(pattern="#,###.##")
    	private Double salary;
    
  1. 数据校验
  • JSR 303 是 Java 为 Bean 数据合法性校验提供的标准框架,它已经包含在 JavaEE 6.0 中

  • JSR 303 通过 Bean 属性上标注类似于 @NotNull、@Max 等标准的注解指定校验规则,并通过标准的验证接口对 Bean 进行验证

    在这里插入图片描述

  • 进行数据校验的步骤

    • 导入数据校验的包( 有几个带el的jar不导入;tomcat中有;如果tomcat的版本是 7.0以下;tomcat7.0以上el表达式比较强大; 如果是7.0以下将带el的几个jar放在tomcat的 lib文件夹下;

          核心校验包
          hibernate-validator-5.0.0.CR2.jar
      	hibernate-validator-annotation-processor-5.0.0.CR2.jar
      	依赖包
          classmate-0.8.0.jar
      	jboss-logging-3.1.1.GA.jar
      	validation-api-1.1.0.CR1.jar
      
    • 加上校验注解

      	@NotEmpty
      	@Length(min=6,max=18)
      	private String lastName;
      
      	@Email
      	private String email;
      	//1 male, 0 female
      	private Integer gender;
      	
      	private Department department;
      	
      	//@Past:必须是一个过去的时间
      	//@@Future:必须是未来的时间
      	@DateTimeFormat(pattern="yyyy-MM-dd")
      	@Future
      	private Date birth;
      
    • 在SpringMVC封装对象的时候,告诉SpringMVC这个javaBean需要校验

      	public String addEmp(@Valid Employee employee){
      
    • 给需要校验的javaBean后面紧跟一个BindingResult。这个BindingResult就是封装前一个bean的校验结果;

      	public String addEmp(@Valid Employee employee,BindingResult bindingResult)
      
    • 根据不同的校验结果决定接下来做什么

      		//获取是否有校验错误
      		boolean hasErrors=bindingResult.hasErrors();
      		if(hasErrors){
      			System.out.println("有校验错误");
      			return "add";
      		}else{
      			System.out.println(employee);
      			employeeDao.save(employee);
      			//返回列表页面,直接重定向查询所有员工请求
      			return "redirect:/emps";
      		}
      
    • 来到页面使用form:errors取出错误信息即可;

      	lastName:<form:input path="lastName"/>
      <form:errors path="lastName"></form:errors>
      	email:<form:input path="email"/>
      <form:errors path="email"></form:errors>
      
    • 如果是在普通的表单里,则将获取到的错误放进请求域中即可

      	Map<String,Object> errorsMap=new HashMap<String,Object>();
      		if(hasErrors){
      			
      		List<FieldError> errors=bindingResult.getFieldErrors();
      		for(FieldError fieldError : errors){
      			System.out.println("错误消息提示:"+fieldError.getDefaultMessage());
      			System.out.println("错误的字段是:"+fieldError.getField());
      			System.out.println(fieldError);
      			System.out.println("---------------");
      			errorsMap.put(fieldError.getField(), fieldError.getDefaultMessage());
      		}
      		model.addAttribute("errorInfo", errorsMap);
      		return "add";
      		}
      
      	lastName:<form:input path="lastName"/><form:errors path="lastName"></form:errors>--->${errorInfo.lastName}
      
      
  • 国际化错误消息处理

    • 每一个字段发生错误以后,都会有自己的错误代码;国际化文件中错误消息的key必须对应一个错误代码

       codes
           [
                Email.employee.email,      校验规则.隐含模型中这个对象的key.对象的属性
                Email.email,                       校验规则.属性名
                Email.java.lang.String,      校验规则.属性类型
                Email
          ];
      
      • 如果是隐含模型中employee对象的email属性字段发生了@Email校验错误,就会生成 Email.employee.email;
      • Email.email:所有的email属性只要发生了@Email错误;
      • Email.java.lang.String,:只要是String类型发生了@Email错误
      • Email:只要发生了@Email校验错误;
    • 先编写国际化配置文件

    在这里插入图片描述

  • 让SpringMVC管理国际化资源文件;

        <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
            <property name="basename" value="errors"></property>
        </bean>
    
  • 来到页面取值

  • 高级国际化 动态传入消息参数;

在这里插入图片描述

{0}:永远都是当前属性名;{1}、{2}

  • 实际上在获取时直接在注解中加message就好
  1. SpringMVC使用ajax
  • 导包

jackson-annotations-2.1.5.jar

jackson-core-2.1.5.jar

jackson-databind-2.1.5.jar

  • 写配置

    @Controller
    public class AjaxTestController {
    	
    	@Autowired
    	EmployeeDao dao;
    	/**
    	 * 将返回的数据放在响应体中
    	 * 如果是对象,jackson包自动将对象转为json格式
    	 */
    	@ResponseBody
    	@RequestMapping("/getalljax")
    	public Collection<Employee> ajaxGetAll(){
    		Collection<Employee> all=dao.getAll();
    		return all;
    	}
    }
    
    
  • 测试

    <body>
    <a href="${ctp}/getalljax">ajax请求</a>
    <div>
    
    </div>
    <script type="text/javascript">
    	$("a:first").click(function(){
    		//1.发送ajax获取 所有员工
    		$.ajax({
    			url:"${ctp}/getalljax",
    			type:"GET",
    			success:function(data){
    				$.each(data,function(){
    					var empInfo=this.lastName+"-->"+this.birth+"-->"+this.gender+"-->";
    					$("div").append(empInfo);
    				});
    			}
    		});
    		//关掉a的跳转属性
    		return false;
    	});
    </script>
    </body>
    
    
@Controller
public class AjaxTestController {
	
	@Autowired
	EmployeeDao employeeDao;
	
	/**
	 * SpringMVC文件下载;
	 * 
	 * @param request
	 * @return
	 * @throws Exception
	 */
	@RequestMapping("/download")
	public ResponseEntity<byte[]> download(HttpServletRequest request) throws Exception{
		
		//1、得到要下载的文件的流;
		//找到要下载的文件的真实路径
		ServletContext context = request.getServletContext();
		String realPath = context.getRealPath("/scripts/jquery-1.9.1.min.js");
		FileInputStream is = new FileInputStream(realPath);
		
		byte[] tmp = new byte[is.available()];
		is.read(tmp);
		is.close();
		
		//2、将要下载的文件流返回
		HttpHeaders httpHeaders = new HttpHeaders();
		httpHeaders.set("Content-Disposition", "attachment;filename="+"jquery-1.9.1.min.js");
		
		return new ResponseEntity<byte[]>(tmp, httpHeaders, HttpStatus.OK);
	}
	
	/**
	 * 将返回数据放在响应体中
	 * 
	 * ResponseEntity<String>:响应体中内容的类型
	 * @return
	 */
	//@ResponseBody
	@RequestMapping("/haha")
	public ResponseEntity<String> hahah(){
		
		MultiValueMap<String, String> headers = new HttpHeaders();
		String body = "<h1>success</h1>";
		headers.add("Set-Cookie", "username=hahahaha");
		
		return new ResponseEntity<String>(body , headers, HttpStatus.OK);
	}
	
	/**
	 * 如果参数位置写HttpEntity<String>  str;
	 * 比@RequestBody更强,可以拿到请求头;
	 *  @RequestHeader("")
	 * 
	 * @param str
	 * @return
	 */
	@RequestMapping("/test02")
	public String test02(HttpEntity<String>  str){
		System.out.println(str);
		return "success";
	}
	
	/**
	 * 
	 */
	@RequestMapping("/test01")
	public String test01(@RequestBody String str){
		System.out.println("请求体:"+str);
		return "success";
	}
	
	/**
	 *  @RequestBody:请求体;获取一个请求的请求体
	 *  @RequestParam:
	 *  
	 *  @ResponseBody:可以把对象转为json数据,返回给浏览器
	 *  
	 *  @RequestBody:接受json数据,封装为对象
	 * @return
	 */
	@RequestMapping("/testRequestBody")
	public String testRequestBody(@RequestBody Employee employee){
		System.out.println("请求体:"+employee);
		return "success";
	}
	
	/**
	 * 将返回的数据放在响应体中;
	 * 如果是对象,jackson包自动将对象转为json格式
	 * @return
	 */
	@ResponseBody
	@RequestMapping("/getallajax")
	public Collection<Employee> ajaxGetAll(){
		Collection<Employee> all = employeeDao.getAll();
		return all;
	}

}
  1. 文件上传
  • 文件上传表单准备

    ${msg}
    <form action="${ctp}/upload" method="post" enctype="multipart/form-data">
    	用户头像:<input type="file" name="headimg"><br/>
    	用户头像:<input type="file" name="headimg"><br/>
    	用户头像:<input type="file" name="headimg"><br/>
    	用户头像:<input type="file" name="headimg"><br/>
    	用户名:<input type="text" name="username">
    	<input type="submit">
    </form>
    
  • 导包

    commons-fileupload-1.2.1.jar
    commons-io-2.0.jar
    
  • 只要在SpringMVC配置文件中,编写一个配置,配置文件上传解析器

    	
    	<!--配置文件上传解析器,id必须是multipartResolver  -->
    	<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    		<property name="maxUploadSize" value="#{1024*1024*20}"></property>
    		<!--设置处理字符编码  -->
    		<property name="defaultEncoding" value="utf-8"></property>
    	</bean>
    
  • 4.文件上传请求处理,在处理器方法上写一个@RequestParam(“headerimg”)MultipartFile file

    @Controller
    public class FileUploadController {
    	
    	@RequestMapping("/upload")
    	public String upload(@RequestParam(value="username",required=false)String username,
    				@RequestParam("headimg")MultipartFile[] file,
    				Model model){
    		System.out.println("上传文件的信息");
    		//System.out.println("文件的名字:"+file.getName());
    		//System.out.println("文件的名字:"+file.getOriginalFilename());
    		
    		//文件保存
    		for(MultipartFile multipartFile:file){
    			if(!multipartFile.isEmpty()){
    				try {
    					multipartFile.transferTo(new File("D:\\"+multipartFile.getOriginalFilename()));
    					model.addAttribute("msg", "文件上传成功");
    				} catch (Exception e) {
    					model.addAttribute("msg", "文件上传失败了");
    					e.printStackTrace();
    				}
    			}
    		}	
    		return "forward:/index.jsp";
    	}
    }
    
  1. 拦截器
  • SpringMVC提供了拦截器机制,允许运行目标方法之前进行一些拦截工作,或者目标方法运行之后进行一些其他处理;

    • Filter:javaWeb
    • HandlerInterceptor:SpringMVC
    • preHandle:在目标方法运行之前调用;返回boolean;return true;(chain.doFilter())放行; return false;不放行
    • postHandle:在目标方法运行之后调用:目标方法调用之后
    • afterCompletion:在请求整个完成之后;来到目标页面之后;chain.doFilter()放行;资源响应之后;
      在这里插入图片描述
  • 拦截器的使用步骤

    • 继承接口实现拦截器功能

      /**
       * 1、实现HandlerInterceptor接口
       * 2、在SpringMVC配置文件中注册这个拦截器的工作;
       * 		配置这个拦截器来拦截哪些请求的目标方法;
       * 
       *
       */
      public class MyFirstInterceptor implements HandlerInterceptor {
      
      	/**
      	 * 目标方法运行之前运行
      	 */
      	@Override
      	public boolean preHandle(HttpServletRequest request,
      			HttpServletResponse response, Object handler) throws Exception {
      		System.out.println("MyFirstInterceptor...preHandle...");
      		return true;
      	}
      
      	@Override
      	public void postHandle(HttpServletRequest request,
      			HttpServletResponse response, Object handler,
      			ModelAndView modelAndView) throws Exception {
      		System.out.println("MyFirstInterceptor...postHandle...");
      	}
      
      	@Override
      	public void afterCompletion(HttpServletRequest request,
      			HttpServletResponse response, Object handler, Exception ex)
      			throws Exception {
      		System.out.println("MyFirstInterceptor...afterCompletion");
      	}
      
      }
      
    • springmvc.xml进行配置

      	<!-- 测试拦截器 -->
      	<mvc:interceptors>
      		<!--配置某个拦截器;默认是拦截所有请求的;  -->
      		<bean class="com.atguigu.controller.MyFirstInterceptor"></bean>
      		<!-- 配置某个拦截器更详细的信息 -->
      		<mvc:interceptor>
      			<!-- 只来拦截test01请求 -->
      			<mvc:mapping path="/test01"/>
      			<bean class="com.atguigu.controller.MySecondInterceptor"></bean>
      		</mvc:interceptor>
      	</mvc:interceptors>
      
    • 测试

      @Controller
      public class InterceptorTestController {
      	@RequestMapping("/test01")
      	public String test01(){
      		System.out.println("test01....");
      		//int i =10/0;
      		return "success";
      	}
      }
      
    • 单个拦截器的运行流程:正常运行流程; 拦截器的preHandle------目标方法-----拦截器postHandle-----页面-------拦截器的afterCompletion;

    • 多个拦截器的运行流程:写在前面的先运行,但

      • 流程:filter的流程;

      • 拦截器的preHandle:是按照顺序执行拦截器的

      • postHandle:是按照逆序执行拦截器的

      • afterCompletion:是按照逆序执行;

      • 已经放行了的拦截器的afterCompletion总会执行;

      • MyFirstInterceptor...preHandle...
        MySecondInterceptor...preHandle...
        test01....
        MySecondInterceptor...postHandle...
        MyFirstInterceptor...postHandle...
        success.jsp....
        MySecondInterceptor...afterCompletion...
        MyFirstInterceptor...afterCompletion
        
    • 单个拦截器流程
      在这里插入图片描述

  • 多个拦截器流程
    在这里插入图片描述
  • 有异常的情况

在这里插入图片描述

  • 什么时候用Filter什么时候用拦截器?如果某些功能;需要其他组件配合完成,我们就使用拦截器;其他情况可以写filter;
  1. 异常处理
  • ExceptionHandler处理局部异常

    	/**
    	 * 告诉SpringMVC这个方法专门处理这个类发生的异常 1、给方法上随便写一个Exception,用来接受发生的异常
    	 * 2、要携带异常信息不能给参数位置写Model; 3、返回ModelAndView就行了;
    	 * 4、如果有多个@ExceptionHandler都能处理这个异常,精确优先 5、全局异常处理与本类同时存在,本类优先;
    	 */
    	@ExceptionHandler(value = { Exception.class })
    	public ModelAndView handleException01(Exception exception) {
    		System.out.println("本类的:handleException01..." + exception);
    		//
    		ModelAndView view = new ModelAndView("myerror");
    		view.addObject("ex", exception);
    		// 视图解析器拼串
    		return view;
    	}
    
  • ExceptionHandler处理全局异常

    /**
     * 集中处理所有异常
     * @author lfy
     * 
     * 1、集中处理所有异常的类加入到ioc容器中
     * 2、@ControllerAdvice专门处理异常的类
     */
    @ControllerAdvice
    public class MyJiZhongException {
    	
    	@ExceptionHandler(value={ArithmeticException.class})
    	public ModelAndView handleException01(Exception exception){
    		System.out.println("全局的:handleException01..."+exception);
    		//
    		ModelAndView view = new ModelAndView("myerror");
    		view.addObject("ex", exception);
    		//视图解析器拼串
    		return view;
    	}
    	
    	@ExceptionHandler(value={Exception.class})
    	public ModelAndView handleException02(Exception exception){
    		System.out.println("全局的:handleException02..."+exception);
    		//
    		ModelAndView view = new ModelAndView("myerror");
    		view.addObject("ex", exception);
    		//视图解析器拼串
    		return view;
    	}
    
    }
    
  • @ResponseStatus;给自定义异常上标注的

    @ResponseStatus(reason="用户被拒绝登陆",value=HttpStatus.NOT_ACCEPTABLE)
    public class UserNameNotFoundException extends RuntimeException {
    
    	private static final long serialVersionUID = 1L;
    	
    }
    
  • DefaultHandlerExceptionResolver:判断是否SpringMVC自带的异常

    try {
                if (ex instanceof NoSuchRequestHandlingMethodException) {
                    return handleNoSuchRequestHandlingMethod((NoSuchRequestHandlingMethodException) ex, request, response,
                            handler);
                }
                else if (ex instanceof HttpRequestMethodNotSupportedException) {
                    return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, request,
                            response, handler);
                }
                else if (ex instanceof HttpMediaTypeNotSupportedException) {
                    return handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException) ex, request, response,
                            handler);
                }
                else if (ex instanceof HttpMediaTypeNotAcceptableException) {
                    return handleHttpMediaTypeNotAcceptable((HttpMediaTypeNotAcceptableException) ex, request, response,
                            handler);
                }
                else if (ex instanceof MissingServletRequestParameterException) {
                    return handleMissingServletRequestParameter((MissingServletRequestParameterException) ex, request,
                            response, handler);
                }
                else if (ex instanceof ServletRequestBindingException) {
                    return handleServletRequestBindingException((ServletRequestBindingException) ex, request, response,
                            handler);
                }
                else if (ex instanceof ConversionNotSupportedException) {
                    return handleConversionNotSupported((ConversionNotSupportedException) ex, request, response, handler);
                }
                else if (ex instanceof TypeMismatchException) {
                    return handleTypeMismatch((TypeMismatchException) ex, request, response, handler);
                }
                else if (ex instanceof HttpMessageNotReadableException) {
                    return handleHttpMessageNotReadable((HttpMessageNotReadableException) ex, request, response, handler);
                }
                else if (ex instanceof HttpMessageNotWritableException) {
                    return handleHttpMessageNotWritable((HttpMessageNotWritableException) ex, request, response, handler);
                }
                else if (ex instanceof MethodArgumentNotValidException) {
                    return handleMethodArgumentNotValidException((MethodArgumentNotValidException) ex, request, response, handler);
                }
                else if (ex instanceof MissingServletRequestPartException) {
                    return handleMissingServletRequestPartException((MissingServletRequestPartException) ex, request, response, handler);
                }
                else if (ex instanceof BindException) {
                    return handleBindException((BindException) ex, request, response, handler);
                }
                else if (ex instanceof NoHandlerFoundException) {
                    return handleNoHandlerFoundException((NoHandlerFoundException) ex, request, response, handler);
                }
            }
            catch (Exception handlerException) {
                logger.warn("Handling of [" + ex.getClass().getName() + "] resulted in Exception", handlerException);
            }
            return null;
        }
    
  • SimpleMappingExceptionResolver:通过配置的方式进行异常处理

    	<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    		<!-- exceptionMappings:配置哪些异常去哪些页面 -->
    		<property name="exceptionMappings">
    			<props>
    				<!-- key:异常全类名;value:要去的页面视图名; -->
    				<prop key="java.lang.NullPointerException">myerror</prop>
    			</props>
    		</property>
    		<!--指定错误信息取出时使用的key  -->
    		<property name="exceptionAttribute" value="ex"></property>
    	</bean>
    
  • 几种异常的运行顺序

    在这里插入图片描述

  1. SpringMVC处理流程
  • 所有请求,前端控制器(DispatcherServlet)收到请求,调用doDispatch进行处理

  • 根据HandlerMapping中保存的请求映射信息找到,处理当前请求的,处理器执行链(包含拦截器)

  • 根据当前处理器找到他的HandlerAdapter(适配器)

  • 拦截器的preHandle先执行

  • 适配器执行目标方法,并返回ModelAndView

    • ModelAttribute注解标注的方法提前运行

    • 执行目标方法的时候(确定目标方法用的参数)

      • 有注解

      • 没注解:

        • 看是否Model、Map以及其他的

        • 如果是自定义类型

          • 从隐含模型中看有没有,如果有就从隐含模型中拿
          • 如果没有,再看是否SessionAttributes标注的属性,如果是从Session中拿,如果拿不到会抛异常
          • 都不是,就利用反射创建对象
  • 拦截器的postHandle执行

  • 处理结果;(页面渲染流程)

    • 如果有异常使用异常解析器处理异常;处理完后还会返回ModelAndView

    • 调用render进行页面渲染

      • 视图解析器根据视图名得到视图对象
      • 视图对象调用render方法
    • 执行拦截器的afterCompletion;

  • 流程图

在这里插入图片描述

  1. SpringMVC与Spring整合
  • SpringMVC和Spring整合的目的;分工明确;SpringMVC的配置文件就来配置和网站转发逻辑以及网站功能有关的(视图解析器,文件上传解析器,支持ajax,xxx);Spring的配置文件来配置和业务有关的(事务控制,数据源,xxx);

    <import resource="spring.xml"/>:可以合并配置文件;
    
    
  • Spring管理业务逻辑组件;

        <context:component-scan base-package="com.atguigu">
            <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
            <context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
        </context:component-scan>
    
  • SpringMVC管理控制器组件;

        <context:component-scan base-package="com.atguigu" use-default-filters="false">
            <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
            <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
        </context:component-scan>
    
  • Spring是一个父容器,SpringMVC是一个子容器;子容器还可以引用父容器的组件;父容器不能引用子容器的组件;

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Md8geypC-1597504590212)(F:\markdown\ssm\Spring\image\Image [56].png)]

y:异常全类名;value:要去的页面视图名; -->

  			<prop key="java.lang.NullPointerException">myerror</prop>
  		</props>
  	</property>
  	<!--指定错误信息取出时使用的key  -->
  	<property name="exceptionAttribute" value="ex"></property>
  </bean>

* 几种异常的运行顺序

[外链图片转存中...(img-RopC0YPb-1597504590210)]
  1. SpringMVC处理流程
  • 所有请求,前端控制器(DispatcherServlet)收到请求,调用doDispatch进行处理

  • 根据HandlerMapping中保存的请求映射信息找到,处理当前请求的,处理器执行链(包含拦截器)

  • 根据当前处理器找到他的HandlerAdapter(适配器)

  • 拦截器的preHandle先执行

  • 适配器执行目标方法,并返回ModelAndView

    • ModelAttribute注解标注的方法提前运行

    • 执行目标方法的时候(确定目标方法用的参数)

      • 有注解

      • 没注解:

        • 看是否Model、Map以及其他的

        • 如果是自定义类型

          • 从隐含模型中看有没有,如果有就从隐含模型中拿
          • 如果没有,再看是否SessionAttributes标注的属性,如果是从Session中拿,如果拿不到会抛异常
          • 都不是,就利用反射创建对象
  • 拦截器的postHandle执行

  • 处理结果;(页面渲染流程)

    • 如果有异常使用异常解析器处理异常;处理完后还会返回ModelAndView

    • 调用render进行页面渲染

      • 视图解析器根据视图名得到视图对象
      • 视图对象调用render方法
    • 执行拦截器的afterCompletion;

  • 流程图

    [外链图片转存中…(img-yIdgPSKY-1597504590211)]

  1. SpringMVC与Spring整合
  • SpringMVC和Spring整合的目的;分工明确;SpringMVC的配置文件就来配置和网站转发逻辑以及网站功能有关的(视图解析器,文件上传解析器,支持ajax,xxx);Spring的配置文件来配置和业务有关的(事务控制,数据源,xxx);

    <import resource="spring.xml"/>:可以合并配置文件;
    
    
  • Spring管理业务逻辑组件;

        <context:component-scan base-package="com.atguigu">
            <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
            <context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
        </context:component-scan>
    
  • SpringMVC管理控制器组件;

        <context:component-scan base-package="com.atguigu" use-default-filters="false">
            <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
            <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
        </context:component-scan>
    
  • Spring是一个父容器,SpringMVC是一个子容器;子容器还可以引用父容器的组件;父容器不能引用子容器的组件;

在这里插入图片描述

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值