Spring MVC学习笔记

作者: 拾壹北 | 来源:发表于2017-01-19 18:40 被阅读760次

    学习资料源:慕课网 - Spring MVC起步

    内容概要

    • 一、前端控制器(Front Controller)
    • 二、MVC
    • 三、Spring MVC基本概念
    • 四、Spring MVC项目搭建
    • 五、配置文件
    • 六、Controller基础代码
    • 七、Binding 绑定
    • 八、FileUpload 单文件上传
    • 九、Spring MVC与json

    一、前端控制器(Front Controller)

    前端控制器

    1、前端控制器通过HTTP等协议接收用户请求,并分发给能处理该请求的Controller;
    2、Controller通过具体的业务逻辑处理请求,并返回model实体给前端控制器;
    3、前端控制器再将生成的model传递给视图模板生成相应的用户视图,再次返回给前端控制器;
    4、前端控制器将页面返回给前端控制器。

    各部分的作用

    • Front Controller:分发调度
    • Controller:业务数据抽取
    • View Template:页面呈现

    ** MVC的核心思想**:业务数据的抽取和业务数据的呈现相分离(这是一种简化)

    二、MVC

    MVC
    • ** view - 视图层 **
      为用户提供UI界面,重点关注数据的呈现
      考虑如何给数据布局,将数据优美合理的展示给用户
    • ** model - 模型层**
      业务数据的信息表示,关注支撑业务的信息构成,通常是多个业务实体的组合
      考虑需要给用户展示什么才能构成模型
    • ** Controller - 控制层 **
      调用业务逻辑产生合适的数据(model),传递数据给视图层用于呈现
      考虑调用哪些业务逻辑使得可以给用户呈现正确的数据,让效率更高,性能更好

    ** MVC是一种架构模式,使得程序分层,分工合作;既相互独立,又协同工作 **

    三、Spring MVC基本概念

    1、DispatcherServlet

    Spring MVC作为前端控制器的一种实现形式,DispatcherServlet就是它的前端控制器。浏览器端(前端)用户的请求就是通过DispatcherServlet进行了分发到达一个合适Controller来生产所需要的业务数据model,model再通过DispatcherServlet进行传递,传递给view来完成最终的业务呈现。

    DispatcherServlet

    2、Controller

    根据业务逻辑生成model。

    3、HandlerAdapter

    处理器适配器,是在DispatcherServlet内部使用的类,是controller的一种表现形式,最终调用的Controller是以HandlerAdapter的形式出现的(适配器模式)

    HandlerAdapter

    4、HandlerInterceptor

    处理器拦截器,是一个接口
    具有三个方法:preHandle、postHandle和afterCompletion,会在调用Controller之前、调用Controller之后和完成页面呈现之后做一些工作。

    5、HandlerMapping

    前端控制器与Controller的映射关系,作用是告知DispatcherServlet,以哪一个Controller来响应特定的请求。

    6 、HandlerExecutionChain

    HandlerExecutionChain
    相关方法构成了执行链条:先执行HandlerInterceptor中的ProHandle,再执行相应Controller的方法,然后执行phstHandle、afterCompletion。

    这个链条是如何实现的?看源码可以知道是通过反射机制实现的。

    7 、ModelAndView

    实现model,同样的还有Model、Map,最后都转换为ModelAndView,是Model的具体表现。

    8 、ViewResolver

    视图解析器,告知DispatcherServlet使用哪一个视图呈现页面(jstl、jsp...)

    9 、Spring MVC工作模块划分(动态)

    Request流程

    除了Controller要根据业务实现,其他部分大都是通过配置实现。

    四、Spring MVC项目搭建

    Github地址:CharacterCounter2

    看视频、查资料,捣鼓了老大半天才把一个简单的测试Demo搭建好,实在是心力憔悴...以下几点体会:

    • Spring MVC极度依赖配置文件,如果配置文件有误,将导致项目出错;
    • 在集成了Maven的IDEA中搭建Spring MVC项目,需要掌握IDEA和Maven的使用技巧,尤其是在Maven的依赖管理方面、IDEA的特性方面要注意,否则掉进坑里爬不出来。
    1、新建Maven Spring MVC项目

    参考之前的文章:创建Maven项目 - 命令行 | IDEA - 使用本地项目模板

    2、配置文件 和 建立目录结构

    这里主要配置3个配置文件:
    pom.xml web.xml mvc-dispatcher-servlet.xml

    (1)配置pom.xml

    如前面学习的Maven知识,pom.xml是Maven的配置文件,主要进行项目管理、依赖管理等。

    感受最深的一点是,之前用非Maven开发Java项目,jar的引入是没有特别的管理方法的,但是Maven的依赖管理做得很好:所有的jar包都存放在Maven的本地资源库里(我的本地资源库在setting.xml中做了配置,改为了D:\Mobile DVE\maven\LocalWareHouse下),如果本地资源库中没有需要的jar文件,就会从中央存储库中下载(我这里默认从阿里云的托管库中下载了):

    本地资源库

    如果自动从中央存储库中下载故障,可以用浏览器到中央下载,再放到指定的本地存储库目录下。不过只要配置对了lib的坐标,应该都能下载得到。

    配置引入org.springframework

    注意IDEA的提示:

    IDEA会自动完成jar的加载、引入

    ** 定义和使用变量 **

     <!--定义变量-->
    <properties>
        <commons-lang.version>2.6</commons-lang.version>
        <slf4j.version>1.7.6</slf4j.version>
        <spring.version>4.1.3.RELEASE</spring.version>
    </properties> 
    

    这些变量可以被用来统一版本号,在后面的坐标配置中可以使用,比如使用<spring.version>来配置spring的版本:


    引用变量:${变量名}
    (2)配置web.xml

    这个是Servlet的配置项,在这里配置使得spring的DispatcherServlet接手相关工作,指定DispatcherServlet的对应xml配置文件(也就是mvc-dispatcher-servlet.xml),并且指定DispatcherServlet能够拦截哪些请求等。

    <servlet>
        <servlet-name>mvc-dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--DispatcherServlet对应的上下文配置,默认为/WEB-INF/$servlet-name$-servlet.xml,我们做了改变-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/configs/spring/mvc-dispatcher-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>mvc-dispatcher</servlet-name>
        <!--使mvc-dispatcher拦截所有请求-->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    

    ** <servlet-name> **:可自定义,不过要注意这个名称与DispatcherServlet的上下文配置文件有关,配置文件的构成规则:

      $servlet-name$-servlet.xml
    

    ** contextConfigLocation ** :DispatcherServlet的上下文配置文件路径
    ** <servlet-mapping> **:指定mvc-dispatcher将拦截的请求,“/”表示拦截所有请求

    (3)配置mvc-dispatcher-servlet.xml

    如(2)中所述,这个文件的名字是任意且有规则的。

    <beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd  ">
    
    <!--使DispatchServlet开启基于annotation的HandlerMapping-->
    <mvc:annotation-driven/>
    
    <!--激活@Required、@Autowired、JSR 250's、@PostConstruct等标注-->
    <context:annotation-config/>
    
    <!--DispatcherServlet上下文,把标记了@Controller注解的类转换为bean,而不搜索其他标注的类 -->
    <context:component-scan base-package="com.qunar.controller">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    
    <!-- 启动Spring MVC的注解功能,完成请求和注解POJO的映射 -->
    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
    
    <!-- 对模型视图名称的解析,即在模型视图名称添加前后缀:/WEB-INF/jsps/文件夹下的jsp文件 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsps/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    
    </beans>
    

    注释中记录得很清楚了,唯一要注意的就是按照配置文件中的描述建立相应的层级目录

    层级目录

    不过这里有一个巨坑!!!

    被坑了2个多小时。

    注意 <context:component-scan base-package="com.qunar.controller"> 这个地方,指定的是Controller的包路径,不过之前在执行的时候扫面不到我写的Controller,所以不会去做mapping,URL访问的时候找不到页面:


    404

    控制台的提示:

    TomcatServer控制台警告

    难道是我配置文件写的有问题?改来改去不晓得问题在哪里,后来在这里找到了一定灵感:SpringMVC配置问题 - 开源中国社区

    虽然他说的也不是很清楚,不过可以有一个定义问题的出路:** 我的com.qunar.controller没有被扫面,所以不会mapping **。

    为什么没有被扫描呢?因为昨天刚记录了这个:IDEA项目不能新建package和class的解决,猜测可能是因为没有将这个目录** Make Directory As - Source Root **,即将目录设置为资源根目录导致的。

    设一下,果然成了。

    Mapped com.qunar.controller.TestController.testMVC()

    不得不吐槽下,坑啊,从Eclipse迁移过来没多久,怎么会get到这个梗!

    3、编写测试Controller
    package com.qunar.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller                     // 将这个类注册为Controller
    @RequestMapping("/test")        // 拦截相应路径:host/test
    public class TestController {
        @RequestMapping("/hello")   // 拦截相应路径:host/test/hello
        public String testMVC(){
            System.out.println("拦截相应路径:host/test/hello");
            return "index";         // 返回ViewTemplate
        }
    }
    
    4、编写测试jsp
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Hello String MVC</title>
    </head>
    <body>
        <h1>Successful!</h1>
    </body>
    </html>
    
    5、配置Tomcat并运行

    在IDEA中配置Tomcat


    在IDEA中配置Tomcat

    记得在Deployment添加运行项目:

    添加部署
    6、Run
    run

    五、配置文件


    web.xml


    1、使web.xml支持EL语言

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns="http://java.sun.com/xml/ns/j2ee"
            xmlns:web="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
            xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd "
            version="2.4">
    

    2、层次化的ApplicationContext

    Spring MVC的上下文层级

    WebApplicationContext
    Spring中的ApplicationContexts可以被限制在不同的作用域。在web框架中,每个DispatcherServlet有它自己的WebApplicationContext,它包含了DispatcherServlet配置所需要的bean。DispatcherServlet 使用的缺省BeanFactory是XmlBeanFactory,并且DispatcherServlet在初始化时会在你的web应用的WEB-INF目录下寻找[servlet-name]-servlet.xml文件。DispatcherServlet使用的缺省值可以使用servlet初始化参数修改。

    ** ContextLoaderListener **
    ContextLoaderListener的作用就是启动Web容器时,自动装配ApplicationContext的配置信息。它实现了ServletContextListener这个接口,在web.xml配置这个监听器,启动容器时,就会默认执行它实现的方法。ContextLoaderListener启动的上下文为根上下文,DispatcherServlet所创建的上下文的的父上下文即为此根上下文,可在FrameworkServlet中的initWebApplicationContext中看出。

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/configs/spring/applicationContext*.xml</param-value>
    </context-param>
    
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    

    ** 多个DispatcherServlet使用同一个公共的上下文 **
    为什么会出现多个DispatcherServlet?同一个网站会提供不同类型的服务,为了更加方便,使用不同的DispatcherServlet做不同的分发,为不同类型的请求提供不同的服务,这样更加方便快捷。

    3、<servlet-mapping>

    指定DispatcherServlet拦截的请求。


    mvc-dispatcher-servlet.xml


    这个配置文件供名为mvc-dispatcher的DispatcherServlet使用,提供其相关的Spring MVC配置。

    1 、配置annotation

    <context:annotation-config/>
    激活@Required、@Autowired、JSR 250's、@PostConstruct等标注服务
    

    2 、配置DispatcherServlet上下文

    <context:component-scan base-package="com.qunar.controller">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    使得DispatcherServlet只管理@Controller类型的bean,忽略其他类型的bean,如@Service
    

    3 、配置HandlerMapping

    指导以何种方式mapping。Spring MVC默认启动DefaultAnnotationHandlerMapping。

    4 、扩充注解驱动

    <mvc:annotation-driven/>
    扩充注解驱动,可以将请求参数绑定到Controller参数。
    也就是说,URL中查询参数的变量可以直接映射到某个Controller参数中,这是一个很强大的功能。
    可以非常方便的获取、传递参数。
    

    5 、静态资源处理

    <mvc:resources mapping="/resources/" location="/resources/"/>
    指导静态资源(css、js、image)的位置
    

    6 、配置ViewResolver

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsps/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    可以有多个ViewResolver,使用order属性排序,
    

    applicationContext.xml


    applicationContext.xml详解 - - 博客频道 - CSDN.NET

    六、Controller基础代码

    @RequestMapping(value="/hello",method = RequestMethod.GET)
    public String testController(@RequestParam("id") Integer id,Model model){
        System.out.println("拦截相应路径:host/test/hello");
        // 逻辑
        TestModel testModel = new TestService().getTestModelById(id);
        // 传递model
        model.addAttribute(testModel);
        System.out.println("拦截相应路径:host/test/hello");
        // 返回ViewTemplate
        return "index";        
    }
    

    ** @RequestMapping注解**
    映射请求路径与Controller Class或Controller Method之间的关系

    ** @RequestParam**
    将请求参数绑定到Controller参数

    ** Model**
    Controller和View(Jsp File)之间传递的模型实体:

    // Controller
    model.addAttribute(Object obj);
    
    // Jsp
    <h3>${testModel.name}</h3>
    

    使用HttpServletRequest对象的方法:传入HttpServletRequest对象:

    @RequestMapping("hello2")
    public String testController(HttpServletRequest request) {
        Integer id = Integer.valueOf(request.getParameter("id"));
        TestModel m = new TestModel("使用HttpServletRequest对象",id);
        request.setAttribute("testModel",m);
        return "index";
    }
    

    七、Binding 绑定

    ** Binding **:将请求中的参数字段按照名字原则(名称匹配)填入对象模型。

    web页面由各种标签所代表的控件组成,在前(请求端)后(服务端)端进行数据传递时,可以按照“名称匹配”的原则将前端请求参数(表单数据)传到后端的对象模型中。

    Model与表单关系示意
    /**
     * 通过绑定(binding)获取到表单数据并直接得到对应的model
     * 注意是按名称对应
     * @param tm
     * @return
     */
    @RequestMapping(value = "/save",method = RequestMethod.POST)
    public String doSave(@ModelAttribute TestModel tm) {
        System.out.println(tm.toString());
        return "redirect:hello?id=" + tm.getId();
    }
    

    ** @ModelAttribute 绑定 **
    参数中是一个模型实体,它有两个属性域:id和name;页面中的form表单也有同样名称的两个控件,这样就通过名称匹配的方式将前端的请求参数同后端的模型绑定了,很方便。

    ** 重定向 **
    在代码中可以看到Spring中的重定向方式:

    return "redirect:hello?id=" + tm.getId();

    ** 请求转发 **

    return "forward:hello";

    八、FileUpload 单文件上传

    Spring MVC提供了文件上传的内置支持,将其作为一个公共的服务,我们只需要通过简单的配置就可以使用接口完成文件上传工作。

    1、配置文件

    配置mvc-dispatcher-servlet.xml,增加bean:CommonsMultipartResolver

    <!--配置文件上传-->
    <bean id="multipartResolver"
          class="org.springframework.web.multipart.commons.CommonsMultipartResolver ">
        <!--限制文件上传最大限制:200*1024*1024=200M-->
        <property name="maxUploadSize" value="209715200" />
        <property name="defaultEncoding" value="UTF-8" />
        <!-- 启用resolveLazily属性是为了推迟文件解析,以便捕获文件大小异常 -->
        <property name="resolveLazily" value="true" />
    </bean>
    

    2、引入包

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

    3、前端页面

    ** (1)form**

    <form method="post" action="/courses/doUpload" enctype="multipart/form-data">
    action:相应方法
    enctype:属性为multipart/form-data,表示上传文件
    

    **(2)input **

    <input type="file" name="file"/>
    

    4、编写处理方法

    @RequestMapping(value="/doUpload", method=RequestMethod.POST)
    public String doUploadFile(@RequestParam("file") MultipartFile file)  throws IOException {
        if(!file.isEmpty()){
            FileUtils.copyInputStreamToFile(file.getInputStream(), new File("D:\\", System.currentTimeMillis()+ file.getOriginalFilename()));
        }
        return "success";
    }
    

    ** MultipartFile对象 **

    // source
    public interface MultipartFile {
        java.lang.String getName();获取表单中文件组件的名字
        java.lang.String getOriginalFilename();获取上传文件的原名
        java.lang.String getContentType();获取文件MIME类型
        boolean isEmpty();文件是否为空
        long getSize();获取文件的字节大小,单位byte
        byte[] getBytes() throws java.io.IOException;获取文件bytes
        java.io.InputStream getInputStream() throws java.io.IOException;获取文件流
        void transferTo(java.io.File file) throws java.io.IOException, java.lang.IllegalStateException;保存到一个目标文件中
    }
    

    ** @RequestParam("file") MultipartFile file **
    绑定参数,<input>的name和Controller的参数名保持一致

    ** FileUtils.copyInputStreamToFile **
    获取文件流,写入文件

    九、Spring MVC与json

    4-7 JSON(上)(03:41)
    4-8 JSON(中)(05:23)
    4-9 JSON(下)(04:46)

    相关文章

      网友评论

        本文标题:Spring MVC学习笔记

        本文链接:https://www.haomeiwen.com/subject/kabibttx.html