转载

Spring MVC配置介绍

一、Spring MVC 纵览

Spring MVC就是Spring框架对MVC设计模式的实现,通过Spring MVC ,我们可以快速的构建灵活、松耦合的web服务。再具体介绍Spring MVC 之前,我们先看一下它的请求处理过程:

这里写图片描述

1.1 springMVC 的请求过程

1. 请求会首先发送到DispatchServlet,这是spring的前置Servlet,它会接收请求并转发给spring的MVC controller,也就是业务controller
2. DispatchServlet通过HandlerMapping确定将请求转发给哪个controller,HandlerMapping主要通过请求中的URL确定映射关系的
3. DispatchServlet将请求转发给确定的controller之后,controller负责处理这个请求,一般会通过调用service层进行业务逻辑处理
4. 当controller处理完请求后,它会把业务处理结果封装成model,为了使处理结果的model在页面上更好的展示,controller还会指定展示model对应的view(比如一个JSP页面),当controller确定了model和view之后,会把它们以请求的形式再转发给DispatchServlet
5. DispatchServlet通过查询ViewResolver找到view对应的页面
6. DispatchServlet最终把model交给页面进行渲染
7. 页面对model进行渲染,将结果展示到客户端,整个请求结束

1.2 配置Spring MVC

其实Spring MVC的核心就是DispatchServlet,配置Spring MVC的过程就是配置DispatchServlet的过程。

1.2.1 配置DispatchServlet

Spring的配置有两种方式,一种通过web.xml配置,另一种就是通过Java配置,在这里我们主要讲如何在web.xml中配置:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
     xmlns="http://java.sun.com/xml/ns/javaee"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
<listener>
    <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class>
</listener>
<servlet>
    <servlet-name>spring</servlet-name>
    <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>spring</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

主要配置项:

  1. <context-param><listener>:配置Spring的RootContext,对应配置文件为root-context.xml
  2. <servlet><servlet-mapping>:配置Spring的WebContext,对应配置文件默认为为WEB-INF/{servlet-name}-servlet.xml

可能有些人还不太清楚为什么要这样配置,在具体讲解之前,先介绍Spring MVC中的两个context(上下文)。

理解两个context

我们知道,Spring有一个核心容器也就是ApplicationContext,所有的Spring组件都由这个ApplicationContext进行管理。但是在Web项目中,Spring会维护两个context:

这里写图片描述

1. WebContext

第一个Context就是由DispatchServlet创建的WebContext,负责装载web组件相关的bean,比如controllers、view resolvers、handler mapping等,对应的配置文件是WEB-INF/{servlet-name}-servlet.xml,在上面例子中我们配置的Servlet的名字是spring,所以它默认加载spring-servlet.xml配置文件作为上下文。

但是我们也可以自己指定一个WebContext配置文件位置,比如指定/WEB-INF/spring/appServlet/servlet-context.xml路径下的配置文件:

<servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring/appServlet/servlet-context.xml
        </param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

2. RootContext

RootContext是由ContextListener加载的,它主要装载除web组件之外的应用程序组件,比如jdbc、mybatis等组件。<context-param>标签指定了RootContext配置文件的位置,并由<listener>标签指定的Listener类进行装载。

1.2.2 启用Spring MVC

上面的配置其实已经基本ok了,但是一个完整的Spring MVC应用还需要controller、service、view等web组件,所以我们还要在配置中启用注解及自动包扫描等功能,方便web组件的自动发现,这些应该在上面介绍的WebContext对应的配置文件中进行配置,也就是在spring-servlet.xml中配置:

<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-4.0.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <mvc:annotation-driven/>
    <context:component-scan base-package="com.springmvc.demo" />

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

</beans>

主要配置项:

  1. <mvc:annotation-driven/>标签作用是开启注解
  2. <context:component-scan/>标签目的是启用自动包扫描,这样spring框架就会自动扫描被注解的类,纳入到WebContext中。

通过上面的配置,Spring MVC的主要配置就已经完成了,我们现在就可以编写Spring MVC的组件了,我们先从controller开始。

二、写一个controller

 2.1 写一个简单的controller

@Controller
public class HomeController {
@RequestMapping(value="/", method= RequestMethod.GET)
    public String home() {
        return "home";
    }
}

声明controller组件:写一个controller很简单,@controller注解声明当前类是一个controller类,上面配置中我们开启了ComponentScan,被@controller注解的类会被自动装载到spring application context中。当然我们使用@component组件效果也是一样的,只不过@controller更能体现controller角色。

定义请求路径: HomeController类里面只有一个home()方法,并且还携带一个@RequestMapping注解,注解中的value属性定义了这个方法的访问路径是“/”,method属性定义了这个方法只处理get请求。

定义渲染view:我们可以看到,home()方法很简单,仅返回了一个“home”字符串。默认情况下,方法返回的字符串默认会被解析成view的名称,DispatcherServlet 会让ViewResolver解析这个view名称对应的真是view页面。我们上面已经配置了一个InternalResourceViewResolver,返回的home会被解析/WEB-INF/views/home.jsp。

写完controller之后我们可以通过测试类测试一下,写一个测试controller:

public class HomeControllerTest {
    @Test
    public void testHomePage() throws Exception {
        HomeController controller = new HomeController();
        Assert.assertEquals("home", controller.home());

    }
    @Test
    public void testHomePageMVC() throws Exception {
        HomeController controller = new HomeController();
        MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
        mockMvc.perform(MockMvcRequestBuilders.get("/")).andExpect(MockMvcResultMatchers.view().name("home"));
    }

}

2.2 定义一个类层次的路径映射

其实@RequestMapping注解既可以注解方法又可以注解类,当注解类时,访问类中所有的方法就必须加上类路径。另外@RequestMapping中的值可以是一个路径数组,当传入一个数组时,我们可以通过数组中的任何一个路径访问到这个类.

@Controller
@RequestMapping({"/", "/homepage"})
public class HomeController {
    ...
}

2.3 返回Model数据给view层

我们知道通常情况下,controller处理完业务后会返回结果数据给view层。为此,SpringMVC提供了Model类来封装结果数据,封装的过程是自动的,我们只需要在方法中加入Model参数即可:

public String getUsers(Model model) {
    model.addAttribute( userService.findSpittles( Long.MAX_VALUE, 20));
    return "userView";
}

Model其实就是一个Map,view层会自动解析model中的数据,然后渲染结果给client端。上面的model.addAttribute方法没有指定key,key值会被默认设置为对象的类型名,比如上例子中对象类型是List<User>,则key值默认为:userList。方法最后返回的String值userView会直接作为view的名称。

当然我们也可以明确指定返回数据模型的key值:

public String getUsers(Model model) {
    model.addAttribute("userList", userService.findUsers( Long.MAX_VALUE, 20));
    return "userView";
}

如果我们不想使用Spring的Model类,我们也可以用java.util.Map类替换Model,这两个效果是完全一样的:

public String getUsers(Map map) {
    map.addAttribute("userList", userService.findUsers( Long.MAX_VALUE, 20));
    return "userView";
}

除了上面两种方式之外,我们还可以这么写:

@RequestMapping(method=RequestMethod.GET)
public List<User> getUsers() {
    return userService.findUsers(Long.MAX_VALUE, 20));
}

这种方式比较特殊,我们既没有设置返回的Model,又没有指定渲染Model的view,仅仅返回处理的结果对象。遇到这种情况时,Spring MVC会把返回的对象会自动放入Model中,其key就是对象的类型名,即userList。而对应的view名称则默认与请求路径名一致,例如我们的请求路径是这样:

@Controller
@RequestMapping("/users")
public class UserController {
    ... ...
}

那么对应渲染结果的view就是users

三、 处理请求数据

SpringMVC提供了多种数据传输方式:

  • QueryParameter:查询参数
  • Form Parameters:表单参数
  • Path variables:路径变量

下面我们逐一说明。

3.1 获取查询参数

首先什么是查询参数呢?比如我们有一个这样的请求http://localhost:8080/user/queryUsers?pageNo=1&count=5,在这个请求中,参数值都是通过URL中?后面的参数传递过来的,这种方式就是查询参数传值。如果要解析查询参数中的值,我们需要用到@RequestParam注解,它会将请求参数映射到方法参数:

@RequestMapping(value = "/getUsersByPage", method = RequestMethod.GET)
public String getUsersByPage(@RequestParam(value = "pageNo",defaultValue = "1") long pageNo,@RequestParam(value = "count",defaultValue = "5") long count ,Model model) {
    model.addAttribute(userService.getAllUsers());
    return "userListPageView";
}

需要注意的是,我们在配置请求参数时可以指定参数的默认值,当client端传过来的参数值不存在或者为空时,就会采用这个默认值,还有一点,因为查询参数都是String类型,所以这里的默认值也都是String类型。

除了通过查询参数传递参数值之外,还有一种流行的方式就是通过请求路径传递参数值,特别是在讨论构建基于资源的服务时会经常用到这种方式。(注:基于资源的服务可以简单看做所有请求都是针对资源,所有的返回结果也是资源)

3.2 通过请求路经获取参数

比如我们有一个根据用户id查询用户信息的需求,通过上面介绍的方式我们可以这么做:

@RequestMapping(value = "/queryUser", method = RequestMethod.GET)
public String queryUser(@RequestParam(value = "employeeId") long employeeId,Model model){
    model.addAttribute(manager.queryEmployeeVO(employeeId));
    return "employee";
}

那么我们客户的的请求路径应该是这样:/employee/queryEmployee ?employeeId=12345,尽管也可以满足需求,但这样不太符合基于资源的理念。理想的情况是,资源应该是由请求路径决定的,而不是由请求参数决定。或者说,请求参数不应该被用来描述一个资源。/employee/12345 这种请求方式显然比/employee/queryEmployee ?employeeId=12345更能合适,前者定义了要查询的资源,而后者更强调了通过参数进行操作。

为了达到构建基于资源的controller这个目标,Spring MVC允许请求路径中包含一个占位符,占位符名称需要用一对{}括起来。在客户的请求资源时,请求路径的其它部分还是用来匹配资源路径,而占位符部分则直接用来传输参数值。下面就是通过占位符的方式实现路径中传递参数值:

@RequestMapping(value ="/{userId}", method = RequestMethod.GET)
public String queryUser(@PathVariable(value = "userId") long userId, Model model){
    model.addAttribute(userService.queryUser(employeeId));
    return "userView";
}

上面例子可以看到,方法参数中有一个@PathVariable的注解,这个注解的意思就是不管请求路径中占位符处的值是什么,它都会被传递到@PathVariable注解的变量中。比如按照上面的配置,如果我们的请求是/employee/12345,则123456便会传入变成userId的值。需要注意的是,如果方法参数值和占位符名称一样,我们也可以省略@PathVariable中的属性值:

@RequestMapping(value ="/{userId}", method = RequestMethod.GET)
public String queryUser(@PathVariable long userId, Model model){
    model.addAttribute(userService.queryUser(employeeId));
    return "userView";
}

在请求的数据量很小的时候使用查询参数和路径参数还是可行的,但是有时候我们请求的数据量会很大,再用上面两种方式就显得有点不太合适,这时就需要考虑用第三种方式:表单参数。

四、处理表单数据

一个Web应用不单单只是给用户展示数据,很多时候它还需要与用户交互获取用户数据,表单就是获取用户数据最常见的方式。

比如我们有个注册表单:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
    <head>
        <title>Spittr</title>
        <link rel="stylesheet" type="text/css"  >
    </head>
    <body>
        <h1>Register</h1>
        <form method="POST">
            First Name: <input type="text" name="name" /><br/>
            Last Name: <input type="text" name="accountNo" /><br/>
            Password: <input type="password" name="password" /><br/>
            <input type="submit" value="Register" />
        </form>
    </body>
</html>

4.1 写一个接收表单数据的controller

表单都是以post方式提交的,所以我们的controller接受的应该是个post请求:

@Controller
@RequestMapping("/user")
public class RegisterController {
    @Autowired
    private RegisterService registerService;
    @RequestMapping(value="/register", method=RequestMethod.POST)
    public String processRegistration(User user) {
        registerService.register(user);
        return "redirect:/user/" + user.getName();
    }
}

在上面例子中,出了接收的请求是post之外,注册方法还有一个含有User对象的参数,这个User对象中含有name、password、accountNo属性,SpringMVC会根据名称从请求中解析相同名称参数并赋值到对象属性中。

当注册方法保存用户信息之后,会返回一个字符串redirect:/user/,这个字符串与前面讲到的都不同,它返回的并不是一个view的名称,而是一个redirect重定向请求。因为返回的字符串中含有一个redirect:重定向关键字,当InternalResourceViewResolver类遇到这个关键字时,它将会拦截这个字符串并把它解析成一个重定向请求而不是view名称。

InternalResourceViewResolver除了能够识别redirect:关键字之外,它还能识别forward:关键字,并把包含forward:关键字的字符串解析成forward请求。

4.2 验证表单

在Spring3.0以后,SpringMVC便支持Java Validation API了,Java Validation API提供了一些注解来约束对象属性值,这些注解有:

注解 解释
@AssertFalse The annotated element must be a Boolean type and be false.
@AssertTrue The annotated element must be a Boolean type and be true.
@DecimalMax The annotated element must be a number whose value is less than or equal toa given BigDecimalString value.
@DecimalMin The annotated element must be a number whose value is greater than orequal to a given BigDecimalString value.
@Digits The annotated element must be a number whose value has a specified number of digits.
@Future The value of the annotated element must be a date in the future.
@Max The annotated element must be a number whose value is less than or equal to a given value.
@Min The annotated element must be a number whose value is greater than or equal to a given value.
@NotNull The value of the annotated element must not be null.
@Null The value of the annotated element must be null.
@Past The value of the annotated element must be a date in the past.
@Pattern The value of the annotated element must match a given regular expression.
@Size The value of the annotated element must be either a String, a collection, or an array whose length fits within the given range.

我们可以利用这些注解给User对象添加一些验证,比如非空和字符串长度验证:

public class User {
    @NotNull
    @Size(min=3,max=20)
    private String name;
    @NotNull
    @Size(min=6,max=20)
    private String password;
    @NotNull
    @Size(min=3,max=20)
    private String accountNo;

}

我们还需要通过@Valid标签在方法中对User启用参数验证:

@RequestMapping(value="/register", method=RequestMethod.POST)
public String processRegistration(@Valid User user, Errors errors) {
    if(errors.hasErrors()){
        return "register";
    }
    registerService.register(user);
    return "redirect:/user/" + user.getName();
}

在上面例子中,我们在方法中的User参数前面添加了@valid注解,这个注解会告诉Spring框架需要启用对这个对象的验证。如果发现任何验证错误,错误信息都将会被封装到Errors对象中,我们可以通过errors.hasErrors()方法判断是否验证通过。

Spring MVC其它相关文章:

正文到此结束
本文目录