Spring MVC是Spring框架的重要组成部分,由名字也可以看出,这是一个MVC框架,本文通过简单的例子介绍Spring MVC的搭建和配置,并对其功能作简单介绍。
Spring MVC项目的创建可以参考:
使用IntelliJ IDEA开发SpringMVC网站(一)开发环境
在这里不再赘述。
Spring MVC 框架配置
Spring MVC框架配置主要有xml配置和java配置两种方式,其中xml配置网上的资源已经比较丰富,可以参考这篇:使用IntelliJ IDEA开发SpringMVC网站(二)框架配置
这里重点介绍下采用java的配置方式。
首先在src/main/java/文件夹下创建“config” package,然后创建如下class
package config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletRegistration;
/**
* Created by chyzh on 2016/3/19.
*/
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] {RootConfig.class};
}
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] {WebConfig.class};
}
protected String[] getServletMappings() {
return new String[] {"/"};
}
}
我们创建了一个“WebAppInitializer”class 继承了“AbstractAnnotationConfigDispatcherServletInitializer ”类,实现了三个方法。这个类的作用类似于xml配置中的“web.xml”文件,但并不相同。
这里有一个WebConfig.class类,这个class的作用很像xml配置中“xxx-servlet.xml”配置文件的作用。有了“WebAppInitializer”类,我们就无需手动创建“DispatcherServlet” 了,Spring会自动给我们创建。
再来看下class “WebConfig”,代码如下:
@Configuration
@EnableWebMvc
@ComponentScan("controller")
public class WebConfig extends WebMvcConfigurerAdapter {
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver resolver =
new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/pages/");
resolver.setSuffix(".jsp");
return resolver;
}
}
可以看到,WebConfig class继承了“WebMvcConfigurerAdapter ”类。class上方 @Configuration
注解表示这是一个Spring MVC配置文件;@EnableWebMvc
注解表示开启WebMvc;@ComponentScan
注解定义了扫描Controller的基本包。
class内部,@Bean
注解定义了一个自动装配的bean,这是一个视图解析bean。其中定义了逻辑视图名称映射到jsp文件的逻辑。
接下来,在/src/main/java/下定义“controller”package,并在其中创建“WelcomeController” 类,其实现如下:
@Controller
@RequestMapping("/")
public class WelcomeController {
@RequestMapping(method = RequestMethod.GET)
public String hello() {
return "welcome";
}
}
其中,@Controller
注解表示这是一个请求处理类,里面的每个方法都是一个请求处理方法。此外,我们看到class 头上和内部的hello方法上都有一个@RequestMapping
注解,它的作用是不一样的,在class头上的表示父路径,其中的每个方法头上的表示父路径下的一个子路径,如下面的例子:
@Controller
@RequestMapping("/aaa")
public class DemoController {
@RequestMapping("/bbb")
public String hello() {
//...
}
}
web端要访问hello方法其路径应该是 http://www.xxx.com/context/aaa/bbb
。
在接着看上面的WelcomeController 类,其hello方法就是一个不同的方法,不接受任何参数,返回一个“welcome”字符串。这个返回的字符串就是逻辑视图名。SprinMVC 会把它转换成实际的jsp名称。由前面“InternalResourceViewResolver”类的定义,加上前缀和后缀,实际的jsp文件应该存储在/src/webapp/WEB-INF/pages/路径下,我们在这个路径下创建一个“welcome.jsp”的文件,内容很简单:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Welcome</title>
</head>
<body>
Welcome!!!
</body>
</html>
完成这些后,启动服务器,打开localhost:8080/
就可以看到“Welcome!!!“了。
Spring MVC 的依赖注入
Spring的依赖注入主要使用 @Component
和 @AutoWired
注解,但在Spring MVC中主要使用 @Service
和@Autowried
注解。事实上,你也可以使用 @Component
注解代替 @Service
注解。
下面的例子模拟在网站上的注册过程,重点关注下@Autowired
和 @Service
注解的使用。
创建class “UserController” ,代码如下:
package controller;
import chy.zhao.data.User;
import chy.zhao.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* Created by chyzh on 2016/3/22.
*/
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping(value = "/register", method = RequestMethod.GET)
public String register() {
return "register";
}
@RequestMapping(value = "/saveUser", method = {RequestMethod.GET, RequestMethod.POST})
public String saveUser(User user, Model model) {
userService.saveUser(user);//保存用户注册信息
model.addAttribute("user", user);
return "userInfo";
}
}
这是一个控制器类,有两个方法,当访问localhost:8080/user/register/
路径时显示注册页面,用户填入用户名和密码后,点击submit,后端获取用户填入的信息,进行相应的处理(如存入数据库)然后返回给用户注册成功信息,并显示用户注册的用户名和邮箱。
这里需要两个jsp页面,分别是 “register.jsp” 和 “userInfo.jsp”,两个页面如下:
register.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Register</title>
</head>
<body>
<form action="/user/saveUser" method="post">
<fieldset>
<legend>Register</legend>
<p>
<label for="username">Username: </label>
<input type="text" id="username" name = "username" tabindex="1">
</p>
<p>
<label for="email">Email: </label>
<input type="text" id="email" name="email" tabindex="2">
</p>
<p>
<label for="password">Password</label>
<input id="password" type="password" name="password" tabindex="3">
</p>
<p>
<input id="reset" type="reset" tabindex="4">
<input id="submit" type="submit" tabindex="5">
</p>
</fieldset>
</form>
</body>
</html>
userInfo.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>User Info</title>
</head>
<body>
<h4>Register Success!</h4>
<p>
<h5>Details</h5>
Username: ${user.username}<br/>
Email: ${user.email}
</p>
</body>
</html>
在Controller类中使用@AutoWired
注解自动注入了一个UserService类型的bean 这里bean主要用鱼处理从前端提交的请求,诸如校验数据,存入数据库等功能都有它实现,在saveUser方法中,我们在获取了User对象后调用了这个bean的saveUser方法保存用户信息。
看一下UserService 类的实现:
“`Java
package service;
import chy.zhao.data.User;
import org.springframework.stereotype.Service;
/**
* Created by chyzh on 2016/3/22.
*/
@Service
public class UserService {
public void saveUser(User user){
//…
}
}
``
@Service` 注解,这样他就能通过Spring的依赖注入机制注入到UserController 类中了。
可以看到 “UserService”类使用了
其中saveUser方法的具体实现在这里省略。
启动tomcat,打开浏览器,在地址栏输入localhost:8080/user/register
可以看到注册表单,填入信息点击submit就可以看到注册成功的页面了。
网页重定向和Flash属性
转发和重定向是有区别的,最重要的区别是转发不经过客户端而重定向经过客户端,所以转发比重定向要快。但有些时候不适合使用转发,而适合使用重定向,比如在上面的例子中,我们使用了转发,但在得到“Register Success!!!”的信息后,如果用户不小心刷新了页面,此时已经提交过的表单信息就会再次提交,这有时会带来一些问题,如注册过的用户再次进行注册,往往会出多。此时最好的办法是使用重定向使客户端重定向到另一个页面,此时再刷新不会重新提交表单。
但重定向有一个不便之处,就是不容易向视图传递参数,好在Spring MVC提供了FlashAttributes,通过它可以向视图传递参数。
我们修改UserController 类,使提交注册表单后显示注册成功页面改为重定向方式。修改后的代码为:
package controller;
import chy.zhao.data.User;
import chy.zhao.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
/**
* Created by chyzh on 2016/3/22.
*/
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping(value = "/register", method = RequestMethod.GET)
public String register() {
return "register";
}
@RequestMapping(value = "/saveUser", method = {RequestMethod.GET, RequestMethod.POST})
public String saveUser(User user, RedirectAttributes attributes) {
userService.saveUser(user);//保存用户注册信息
attributes.addFlashAttribute("user", user);
return "redirect:/user/userInfo";
}
@RequestMapping(value = "/userInfo", method = RequestMethod.GET)
public String userInfo() {
return "userInfo";
}
}
可以看到,我们在saveUser方法中去掉了Model参数,添加了FlashAttributes参数,通过她,添加一个键为“user”的User对象到视图,并将return方法改为了重定向方式,重定向到了“userInfo”路径。同时添加了“userInfo”方法,这个方法返回”userInfo” 字符串也就是“userInfo.jsp”页面。
此时,提交表单后再刷新页面就不会重新提交表单了。
请求参数和路径变量
请求参数和路径变量都可以用于发送值给服务器,二者都是URL的一部分,请求参数采用“key=value”的形式,中间使用“&”符号分割,在Spring MVC中我们可以通过@RequestParam
注解标注一个参数来获取请求参数的值,比如下面的URL:
localhost:8080/user/register?age=12
我们可以这样获取“age“变量:
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping(value="/register", method=RequestMethod.GET)
public String register(@RequestParam int age){
System.out.println("age : " + age);
return "register";
}
}
路径变量的形式为:localhost:8080/context/value
,它没有key部分,只有一个值。获取路径变量使用@PathVariable
:
@RequestMapping("/")
public String hello(@PathVariable int age) {
return "hello";
}
@ModelAttribute 注解
Spring MVC在每次调用请求处理方法时,都会自动创建一个Model 类型的对象,若需要使用这个对象,我们只需在方法的参数中加上一个Model类型的对象即可,就像我们在第一个版本的例子(使用转发的那个版本)中使用的那样,通过这个Model类型的对象,我们就可以把User类型的对象传递给了视图以显示在jsp页面中。
除此之外,也可以使用@ModelAttribute
注解来访问Model实例,带有这个注解的参数会将其创建或输入的参数对象添加到Model对象中(在上面的例子中,我们是手动调用了Model的addAttribute方法)。我们在使用这个注解时,还可以为其指定一个键值,指定的方式是在注解后面的括号里添加一个表示建的字符串。
比如,我们可以把上面的例子(使用转发方式而不是重定向的那个例子)改为下面的形式:
@RequestMapping(value = "/saveUser", method = {RequestMethod.GET, RequestMethod.POST})
public String saveUser(@ModelAttribute("user") User user) {
userService.saveUser(user);//保存用户注册信息
return "userInfo";
}
这样,我们就使用”user“建添加了一个User类型的对象到”userInfo“视图中,在”userInfo“页面中就可以使用”user“键值来访问这个User对象的各个属性了。这个User中的各个参数值来自”register“输入的值。