文档章节

SpringMVC总结篇

向码而生
 向码而生
发布于 2017/07/22 11:21
字数 3384
阅读 62
收藏 2

SpringMVC的很多特性使用略少,些许内容有些遗忘,开篇对SpringMVC进行系统总结。

IDEA创建项目

一般我会使用IntelliJ IDEA来创建Maven的web项目(File -> New -> Project -> Maven -> Create from archetype -> maven-archetype-webapp), 用该模版创建出来的是一个空的maven web项目。有两个操作需要注意:

    1. src/main下创建java文件夹,并在Project Structure下标注为Sources。

    2. Add framework support, 增加web.xml, applicationContext.xml, dispatcher-servlet.xml文件。

操作完上述两部,才是一个完整的Web项目,可以创建Package以及Java Class等,同时需要简单修改web.xml以及dispatcher-servlet.xml文件内容。

 

SpringMVC基础

    1. SpringMVC中的@RequestMapping可以修饰类和方法,支持的属性有value(缺省), method, params, header,并且支持value路径使用ant-stype通配符(?/*/**) 

    2. @PathVariable注解:映射URL中绑定占位符到目标方法的参数中。

@Controller
public class TestHelloWorld {

    private final static String SUCCESS = "success";

    @RequestMapping("/hello")
    public String sayHello() {
        return SUCCESS;
    }

    @RequestMapping(value = "/testMethod", method = RequestMethod.POST)
    public String testMethod() {
        return SUCCESS;
    }

    @RequestMapping(value = "/testPathVariable/{id}", method = RequestMethod.GET)
    public String testPathVariable(@PathVariable Integer id) {
        System.out.println(id);
        return SUCCESS;
    }

    @RequestMapping(value = "/testParams", method = RequestMethod.GET, params = {"name", "age!=10"})
    public String testParams() {
        return SUCCESS;
    }

    /**
     * ?通配一个字符
     * *通配0或者任意数量的字符
     * **通配0或者更多的目录
     *
     * @return
     */
    @RequestMapping(value = "/testWildcards/?/test", method = RequestMethod.GET)
    public String testWildcards() {
        return SUCCESS;
    }
}

    3. Spring3.0后使用HiddenHttpMethodFilter过滤器支持RESTFUL API。

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>
@RequestMapping("/restSupport")
@Controller
public class TestRestfulAPI {

    private final static String SUCCESS = "success";

    @RequestMapping(value = "/testRestGet/{id}", method = RequestMethod.GET)
    public String testRestGet(@PathVariable("id") Integer id) {
        System.out.println("testRestGet: " + id);
        return SUCCESS;
    }

    @RequestMapping(value = "/testRestPost", method = RequestMethod.POST)
    public String testRestPost() {
        System.out.println("testRestPost");
        return SUCCESS;
    }

    @RequestMapping(value = "/testRestPut/{id}", method = RequestMethod.PUT)
    @ResponseBody()
    public String testRestPut(@PathVariable("id") Integer id) {
        System.out.println("testRestPut: " + id);
        return SUCCESS;
    }

    @RequestMapping(value = "/testRestDelelte/{id}", method = RequestMethod.DELETE)
    @ResponseBody()
    public String testRestDelete(@PathVariable("id") Integer id) {
        System.out.println("testRestDelete: " + id);
        return SUCCESS;
    }
}

备注:在Tomcat8.x以及Spring4.x下调用PUT和DELETE的RESTFUL API时,报错:SpringMVC: HTTP Status 405 - JSPs only permit GET POST or HEAD,通过添加@RestponseBody()解决该问题。

    4. @RequestParam 映射URL的请求参数(value, required, defalutValue)

@RequestMapping("/testRequestParam")
public String testRequestParam(@RequestParam(value = "username") String un, @RequestParam(value = "age", required = false) Integer age) {
    System.out.println("username: " + un + ", age: " + age);
    return SUCCESS;
}

    5. @RequestHeader 映射请求头信息

@RequestMapping("/testRequestHeader")
public String testRequestHeader(@RequestHeader(value = "Accept-Language") String header) {
    System.out.println(">>>>>>>>>>>>>>" + header);
    return SUCCESS;
}

    6. @CookieValue 可以把Request header中关于cookie的值绑定在方法的参数上

@RequestMapping("/testCookieValue")
public String testCookieValue(@CookieValue("JSESSIONID") String sessionID) {
    System.out.println("CookieValue: " + sessionID);
    return SUCCESS;
}

    7. SpringMVC支持使用POJO作为参数[常用]:SpringMVC会按传递的参数与POJO参数的属性一一对应,进行匹配,并且支持级联属性。【备注】如果不使用SpingMVC的该属性,使用@RequestParam需要一个参数一个参数的获取,使用Servlet API也需要通过request.getParameter("key")来进行传递。

   8. 使用Servlet原生API作为参数,支持HttpServletRequest, HttpServletResponse, HttpSession, java.security.Principal, Locale, InputStream, OutputStream, Reader, Writer作为参数传递。

    9. ModelAndView:在MVC设计模式中,发送一个请求到目标处理器,目标处理器调用业务方法,业务方法会有返回值(例如:一个对象或者一个集合),然后转发到页面(需要把业务方法的返回值在页面上显示出来)。

@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView() {
    String viewPage = SUCCESS;
    ModelAndView mv = new ModelAndView(viewPage);
    mv.addObject("time", new Date());
    return mv;
}

    通过ModelAndView将数据放进requestScope中,然后在页面中通过${requestScope.time}获取,如果无法解析${requestScope.time},可以设置jsp的isELIgnored="false"参数->表示在此JSP页面中执行EL表达式。

    10. 模型Model以及Map,使用Map或者Model作为参数,将数据传递给页面。解析:将Model或者Map作为参数传递给目标方法,然后把数据存入其中,可以轻松的传给页面。SpringMVC会将他们转化为ModelAndView,viewName为SUCCESS,Model就是这里的Map或者Model.

@RequestMapping("/testModel")
public String testModel(Model model) {
    model.addAttribute("time", new Date());
    return SUCCESS;
}

@RequestMapping("/testMap")
public String testMap(Map<String, Object> map) {
    map.put("time", new Date());
    return SUCCESS;
}

    11. @SessionAttributes, 将数据放进session域中。

@SessionAttributes(value = "student", types = Student.class)
@RequestMapping("/restSupport")
@Controller
public class TestRestfulAPI {

    private final static String SUCCESS = "success";

    @RequestMapping("/testSessionAttribute")
    public ModelAndView testSessionAttribute() {
        ModelAndView modelAndView = new ModelAndView(SUCCESS);
        Student student = new Student();
        student.setId(100);
        student.setName("Tom");
        student.setAge(20);
        modelAndView.addObject("student", student);
        return modelAndView;
    }
}
<h2>${requestScope.student}</h2>
<h2>${sessionScope.student}</h2>

    12. @ModelAttribute: 被@ModelAttribute注解的方法会在此Controller每个方法执行前被调用。常用的几种方式如下:

(1). @ModelAttribute注解修饰返回值为void的方法

@ModelAttribute
public void testModelAttribute001(@RequestParam String abc, Model model) {
    model.addAttribute("testAttribute", abc);
}

(2). @ModelAttribute注解修饰返回值为具体类的方法

@ModelAttribute
public Student getStudent() {
    System.out.println("getStudent method...");
    Student student = new Student();
    student.setId(2001);
    student.setName("Jobs");
    student.setAge(23);
    return student;
}

(3). @ModelAttribute(value = "xxx")注解修饰返回值为具体类的的方法

@ModelAttribute("testAttribute")
public Student testModelAttribute002(@RequestParam Student student) {
    return student;
}

(4). @ModelAttribute和@RequestMapping注解修饰同一个方法:在调用/helloWorld的时候,会将"testAttribute"和返回值“helloworld”放进model中。

@RequestMapping("/helloWorld")
@ModelAttribute("testAttribute")
public String sayHelloWorld() {
    System.out.println("sayHelloWorld method....");
    return "helloworld";
}

(5). @ModelAttribute注解修饰一个方法的参数

@ModelAttribute
public void sayHello(Map<String, Object> map) {
    //模拟操作
    Student student = new Student();
    student.setId(100);
    student.setName("Cat");
    student.setAge(25);

    map.put("student", student);
}

@RequestMapping("/testModelAttribute")
public String testModelAttribute(@ModelAttribute("student") Student student) {
    System.out.println(student);
    return SUCCESS;
}

【备注】执行流程如下:

    (1). 执行@ModelAttribute注解修饰的方法:从数据库中(此处模拟)取出对象,把对象放入到了Map中,键为“student”.

     (2). SpringMVC从Map中取出Student对象,并把表单的请求参数赋给该Student对象的对应属性。

     (3). SpringMVC把上述对象传入目标方法的参数。

注意:在@ModelAttribute修饰的方法中,放入到Map时的键需要和目标方法入参类型的“第一个字母小写的字符串”一致。

    13. SpringMVC视图解析器ViewResolver : 当对SpringMVC的资源发起请求时,这些请求会被SpringMVC的DispatcherServlet处理,然后Spring会通过分析HandlerMapping定义的所有请求映射,并找到合适的映射,然后通过该HandlerMapping取得其对应的handler,接着通过HandlerAdapter处理该handler。该HandlerAdapter处理handler后会返回一个ModelAndView对象,获得了ModelAndView对象后,Spring需要把该view渲染给用户,即返回给浏览器。在该过程中,发挥作用的是ViewResolver,常用的InternalResourceViewResolver为常用的接口ViewResolver的实现。

<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/"/>
    <property name="suffix" value=".jsp"/>
</bean>

    14. 国际化:只要在classpath下添加了jstl.jar,view即从InternalResourceViewResolver变为JstlView.

需要在SpringMVC配置文件中配置国际化资源文件。

<bean id="resourceBundleMessageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basename" value="i18n"></property>
</bean>

    15. 在SpringMVC配置文件中使用mvc:view-controller标签:在springMVC中使用mvc:view-controller标签可以直接访问URL和视图进行映射,而无需通过控制器。

<mvc:view-controller path="/testController" view-name="success"/>

采用该配置,默认其他使用@Controller修饰的URL路径都不可访问,如果两者兼容,那么需要配置

<mvc:annotation-driven />

    16. 自定义视图类 implements View接口 : 

    17. SpringMVC的重定向 : redirect关键字

@RequestMapping("/testRedirect")
public String testRedirect() {
    System.out.println("testRedirect method...");
    return "redirect:hello";
}

@RequestMapping("/hello")
public String sayHello() {
    System.out.println("hello method...");
    return SUCCESS;
}

 18.<mvc:annotation-driven /> : 会自动注册DefaultAnnotationHandlerMapping, AnnotationMethodHandlerAdapter与ExceptionHandlerExceptionResolver三个bean, 还提供以下支持:

(1). 支持使用ConversionService实例对表单参数进行类型转换

(2). 支持使用@NumberFormatannotaion、@DataTimeFormat注解完成数据类型的格式化

(3). 支持使用@valid注解对javabean实例进行JSR303验证

(4). 支持使用@RequestBody和@ResponseBody注解

   19. 数据转换 & 数据格式化 & 数据校验

    数据转换:

    例如:表单传入的数据为String, 而JavaBean对应的类型可能为Integer, Long, Boolean等,SpringMVC是如何将表单String类型转换为JavaBean的Integer等类型的呢?这里就讲解了数据转换的过程。

    流程解析:SpringMVC将ServletRequest对象以及目标方法入参实例(例如:JavaBean实例或者Controller方法的参数等)传递给WebDataBinderFactory实例,以创建dataBinder实例。而dataBinder调用装配在SringMVC上下文中的ConversionServife组件进行数据类型的转换、数据格式化工作。dataBinder调用Validator组件进行数据合法性校验,并最终生成数据绑定结果。SpringMVC从BindingResult中抽取结果。

    SpringMVC已经默认内建了很多类型转换器。

    数据格式化:

    需求1: 表单提交String类型的2017-08-08,格式化为JavaBean的Date类型。

    需求2: 表单提交String类型的123,345.9, 转换为JavaBean的Float类型。

    分别在JavaBean对应属性上添加注解

    @DateTimeFormat(pattern="yyyy-MM-dd")@NumberFormat(pattern="#,###,###.#")解决该问题。

    数据校验:

    使用JSR 303 以及hibernate validator框架做校验。

   20. 自定义类型转换器

    例如:将表单提交的字符串转换为JavaBean实例?

    Step1. 自定义一个转换器类(例如:MyConversionService)并实现Converter接口,重写converter()方法。注意给该转换器类添加@Conponent注解,让其注入Spring IOC容器中。

     Step2. SpringMVC中配置ConversionServiceFactoryBean

<mvc:annotation-driven conversion-service="conversionService" />

<bean name="conversionService"   class="org.springframework.context.support.ConversionServiceFactoryBean">  
<property name="converters">  
<set>  
    <ref bean="myConversionService"></ref>  
</set>  
</property>

   21. @InitBinder解决SpringMVC中类型转换问题。最常见的问题是表单提交的日期字符串和JavaBean的Date类型的转换,Spring默认不支持这种转换,需要手动配置,自定义数据绑定。

@InitBinder
public void initBinder(WebDataBinder dataBinder) {
    dataBinder.setDisallowedFields("userId");
}

    22. 错误消息显示以及国际化:

    使用<form:errors path="xxx"></form:errors>在前端显示错误消息。

  国际化:配置国际化资源文件i1n8.properties以及SpringMVC中配置国际化bean:ResourceBundleMessageSource。

    23. 返回Json & HttpMessageConverter

添加Jackson的dependency -> 使用MappingJackon2HttpMessageConverter这个HttpMessageConverter接口的实现

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

同时添加@ResponseBody注解,完成javabean到json的转换。

    @RequestMapping(value = "testJson", method = RequestMethod.GET)
    public @ResponseBody Student testJson() {
        Student student = new Student();
        student.setId(100011);
        student.setAge(29);
        student.setName("maliang");

        return student;
    }

    24. 自定义拦截器:Intercepter

step1. 自定是Intercepter

package com.maliang;

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

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Created by maliang on 2017/8/8.
 */
public class MyHandlerIntercepter implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandler...");
        return true;
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandler...");
    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompetion...");
    }
}

step2. 配置dispatcher-servlet.xml

<mvc:interceptors>
    <bean id="myHandlerIntercepter" class="com.maliang.MyHandlerIntercepter"/>
</mvc:interceptors>

如果配置多个拦截器,执行顺序?

first PreHandler -> second PreHandler -> second PostHandler -> first PostHandler -> second afterCompetion -> first afterCompetion.

    25. SpringMVC异常处理ExceptionHandler

    在程序中需要捕捉异常,防止程序异常退出。可以通过try..catch, 也可以通过SpringMVC提供的ExceptionHander处理。

@RequestMapping("/testExceptionHandler/{id}")
public String testExceptionHandler(@PathVariable("id") Integer id) {
    System.out.println(10/id);
    return SUCCESS;
}

当id传入为0时,该Controller接口会抛出ArithmeticException异常,用SpringMVC可以有两种方式处理该异常。

局部处理:处理该Controller的异常:

@ExceptionHandler(ArithmeticException.class)
public String exceptionHandler() {
    return ERROR;
}

全局异常:新建异常处理类,并用@ControllerAdvice修饰,该异常会处理全局ArithmeticException异常。

package com.maliang;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

/**
 * Created by maliang on 2017/8/10.
 */
@ControllerAdvice
public class MyHandlerForException {

    private static final String ERROR = "error";

    @ExceptionHandler({ArithmeticException.class})
    public String exceptionForHandler() {
        System.out.println("ArithmeticException...");
        return ERROR;
    }
}

    @ResponseStatus: 使用该注解修饰自己创建的类,在异常界面显示自定义异常信息。

package com.maliang;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

/**
 * Created by maliang on 2017/8/10.
 */
@ResponseStatus(value = HttpStatus.FORBIDDEN, reason = "User not exist.")
public class UserNotMatchException extends RuntimeException {

}
package com.maliang;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * Created by maliang on 2017/8/10.
 */
@Controller
public class TestResponseStatus {

    private static final String SUCCESS = "success";

    @RequestMapping("/testResponseStatus/{id}")
    public String testResponseStatus(@PathVariable("id") Integer id) {
        if (id == 0) {
            throw new UserNotMatchException();
        }
        return SUCCESS;
    }
}

SimpleMappingExceptionResolver: 当异常发生时,可以将它映射到指定的界面。

@RequestMapping("/testSimpleMappingExceptionResolver")
public String testSimpleMappingExceptionResolver() {
    int arrays[] = new int[10];
    System.out.println(arrays[10]);
    return SUCCESS;
}

当调用该Controller接口发生异常时,会根据如下的bean配置,完成exception的处理:

<bean id="simpleMappingExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="exceptionAttribute" value="ex"/>
    <property name="exceptionMappings">
        <props>
            <prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>
        </props>
    </property>
</bean>

    26. SpringMVC运行流程

Spring工作流程描述

      1. 用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获;

      2. DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;

      3. DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(...)方法)

       4.  提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:

      HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息

      数据转换:对请求消息进行数据转换。如String转换成Integer、Double等

      数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等

      数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中

      5.  Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象;

      6.  根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet ;

      7. ViewResolver 结合Model和View,来渲染视图

      8. 将渲染结果返回给客户端。

Spring工作流程描述

    为什么Spring只使用一个Servlet(DispatcherServlet)来处理所有请求?

     详细见J2EE设计模式-前端控制模式

    Spring为什么要结合使用HandlerMapping以及HandlerAdapter来处理Handler?

    符合面向对象中的单一职责原则,代码架构清晰,便于维护,最重要的是代码可复用性高。如HandlerAdapter可能会被用于处理多种Handler。

© 著作权归作者所有

共有 人打赏支持
向码而生
粉丝 7
博文 13
码字总数 10088
作品 0
西城
个人站长
私信 提问
吐血整理 20 道 Spring Boot 面试题,我经常拿来面试别人!

面试了一些人,简历上都说自己熟悉 Spring Boot, 或者说正在学习 Spring Boot,一问他们时,都只停留在简单的使用阶段,很多东西都不清楚,也让我对面试者大失所望。 下面,我给大家总结下有...

Java技术栈
10/15
0
0
spring cloud 入门系列:总结

从我第一次接触Spring Cloud到现在已经有3个多月了,当时是在博客园里面注册了账号,并且看到很多文章都在谈论微服务,因此我就去了解了下,最终决定开始学习Spring Cloud。我在一款阅读App...

JAVA开发老菜鸟
07/20
0
0
那些年,我们一起追的Spring

学无止境,但仍需及时总结。 自去年开始写作以来,写了一些关于Spring的文章,今天将它们汇总起来,一方面方便大家阅读,另一方面,也是一次小的复盘总结。 IOC 首先是Spring的IOC,也就是控...

SexyCode
08/14
0
0
Java基础教程:tutorialspoint-spring mvc

教程: 来自turorialspoint的Spring MVC 4.1.6教程(英文),官网:https://www.tutorialspoint.com/springmvc/index.htm 离线版本:(链接: https://pan.baidu.com/s/1hsvL7wS 密码: vg7x)......

easonjim
2017/08/28
0
0
spring+ibatis整合之jar包引入配置篇

1.spring-2.5.6.SEC03,再加上spring-webmvc- 3.2.3,马上动手springmvc的工作, 2.使用了spring的注解方式,发生了在spring-2.5.6.SEC03的spring-core包缺少对springmvc的@Controller等一些的...

无起
2012/12/28
0
1

没有更多内容

加载失败,请刷新页面

加载更多

Apache日志不记录访问静态文件,访问日志切割,静态元素过期时间设置

Apache配置不记录访问静态文件的日志 网站大多元素为静态文件,如图片、css、js等,这些元素可以不用记录 vhost原始配置 <VirtualHost *:80> ServerAdmin test@163.com DocumentRoo...

野雪球
今天
1
0
聊聊storm的ICommitterTridentSpout

序 本文主要研究一下storm的ICommitterTridentSpout ICommitterTridentSpout storm-core-1.2.2-sources.jar!/org/apache/storm/trident/spout/ICommitterTridentSpout.java public interface......

go4it
今天
2
0
Ubuntu常用操作

查看端口号 netstat -anp |grep 端口号 查看已使用端口情况 netstat -nultp(此处不用加端口号) netstat -anp |grep 82查看82端口的使用情况 查找被占用的端口: netstat -tln netstat -tl...

hc321
昨天
1
0
网站cdn的静态资源突然访问变的缓慢,问题排查流程

1.首先我查看了一下是否自己的网络问题,通过对比其他资源的访问速度和下载速度,确认不是 2.通过ping 和 tracert 判断cdn域名能否正常访问,(最后回想感觉这一步可以省略,因为每次最终能访...

小海bug
昨天
3
0
Mybatis 学习笔记四 MyBatis-Plus插件

Mybatis 学习笔记四 MyBatis-Plus插件 maven依赖 <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus</artifactId> <ve......

晨猫
昨天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部