关于SpringMVC的使用总结

  • 简介

springMVC即Spring Web MVC,是spring web模块的一部分,是spring自己的web框架

springMVC对Servlet API 进行了完善的封装,极大的简化了开发人员的编程工作。同时springMVC也提供了友好简便的方式让开发人员可以使用Servlet API,十分灵活。

  • Servlet的操作方式

  • springMVC的操作方式

maven 依赖和插件配置:

<dependencies>
    <!-- spring最基本的环境支持依赖,会传递依赖core、beans、expression、aop等基本组件,以及commons-logging、aopalliance -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.3.2.RELEASE</version>
    </dependency>
    
    <!-- 提供了对其他第三方库的内置支持,如quartz等 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>4.3.2.RELEASE</version>
    </dependency>
    
    <!-- spring处理对象关系映射的组件,传递依赖了jdbc、tx等数据库操作有关的组件 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>4.3.2.RELEASE</version>
    </dependency>
    
    <!-- spring对面向切面编程的支持,传递依赖了aspectjweaver -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>4.3.2.RELEASE</version>
    </dependency>
    
    <!-- spring处理前端表现层的组件,即springMVC,传递依赖了web操作有关的组件 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>4.3.2.RELEASE</version>
    </dependency>
        
    <!-- 数据校验,springMVC需要用到 -->
    <dependency>
       <groupId>org.hibernate</groupId>
       <artifactId>hibernate-validator</artifactId>
       <version>5.2.4.Final</version>
    </dependency>
    
    <!-- json解析,springMVC需要用到 -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.6.0</version>
    </dependency>
    
     <!-- 文件上传,springMVC需要用到 -->
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.3.1</version>
    </dependency>
    
    <!-- junit -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
    </dependency>
    
    <!-- servlet依赖 -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>
    
    <!-- jsp依赖 -->
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.2</version>
        <scope>provided</scope>
    </dependency>
    
    <!-- jstl依赖 -->
    <dependency>
        <groupId>org.glassfish.web</groupId>
        <artifactId>jstl-impl</artifactId>
        <version>1.2</version>
        <exclusions>
            <exclusion>
                <groupId>javax.servlet</groupId>
                <artifactId>servlet-api</artifactId>
            </exclusion>
            <exclusion>
                <groupId>javax.servlet.jsp</groupId>
                <artifactId>jsp-api</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>
  
<build>
    <plugins>
        <!-- 指定JDK编译版本 -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.1</version>  
            <configuration>  
              <source>1.8</source>
              <target>1.8</target>
            </configuration> 
        </plugin>
    </plugins>
</build>
  • 核心控制器——DispatcherServlet

springMVC提供了一个核心控制器(大总管)——DispatcherServlet,负责统一调用springMVC提供的其他组件,按照固定步骤处理请求,生成响应。

 

在web.xml中配置DispatcherServlet :

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:dispatcher-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
  • DispatcherServlet结构

  • springMVC的核心配置文件

springMVC是spring的一部分,需要spring上下文的支持,所以需要一个像beans.xml一样的配置文件,我们把这个配置文件命名为dispatcher-servlet.xml(其他名称也可以)

除了以bean的方式配置、管理springMVC的组件外,springMVC的其他内容也需要在这个文件中配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
                        http://www.springframework.org/schema/beans/spring-beans.xsd  
                        http://www.springframework.org/schema/mvc 
                        http://www.springframework.org/schema/mvc/spring-mvc.xsd 
                        http://www.springframework.org/schema/context 
                        http://www.springframework.org/schema/context/spring-context.xsd 
                        http://www.springframework.org/schema/aop 
                        http://www.springframework.org/schema/aop/spring-aop.xsd 
                        http://www.springframework.org/schema/tx 
                        http://www.springframework.org/schema/tx/spring-tx.xsd">
                        
    <!-- 配置扫描spring注解时扫描的包,同时也开启了spring注解支持 -->
    <context:component-scan base-package="com.rupeng.web" />

    <!-- 开启springMVC相关注解支持 -->
    <mvc:annotation-driven />

    <!-- 视图解析器,视图页面的全路径为 prefix + viewName + suffix -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!-- 文件上传解析器 -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!--整个请求的最大大小,用来限制上传的文件大小-->
        <property name="maxUploadSize" value="20971520" />
        <property name="defaultEncoding" value="UTF-8" />
        <!--延迟解析,以便捕获文件大小超出限制的异常,方便生成错误提示信息-->
        <property name="resolveLazily" value="true"/>
    </bean>
        
    <!-- 资源映射 -->
    <mvc:resources location="/css/" mapping="/css/**" />
    <mvc:resources location="/js/" mapping="/js/**" />
    <mvc:resources location="/images/" mapping="/images/**" />
</beans>

这个文件中配置的都是和web有关的内容,至于项目的其他部分,比如service、dao等也可以配置在这个文件里面。但更一般的,会初始化两个spring上下文,一个用来管理springMVC有关的内容,另一个用来管理项目其他内容

  • 额外的字符编码过滤器

springMVC还提供了一个字符编码过滤器,可以对POST请求和响应设置编码(tomcat8会自动对GET请求进行编码):

<!-- 设置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>
    <init-param>
        <!-- 为true时也对响应进行编码 -->
        <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>
  • DispatcherServlet处理时序图

URL—handler映射

springMVC通过封装,替开发人员做了很多事情,对于那些必须由开发人员来实现的部分,也提供了一整套机制让开发人员实现起来更加简单

开发人员通过实现自己的handler,对请求进行具体的业务处理。实现方式:

  • 声明一个普通类并使用@Controller(不能使用其他方式)把该类标注成spring的bean

  • 使用@RequestMapping把该类的方法标注为handler

@RequestMapping有两个常用的属性:value和method

value指定匹配的请求路径,单独使用时可省略此属性名

method 指定匹配的请求方法(GET、POST),值为枚举类型RequestMethod

 

@RequestMapping也可以标注在类上,为此类所有的handler统一指定"父路径"

  • handler方法的参数类型

handler方法的参数支持很多类型,不同的类型会有不同的用法和效果

支持Servlet API

即HttpServletRequest、HttpServletResponse、HttpSession等。

如果handler方法声明了以上类型的参数,方法执行时springMVC会把和当前请求相关的这些对象注入到方法参数。

其实只要拿到了request、response对象,完全可以按照普通的Servlet方式处理当前请求,这也体现了springMVC高度的灵活性。

还可以通过RequestContextHolder工具类在任何springMVC环境下访问Servlet API:

HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

支持常用类型

包括基本类型及其包装类型、String、Date等

如果handler方法有以上类型的参数,再配合使用相关的注解,springMVC可以自动从请求中获取相应的值,并且进行类型转换等。

常用注解:

  • @RequestParam 获取指定请求参数,相当于request.getParameter()
  • @RequestHeader 获取指定请求头,相当于request.getHeader()
  • @CookieValue 获得指定cookie的value值
  • @SessionAttribute 获取指定session域的属性值,相当于session.getAttribute()

注意:这几个注解的required属性默认为true,即值不能为null

特别的,新版本的springMVC(比如当前使用的版本),@RequestParam可以省略,springMVC会根据参数名称尝试操作,且允许值为null

对于Date类型的参数,由于springMVC在简体中文环境下默认的日期格式不太适用,可自定义日期格式:

@InitBinder
protected void initBinder(WebDataBinder binder) {
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
    binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}

支持pojo类

如果handler方法的参数是pojo类型,可以配合使用@ModelAttribute,springMVC会自动创建此pojo类对象,并尝试从请求参数中获取pojo字段相应的值、类型转换、数据绑定等

特别的,可以省略@ModelAttribute

特别的,可以配合使用@Valid、BindingResult,对pojo类的字段进行有效性校验

支持Map、Model、ModelMap

这三个实际上是一样的。即使不在handler方法中声明Model参数,springMVC内部也会对每个请求创建一个Model对象,而且持有pojo参数(如果有)的引用。

Model中可以存入一些键值对数据,最终Model中的数据会被添加到request中,所以也可以直接把键值对存入request。这么看来Model似乎是多余的,其实Model可以和spring自定义标签配合使用,为其提供pojo对象

此外,handler方法还支持其他类型,如RedirectAttributes、MultipartFile等

  • handler方法的返回值类型

支持String、ModelAndView

String类型的返回值表示视图名称

ModelAndView就相当于方法参数Model + 返回值String,比较常用

特别的,视图名称使用redirect: 前缀表示重定向到另一个handler(不使用视图解析器),配合使用RedirectAttrbutes方法参数,springMVC会生成带参数的重定向URL

特别的,视图名称使用forward: 前缀表示转发到另一个handler(不使用视图解析器)

支持pojo类

对于ajax请求来说,一般需要返回json格式的字符串数据。springMVC提供了@ResponseBody注解,当标注在handler方法的返回值之前时,可以自动把返回对象转换为json格式的字符串并发送到客户端。

springMVC把对象转换为json格式字符串使用的是Jackson工具包,maven依赖如下(已配置好):

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.6.0</version>
</dependency>

对于Date类型的字段,可以使用Jackson提供的@JsonFormat指定日期格式(标注在字段上)

支持void

如果handler方法的返回值类型是void,则springMVC会认为开发人员已经使用Servlet API 生成了响应,就不再继续处理了。

特别的,无论返回值类型是什么,return null和void含义是一样的。

  • 视图层

可以使用JSP页面作为视图层,常用的技术组合为JSP + EL + JSTL(+ spring form标签库)

spring form标签库

spring提供了两个标签库,主要的是form标签库,常用的有<form>、<input>、<checkboxes>、<radiobuttons>、<select>、<errors>等

在使用这些标签时,要求handler方法需要有pojo参数,并且其他标签需要放到<form>标签内部

  • <form>会生成HTML的form标签,主要用来为其他spring标签提供Model中的pojo对象。modelAttribute属性值需要和pojo参数名一致
  • <input> 默认生成HTML的input(text)标签,path属性值为pojo类的某个字段名
  • <checkboxes> 使用数组、集合生成一组input(checkbox)标签
<form:checkboxes items="${allRoleList }" path="roleList" itemLabel="name" itemValue="id" />

<radiobuttons> 和<checkboxes>用法相似 

  • <select> 和<checkboxes>用法相似,可以使用multiple="false"属性指定不允许多选
  • <errors>可以输出某个字段的验证失败信息,也可以输出全部字段的验证失败信息

<form>的action属性不支持JSP表达式<%= %>,可以使用${pageContext.request.contextPath}的方式取得项目路径

这里对于日期字段来说,可以使用@DateTimeFormat标注在字段上指定日期格式

在实际使用时,既可以使用springMVC提供的这套标签,也可以使用EL+JSTL的方式。如果对页面样式要求较高,可能并不适合使用这些spring form标签

  •  数据校验

当handler方法的参数是pojo类时,就可以使用spring提供的一套数据校验方案对pojo类中的字段值进行有效性校验

使用hibernate提供的校验实现

maven依赖(已配置好):

<dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-validator</artifactId>
   <version>5.2.4.Final</version>
</dependency>

把校验器配置为bean,id需要指定为validator:

<bean id="validator"
         class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    <property name="providerClass"  value="org.hibernate.validator.HibernateValidator"/>
</bean>

校验相关注解

@Valid 标注在handler方法的pojo参数前面,相当于此pojo对象的校验开关

pojo参数后面需要紧跟BindingResult参数,记录校验结果。

可以使用spring form标签库的<errors>标签把校验失败信息输出在页面上

下面的注解都标注在字段上,表示对标注的字段应用某种校验规则,可以直接在注解上指定校验失败时的错误信息(不指定时使用默认的)

@NotEmpty 此字段不能为null,也不能为空字符串

@Length 可以指定字符串的最小、最大长度

@Min 指定数值的最小值

@Max 指定数值的最大值

@Range 指定数值的范围

@Size 指定数组、集合的元素范围

@AssertTrue 断定boolean值为true

@AssertFalse

@Email 指定字符串符合email格式

@Pattern 字符串符合指定的正则表达式

在实际使用时,既可以采用这种校验方式,也可以使用普通的校验方式,即手动对需要校验的字段进行校验,然后向request中添加校验失败的信息,然后在页面中使用EL表达式把失败信息输出到页面上,普通校验方式大家比较熟悉,而且非常灵活。

  •  springMVC拦截器

springMVC的interceptor和Servlet的filter非常相似,可以拦截请求进行预处理和后处理

  • 实现HandlerInterceptor接口编写自己的拦截器(或者继承HandlerInterceptorAdapter)
  • 在dispatcher-servlet.xml文件中配置此拦截器
<mvc:interceptors> 
         <mvc:interceptor>
                   <mvc:mapping path="/xx/*" />
                   <mvc:exclude-mapping path="/xx/xx.do"/>
                   <bean class="xx" />
         </mvc:interceptor>
</mvc:interceptors>

可以使用若干个<mvc:mapping>指定拦截的请求,支持 * 通配符 

也可以使用<mvc:exclude-mapping>指定不拦截的请求,一般和<mvc:mapping>配合使用从拦截的请求中排除某些请求

servlet的filter调用链和springMVC的interceptor调用链执行时候的差别:

 

  • 文件上传

springMVC封装了Commons FileUpload 普通文件上传方式,简化了文件上传操作。(此外还支持servlet3.0方式的文件上传)

普通文件上传方式

maven依赖(已配置好):

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

文件上传解析器,id需要指定为multipartResolver(已配置好): 

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!--整个请求的最大大小,用来限制上传的文件大小-->
    <property name="maxUploadSize" value="20971520" />
    <property name="defaultEncoding" value="UTF-8" />
    <!--延迟解析,以便捕获文件大小超出限制的异常,方便生成错误提示信息-->
    <property name="resolveLazily" value="true"/>
</bean>

接下来在handler方法上使用MultipartFile类型的参数即可(@RequestParam 注解可省略) 

MultipartFile 常用方法:

getContentType() 获得上传文件的类型

getOriginalFilename() 获得文件原始名称

getSize() 获得上传文件的大小

getInputStream() 获得上传文件的读取流

transferTo(dest) 把上传的文件复制到目标文件

 

当然,还对页面的form表单有要求:<form method="post" enctype="multipart/form-data">

 

处理文件大小超出限制时的错误提示信息:

@ExceptionHandler(MultipartException.class)
public ModelAndView handleException(MultipartException ex) {
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("message", "上传文件的大小不能超过20M");
    return modelAndView;
}
  • 异常解析器

通过实现HandlerExceptionResolver接口可以编写自己的异常解析器,注册为bean后可以捕获服务器运行时抛出的异常

通常会在异常解析器中记录异常日志信息,还可以根据请求的类型(普通请求、ajax请求)生成合适的错误提示

@Component
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {

        //实际项目中要改为记录日志
        ex.printStackTrace();

        //判断请求的类型
        if (request.getHeader("X-Requested-With") != null) {  //ajax请求
            try {
                response.getWriter().print("{\"status\":\"error\", \"data\":\"服务器出错了\"}");
            } catch (IOException e) {
                //实际项目中要改为记录日志
                e.printStackTrace();
            }
            //此处返回没有视图名称的ModelAndView对象表示开发人员自己生成了响应
            return new ModelAndView();
        } else {  //普通请求
            return new ModelAndView("500");
        }
    }
}

 

posted on 2019-02-25 17:23  朱*力  阅读(307)  评论(0编辑  收藏  举报

导航