Spring MVC,即 Spring Model-View-Controller,是一个实现了通用开发模式(模型-视图-控制器)的Web框架,它通过一个DispatcherServlet处理HTTP请求、完成资源映射、递交请求给控制器完成业务逻辑,相应数据则通过Model传递给视图解析器解析为相应的页面或数据流返回给客户端。
这里,我们可以通过Spring官方给出的图示大致了解其内部的工作机制:
DispatcherServlet作为前端控制器(Front Controller)过滤所有客户端发来的请求,检查请求路径并根据配置好的映射规则,将请求提交给指定的控制器(Controller),完成业务逻辑处理(比如,数据库访问),生成数据模型(model),传递给指定视图解析器(Spring内部已为我们定义好的一系列模板,拿来用即可)解析为相应的视图数据,最后返回客户端响应。
其实,好比我们最初学习Java Web开发一样,首先要在web.xml文件中配置DispatcherServlet:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<!-- Spring Dispatcher Servlet--> < servlet > < servlet-name >SpringDispatcher</ servlet-name > < servlet-class >org.springframework.web.servlet.DispatcherServlet</ servlet-class > < init-param > < param-name >contextConfigLocation</ param-name > < param-value >classpath:config/springDispatcher.xml</ param-value > </ init-param > < load-on-startup >1</ load-on-startup > </ servlet > < servlet-mapping > < servlet-name >SpringDispatcher</ servlet-name > < url-pattern >/</ url-pattern > </ servlet-mapping > |
这里配置了名为SpringDispatcher的Servlet,处理所有客户端的请求,contextConfigLocation参数指明了同时要加载的Spring MVC配置信息。
既然SpringDispatcher会过滤所有的请求,那如果请求的是静态资源的话,我们这样做就有点得不偿失了。不过不用担心,Spring MVC为我们提供了处理静态资源的解决办法:
在springDispatcher.xml文件中,引入spring mvc标记,并添加<mvc:resource>标签即可:
1
2
3
4
5
6
7
8
9
10
11
12
|
<? 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" xsi:schemaLocation=" http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd> <!-- Handle requests for static resources --> < mvc:resources mapping = "/resources/**" location = "/resources/" /> </ beans > |
如上所配置,<mvc:resources>会将所有直接返回以/resources/开始的静态资源请求,而不会通过SpringDispatcher进行处理。
DispatcherServlet配置好后,接下来就需要创建我们的控制器类了,Spring MVC里我们可以通过组件扫描来注册我们所写的控制器,自动织入所需的bean:
1
2
3
4
5
6
7
8
|
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd> <!-- Enable annotation-driven features --> < mvc:annotation-driven /> <!-- Enable component-scan features --> < context:component-scan base-package = "com.alan" /> |
控制器类如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
@Controller @RequestMapping ( "/user" ) public class UserController { private final UserService userService; @Autowired public UserController(UserService userService) { this .userService = userService; } @RequestMapping ( "/queryAll" ) public String queryAll( @RequestParam ( "type" ) int type, Model model) { if (type == 1 ) { List<User> users = userService.findAll(); model.addAttribute( "users" , users); } return "UserList" ; } } |
通过注解技术,我们可以很方便的将我们的业务类注册给控制器,在初始化时由Spring容器帮我们完成依赖注入。其中@RequestMapping注解则告诉Spring所有以“/user”开始的请求将由UserController来处理,而"/user/queryAll"则交由queryAll方法处理。@RequestParam则会接收URL请求参数,这里为type,并且自动转化为对应的参数类型。Model即为数据模型,由Spring提供,我们可以将处理后的结果数据绑定到Model上,返回Model给指定视图解析器。queryAll方法最后的return "UserList"意思是告诉视图解析器返回哪一个页面。这里我们需要再增加视图解析器的配置到springDsipatcher.xml中:
1
2
3
4
5
6
|
<!-- Configure view resolver --> < bean class = "org.springframework.web.servlet.view.InternalResourceViewResolver" > < property name = "viewClass" value = "org.springframework.web.servlet.view.JstlView" /> < property name = "prefix" value = "/WEB-INF/views/" /> < property name = "suffix" value = ".jsp" /> </ bean > |
以上XML配置表明,我们使用JstlView视图(支持JSTL),并且返回“/WEB-INF/views/”下面所有以“.jsp”为后缀的文件。本例中,即为“ /WEB-INF/views/UserList.jsp ”。
是不是很easy呢?不过,可能有的同学要问了,如果我不想返回页面,而是直接返回字符串或者IO流怎么办。好吧,Spring框架当然也想到了。我们可以在方法上增加@ResponseBody标记以表明直接返回响应内容,而不是转交视图解析器处理,就像下面这样:
1
2
3
4
5
6
7
8
9
10
|
@RequestMapping ( "/queryAll2" ) @ResponseBody public String queryAll2( @RequestParam ( "type" ) int type) { JSONObject jsonObj = new JSONObject(); if (type == 2 ) { List<User> users = userService.findAll(); jsonObj.put( "users" , users); } return jsonObj.toString(); } |
我们即可很轻松的返回字符串数据,比如返回JSON字符串。
InternalResourceViewResolver通常使用转发的方式返回页面数据。如果我们需要重定向到某个页面,则可以在方法返回的时候增加“redirect:”标记即可:
1
|
return "redirect:/UserList" ; |
当然,如果需要转发至某个Controller的方法也很方便:
1
|
return "forward:/user/queryAll2?type=2" |
此外,经常使用Struts的同学也比较喜欢表单数据绑定到Model的功能以减轻自己获取所有请求参数的繁琐工作,Spring亦有提供实现。即可通过@ModelAttribute标记获取表单参数(这需要Spring MVC所提供的form tag库的支持):
1
2
3
4
5
6
7
8
|
@Controller public class HelloWorldController { @RequestMapping (value = "/helloWorld" , method=RequestMethod.POST) public String helloWorld( @ModelAttribute User user) { return "helloWorld" ; } } |
好像还少了点什么对不对?是的,我如果要在Controller里获得ServletContext对象呢?别担心,只要我们implements ServletContext即可,这样我们就可以在Controller里获取Servlet上下文,因而什么request、response都不在话下了,这里给出一个上传JPG图片的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
@Controller @RequestMapping ( "/upload" ) public class FileUploadController implements ServletContextAware { private ServletContext servletContext; public void setServletContext(ServletContext servletContext) { this .servletContext = servletContext; } @RequestMapping ( "/jpg" ) public String uploadJPG( @RequestParam ( "image" ) MultipartFile image, Model model) { try { if (!image.isEmpty()) { validateImage(image, "image/jpeg" ); saveImage(image); model.addAttribute( "img" , "resources/upload" + File.separator + image.getName()); } } catch (UploadImageException e) { model.addAttribute( "msg" , e.getMessage()); return "uploadError" ; } return "images" ; } private void saveImage(MultipartFile image) throws UploadImageException { String name = image.getName(); try { File file = new File(servletContext.getRealPath( "/resources/upload" ) + File.separator + name); FileUtils.writeByteArrayToFile(file, image.getBytes()); } catch (IOException e) { throw new UploadImageException( "保存图片出错!" ); } } private void validateImage(MultipartFile image, String type) throws UploadImageException { if (!image.getContentType().equals(type)) { throw new UploadImageException( "只接受JPG格式的文件!" ); } } } |
还有,还有,记得我们经常会用到Session来保存一些共享数据,Spring MVC里可以在Controller上加上@SessionAttributes标记来完成这个功能:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
@Controller @RequestMapping ( "/user" ) @SessionAttributes ( "user" ) public class UserController { private final UserService userService; @Autowired public UserController(UserService userService) { this .userService = userService; } /** ....... */ } |
转自:http://my.oschina.net/moson/blog/146808#