Spring MVC配置介绍
2016-06-06 18:56
507 查看
一、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>
主要配置项:
<context-param>和
<listener>:配置Spring的
RootContext,对应配置文件为
root-context.xml
<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>
主要配置项:
<mvc:annotation-driven/>标签作用是开启注解
<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. |
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其它相关文章:
Spring MVC 页面渲染( render view )
Spring MVC 中的异常处理 (handling exceptions)
Spring MVC 上传文件(upload files)
相关文章推荐
- Java并发编程之——CountDownLatch的使用
- JAVA学习笔记--多线程(三)synchronized实现线程同步
- 对于java中接口的理解
- Java进阶10 内存管理与垃圾回收
- Android Studio导入Eclipse项目的一系列问题解决
- Android之 Eclipse NDK编译——常见错误的解决方法记录
- JavaFx使用指南
- Java RandomAccessFile用法
- Java中用内存映射处理大文件
- JAVA随机:当前年月日时分秒+五位随机数
- java异常记录集
- Java中关于字符串转成毫秒数以及毫秒数转化成字符串的总结
- java.io.IOException: No FileSystem for scheme: hdfs
- Spring 攻略第002讲
- java实现ftp文件操作的方式有哪些?
- 捷易拍与springMVC系统结合
- 一台电脑安装两个不同版本JDK
- Spring官方文档学习(3.1)
- java mail实现Email的发送,完整代码
- spring参数类型异常输出(二), SpringMvc参数类型转换错误输出(二)