简短 & 快速介绍REST
REST表示 Representational State Transfer(表示性状态转换).
它是可以用来设计web services的框架,可以被不同的客户端调用。
核心思想是:使用简单的HTTP协议来实现调用,而不是CORBA, RPC 或者 SOAP等负责的机制。
在Rest 基础设计中,资源使用以下动词进行操作。
- 创建资源 : 使用 HTTP POST
- 获取资源 : 使用 HTTP GET
- 更新资源 : 使用 HTTP PUT
- 删除资源 : 使用 HTTP DELETE
也意味着,你作为Rest 服务开发者或者客户,应该遵循以上的标准。
尽管没有限制必须返回的类型,但是一般基于Web services的Rest返回JSON或者XML作为响应。
客户端可以指定(使用HTTP Accept header)他们想要的资源类型吗,服务器返回需要的资源。
指明资源的Content-Type。如果想详细的理解 restful可以参考这里:StackOverflow link
基于Rest的Controller(控制器)
我们的 REST API :
- GET
- GET
- POST
- PUT
- DELETE
- DELETE 方式请求/api/user/删除所有user
[java] view plain copy
1. package com.websystique.springmvc.controller;
2.
3. import java.util.List;
4.
5. import org.springframework.beans.factory.annotation.Autowired;
6. import org.springframework.http.HttpHeaders;
7. import org.springframework.http.HttpStatus;
8. import org.springframework.http.MediaType;
9. import org.springframework.http.ResponseEntity;
10. import org.springframework.web.bind.annotation.PathVariable;
11. import org.springframework.web.bind.annotation.RequestBody;
12. import org.springframework.web.bind.annotation.RequestMapping;
13. import org.springframework.web.bind.annotation.RequestMethod;
14. import org.springframework.web.bind.annotation.RestController;
15. import org.springframework.web.util.UriComponentsBuilder;
16.
17. import com.websystique.springmvc.model.User;
18. import com.websystique.springmvc.service.UserService;
19.
20. @RestController
21. public class HelloWorldRestController {
22.
23. @Autowired
24. //Service which will do all data retrieval/manipulation work
25.
26.
27. //-------------------Retrieve All Users--------------------------------------------------------
28.
29. @RequestMapping(value = "/user/", method = RequestMethod.GET)
30. public ResponseEntity<List<User>> listAllUsers() {
31. List<User> users = userService.findAllUsers();
32. if(users.isEmpty()){
33. return new ResponseEntity<List<User>>(HttpStatus.NO_CONTENT);//You many decide to return HttpStatus.NOT_FOUND
34. }
35. return new ResponseEntity<List<User>>(users, HttpStatus.OK);
36. }
37.
38.
39. //-------------------Retrieve Single User--------------------------------------------------------
40.
41. @RequestMapping(value = "/user/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
42. public ResponseEntity<User> getUser(@PathVariable("id") long id) {
43. "Fetching User with id " + id);
44. User user = userService.findById(id);
45. if (user == null) {
46. "User with id " + id + " not found");
47. return new ResponseEntity<User>(HttpStatus.NOT_FOUND);
48. }
49. return new ResponseEntity<User>(user, HttpStatus.OK);
50. }
51.
52.
53.
54. //-------------------Create a User--------------------------------------------------------
55.
56. @RequestMapping(value = "/user/", method = RequestMethod.POST)
57. public ResponseEntity<Void> createUser(@RequestBody User user, UriComponentsBuilder ucBuilder) {
58. "Creating User " + user.getName());
59.
60. if (userService.isUserExist(user)) {
61. "A User with name " + user.getName() + " already exist");
62. return new ResponseEntity<Void>(HttpStatus.CONFLICT);
63. }
64.
65. userService.saveUser(user);
66.
67. new HttpHeaders();
68. "/user/{id}").buildAndExpand(user.getId()).toUri());
69. return new ResponseEntity<Void>(headers, HttpStatus.CREATED);
70. }
71.
72.
73. //------------------- Update a User --------------------------------------------------------
74.
75. @RequestMapping(value = "/user/{id}", method = RequestMethod.PUT)
76. public ResponseEntity<User> updateUser(@PathVariable("id") long id, @RequestBody User user) {
77. "Updating User " + id);
78.
79. User currentUser = userService.findById(id);
80.
81. if (currentUser==null) {
82. "User with id " + id + " not found");
83. return new ResponseEntity<User>(HttpStatus.NOT_FOUND);
84. }
85.
86. currentUser.setName(user.getName());
87. currentUser.setAge(user.getAge());
88. currentUser.setSalary(user.getSalary());
89.
90. userService.updateUser(currentUser);
91. return new ResponseEntity<User>(currentUser, HttpStatus.OK);
92. }
93.
94. //------------------- Delete a User --------------------------------------------------------
95.
96. @RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE)
97. public ResponseEntity<User> deleteUser(@PathVariable("id") long id) {
98. "Fetching & Deleting User with id " + id);
99.
100. User user = userService.findById(id);
101. if (user == null) {
102. "Unable to delete. User with id " + id + " not found");
103. return new ResponseEntity<User>(HttpStatus.NOT_FOUND);
104. }
105.
106. userService.deleteUserById(id);
107. return new ResponseEntity<User>(HttpStatus.NO_CONTENT);
108. }
109.
110.
111. //------------------- Delete All Users --------------------------------------------------------
112.
113. @RequestMapping(value = "/user/", method = RequestMethod.DELETE)
114. public ResponseEntity<User> deleteAllUsers() {
115. "Deleting All Users");
116.
117. userService.deleteAllUsers();
118. return new ResponseEntity<User>(HttpStatus.NO_CONTENT);
119. }
120.
121. }
详解:
@RestController首先我们使用的是Spring 4的新注解 @RestController注解.
此注解避免了每个方法都要加上@ResponseBody注解。也就是说@RestController 自己戴上了 @ResponseBody注解,看以看作是
@Controller 和 @ResponseBody的结合体。
@RequestBody如果方法参数被 @RequestBody注解,Spring将绑定HTTP请求体到那个参数上。如果那样做,Spring将根据请求中的ACCEPT或者 Content-Type header(私下)使用 HTTP Message converters 来将http请求体转化为domain对象。
@ResponseBody如果方法加上了@ResponseBody注解,Spring返回值到响应体。如果这样做的话,Spring将根据请求中的 Content-Type header(私下)使用 HTTP Message converters 来将domain对象转换为响应体。
ResponseEntity是一个真实数据.它代表了整个 HTTP 响应(response). 它的好处是你可以控制任何对象放到它内部。
你可以指定状态码、头信息和响应体。它包含你想要构建HTTP Response 的信息。
@PathVariable此注解意味着一个方法参数应该绑定到一个url模板变量[在'{}'里的一个]中
一般来说你,要实现REST API in Spring 4 需要了解@RestController , @RequestBody, ResponseEntity 和 @PathVariable 这些注解 .另外, spring 也提供了一些支持类帮助你实现一些可定制化的东西。
MediaType : 带着 @RequestMapping 注解,通过特殊的控制器方法你可以额外指定,MediaType来生产或者消耗。
发布和测试此API
http://localhost:8080/Spring4MVCCRUDRestService.
想要测试此API,我将使用POSTMAN这个外部客户端,接下来我们也将写我们自己的客户端。
1. 获取所有用户
打开 POSTMAN工具,选择请求类型为GET,指明uri
注意:我们没有指明任何HTTP头。点击 发送,将接收到所有用户的列表
也要注意HTTP 200 响应。
你也许好奇为什么此响应通过JSON字符串发送的,在响应里的Content-Type 头说明了这个。
因为我们添加了JACKSON
[html] view plain copy
1. <dependency>
2. <groupId>com.fasterxml.jackson.core</groupId>
3. <artifactId>jackson-databind</artifactId>
4. <version>2.5.3</version>
5. </dependency
因为Spring在类路径发现了这个库,它调用了内置的
MappingJackson2HttpMessageConverter
转换器将响应(对象集合)转换为JSON格式。
Spring内置转换器的好处是,大部分情况下只要把库放到类路径,即可完成转换。当然了有时候我们也需要
采用我们的API。比如,如果我们像也提供XML格式的话,我们需要对User类加上JAXB注解。
2. 获取单个用户
GET方式 指定/user/1
现在试着发送一个带有错误识别码的GET请求,将收到一个HTTP 404
3.创建一个 User
选择POST方法,指明uri /user/ 指明POSTMAN Body选项卡,选择application/json类型
Content-Type 头信息
记住: Accept header包含client能给识别的类型。 Content-Type header表示数据的实际类型。
点击发送以后 将收到 HTTP 200 没有响应体(api里面没有在响应体发送任何东西)
你可以查询新创建的用户
这是实现REST的普通实现方式。但是也没人阻止你为POST或者PUT方式响应体里发送内容。但是这还是REST 的API?值得怀疑。
不管怎样,我们试着创建同一个用户时,你将获得HTTP冲突的响应。
4.更新用户
发送一个HTTP PUT 请求来更新用户。
注意:这次我们接收到了响应体。这是因为在控制器的方法实现里我们发送了数据。再次强调,有的人也许不在响应体里面发送更新的详情,只发送位置头(和创建用户一样)。
5.删除用户
6 删除所有用户
7.删除用户后验证
根据RestTemplate 写REST Client
Postman是测试Rest Api的超好用的工具,但是如果你想完整的消化REST,可以尝试自己写一个。
HttpClient(
Apache HttpComponents )。
但是用它来访问REST service则相对少见。
Spring的
RestTemplate随之出现。RestTemplate 提供了高级方法,来响应者6种主要的HTTP方法。
HTTP 方法和对应的 RestTemplate方法:
• HTTP GET : getForObject, getForEntity
• HTTP PUT : put(String url, Object request, String…urlVariables)
• HTTP DELETE : delete
• HTTP POST : postForLocation(String url, Object request, String… urlVariables), postForObject(String url, Object request, ClassresponseType, String… uriVariables)
• HTTP HEAD : headForHeaders(String url, String… urlVariables)
• HTTP OPTIONS : optionsForAllow(String url, String… urlVariables)
• HTTP PATCH and others : exchange execute
定义 Rest client , 定义REST services
[java] view plain copy
1. package com.websystique.springmvc;
2.
3. import java.net.URI;
4. import java.util.LinkedHashMap;
5. import java.util.List;
6.
7. import org.springframework.web.client.RestTemplate;
8.
9. import com.websystique.springmvc.model.User;
10.
11. public class SpringRestTestClient {
12.
13. public static final String REST_SERVICE_URI = "http://localhost:8080/Spring4MVCCRUDRestService";
14.
15. /* GET */
16. @SuppressWarnings("unchecked")
17. private static void listAllUsers(){
18. "Testing listAllUsers API-----------");
19.
20. new RestTemplate();
21. "/user/", List.class);
22.
23. if(usersMap!=null){
24. for(LinkedHashMap<String, Object> map : usersMap){
25. "User : id="+map.get("id")+", Name="+map.get("name")+", Age="+map.get("age")+", Salary="+map.get("salary"));;
26. }
27. else{
28. "No user exist----------");
29. }
30. }
31.
32. /* GET */
33. private static void getUser(){
34. "Testing getUser API----------");
35. new RestTemplate();
36. "/user/1", User.class);
37. System.out.println(user);
38. }
39.
40. /* POST */
41. private static void createUser() {
42. "Testing create User API----------");
43. new RestTemplate();
44. new User(0,"Sarah",51,134);
45. "/user/", user, User.class);
46. "Location : "+uri.toASCIIString());
47. }
48.
49. /* PUT */
50. private static void updateUser() {
51. "Testing update User API----------");
52. new RestTemplate();
53. new User(1,"Tomy",33, 70000);
54. "/user/1", user);
55. System.out.println(user);
56. }
57.
58. /* DELETE */
59. private static void deleteUser() {
60. "Testing delete User API----------");
61. new RestTemplate();
62. "/user/3");
63. }
64.
65.
66. /* DELETE */
67. private static void deleteAllUsers() {
68. "Testing all delete Users API----------");
69. new RestTemplate();
70. "/user/");
71. }
72.
73. public static void main(String args[]){
74. listAllUsers();
75. getUser();
76. createUser();
77. listAllUsers();
78. updateUser();
79. listAllUsers();
80. deleteUser();
81. listAllUsers();
82. deleteAllUsers();
83. listAllUsers();
84. }
85. }
重启服务器,运行上面的程序。
下面是输出:
[plain] view plain copy
1. Testing listAllUsers API-----------
2. User : id=1, Name=Sam, Age=30, Salary=70000.0
3. User : id=2, Name=Tom, Age=40, Salary=50000.0
4. User : id=3, Name=Jerome, Age=45, Salary=30000.0
5. User : id=4, Name=Silvia, Age=50, Salary=40000.0
6. Testing getUser API----------
7. User [id=1, name=Sam, age=30, salary=70000.0]
8. Testing create User API----------
9. Location : http://localhost:8080/Spring4MVCCRUDRestService/user/5
10. Testing listAllUsers API-----------
11. User : id=1, Name=Sam, Age=30, Salary=70000.0
12. User : id=2, Name=Tom, Age=40, Salary=50000.0
13. User : id=3, Name=Jerome, Age=45, Salary=30000.0
14. User : id=4, Name=Silvia, Age=50, Salary=40000.0
15. User : id=5, Name=Sarah, Age=51, Salary=134.0
16. Testing update User API----------
17. User [id=1, name=Tomy, age=33, salary=70000.0]
18. Testing listAllUsers API-----------
19. User : id=1, Name=Tomy, Age=33, Salary=70000.0
20. User : id=2, Name=Tom, Age=40, Salary=50000.0
21. User : id=3, Name=Jerome, Age=45, Salary=30000.0
22. User : id=4, Name=Silvia, Age=50, Salary=40000.0
23. User : id=5, Name=Sarah, Age=51, Salary=134.0
24. Testing delete User API----------
25. Testing listAllUsers API-----------
26. User : id=1, Name=Tomy, Age=33, Salary=70000.0
27. User : id=2, Name=Tom, Age=40, Salary=50000.0
28. User : id=4, Name=Silvia, Age=50, Salary=40000.0
29. User : id=5, Name=Sarah, Age=51, Salary=134.0
30. Testing all delete Users API----------
31. Testing listAllUsers API-----------
32. No user exist----------
完整的例子
更新pom.xml添加项目依赖
[html] view plain copy
1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
3. <modelVersion>4.0.0</modelVersion>
4. <groupId>com.websystique.springmvc</groupId>
5. <artifactId>Spring4MVCCRUDRestService</artifactId>
6. <packaging>war</packaging>
7. <version>1.0.0</version>
8. <name>Spring4MVCCRUDRestService Maven Webapp</name>
9.
10. <properties>
11. <springframework.version>4.2.0.RELEASE</springframework.version>
12. <jackson.version>2.5.3</jackson.version>
13. </properties>
14.
15. <dependencies>
16. <dependency>
17. <groupId>org.springframework</groupId>
18. <artifactId>spring-webmvc</artifactId>
19. <version>${springframework.version}</version>
20. </dependency>
21. <dependency>
22. <groupId>org.springframework</groupId>
23. <artifactId>spring-tx</artifactId>
24. <version>${springframework.version}</version>
25. </dependency>
26.
27. <dependency>
28. <groupId>com.fasterxml.jackson.core</groupId>
29. <artifactId>jackson-databind</artifactId>
30. <version>${jackson.version}</version>
31. </dependency>
32. <dependency>
33. <groupId>javax.servlet</groupId>
34. <artifactId>javax.servlet-api</artifactId>
35. <version>3.1.0</version>
36. </dependency>
37.
38. </dependencies>
39.
40.
41. <build>
42. <pluginManagement>
43. <plugins>
44. <plugin>
45. <groupId>org.apache.maven.plugins</groupId>
46. <artifactId>maven-compiler-plugin</artifactId>
47. <version>3.2</version>
48. <configuration>
49. <source>1.7</source>
50. <target>1.7</target>
51. </configuration>
52. </plugin>
53. <plugin>
54. <groupId>org.apache.maven.plugins</groupId>
55. <artifactId>maven-war-plugin</artifactId>
56. <version>2.4</version>
57. <configuration>
58. <warSourceDirectory>src/main/webapp</warSourceDirectory>
59. <warName>Spring4MVCCRUDRestService</warName>
60. <failOnMissingWebXml>false</failOnMissingWebXml>
61. </configuration>
62. </plugin>
63. </plugins>
64. </pluginManagement>
65.
66. <finalName>Spring4MVCCRUDRestService</finalName>
67. </build>
68. </project>
User Service
[java] view plain copy
1. package com.websystique.springmvc.service;
2.
3. import java.util.List;
4.
5. import com.websystique.springmvc.model.User;
6.
7.
8.
9. public interface UserService {
10.
11. long id);
12.
13. User findByName(String name);
14.
15. void saveUser(User user);
16.
17. void updateUser(User user);
18.
19. void deleteUserById(long id);
20.
21. List<User> findAllUsers();
22.
23. void deleteAllUsers();
24.
25. public boolean isUserExist(User user);
26.
27. }
[java] view plain copy
1. package com.websystique.springmvc.service;
2.
3. import java.util.ArrayList;
4. import java.util.Iterator;
5. import java.util.List;
6. import java.util.concurrent.atomic.AtomicLong;
7.
8. import org.springframework.stereotype.Service;
9. import org.springframework.transaction.annotation.Transactional;
10.
11. import com.websystique.springmvc.model.User;
12.
13. @Service("userService")
14. @Transactional
15. public class UserServiceImpl implements UserService{
16.
17. private static final AtomicLong counter = new AtomicLong();
18.
19. private static List<User> users;
20.
21. static{
22. users= populateDummyUsers();
23. }
24.
25. public List<User> findAllUsers() {
26. return users;
27. }
28.
29. public User findById(long id) {
30. for(User user : users){
31. if(user.getId() == id){
32. return user;
33. }
34. }
35. return null;
36. }
37.
38. public User findByName(String name) {
39. for(User user : users){
40. if(user.getName().equalsIgnoreCase(name)){
41. return user;
42. }
43. }
44. return null;
45. }
46.
47. public void saveUser(User user) {
48. user.setId(counter.incrementAndGet());
49. users.add(user);
50. }
51.
52. public void updateUser(User user) {
53. int index = users.indexOf(user);
54. users.set(index, user);
55. }
56.
57. public void deleteUserById(long id) {
58.
59. for (Iterator<User> iterator = users.iterator(); iterator.hasNext(); ) {
60. User user = iterator.next();
61. if (user.getId() == id) {
62. iterator.remove();
63. }
64. }
65. }
66.
67. public boolean isUserExist(User user) {
68. return findByName(user.getName())!=null;
69. }
70.
71. private static List<User> populateDummyUsers(){
72. new ArrayList<User>();
73. new User(counter.incrementAndGet(),"Sam",30, 70000));
74. new User(counter.incrementAndGet(),"Tom",40, 50000));
75. new User(counter.incrementAndGet(),"Jerome",45, 30000));
76. new User(counter.incrementAndGet(),"Silvia",50, 40000));
77. return users;
78. }
79.
80. public void deleteAllUsers() {
81. users.clear();
82. }
83.
84. }
Model (模型)类
[java] view plain copy
1. package com.websystique.springmvc.model;
2.
3. public class User {
4.
5. private long id;
6.
7. private String name;
8.
9. private int age;
10.
11. private double salary;
12.
13. public User(){
14. 0;
15. }
16.
17. public User(long id, String name, int age, double salary){
18. this.id = id;
19. this.name = name;
20. this.age = age;
21. this.salary = salary;
22. }
23.
24. public long getId() {
25. return id;
26. }
27.
28. public void setId(long id) {
29. this.id = id;
30. }
31.
32. public String getName() {
33. return name;
34. }
35.
36. public void setName(String name) {
37. this.name = name;
38. }
39.
40. public int getAge() {
41. return age;
42. }
43.
44. public void setAge(int age) {
45. this.age = age;
46. }
47.
48. public double getSalary() {
49. return salary;
50. }
51.
52. public void setSalary(double salary) {
53. this.salary = salary;
54. }
55.
56. @Override
57. public int hashCode() {
58. final int prime = 31;
59. int result = 1;
60. int) (id ^ (id >>> 32));
61. return result;
62. }
63.
64. @Override
65. public boolean equals(Object obj) {
66. if (this == obj)
67. return true;
68. if (obj == null)
69. return false;
70. if (getClass() != obj.getClass())
71. return false;
72. User other = (User) obj;
73. if (id != other.id)
74. return false;
75. return true;
76. }
77.
78. @Override
79. public String toString() {
80. return "User [id=" + id + ", name=" + name + ", age=" + age
81. ", salary=" + salary + "]";
82. }
83.
84.
85. }
配置类
[java] view plain copy
1. package com.websystique.springmvc.configuration;
2.
3. import org.springframework.context.annotation.ComponentScan;
4. import org.springframework.context.annotation.Configuration;
5. import org.springframework.web.servlet.config.annotation.EnableWebMvc;
6.
7. @Configuration
8. @EnableWebMvc
9. @ComponentScan(basePackages = "com.websystique.springmvc")
10. public class HelloWorldConfiguration {
11.
12.
13. }
[java] view plain copy
1. package com.websystique.springmvc.configuration;
2.
3. import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
4.
5. public class HelloWorldInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
6.
7. @Override
8. protected Class<?>[] getRootConfigClasses() {
9. return new Class[] { HelloWorldConfiguration.class };
10. }
11.
12. @Override
13. protected Class<?>[] getServletConfigClasses() {
14. return null;
15. }
16.
17. @Override
18. protected String[] getServletMappings() {
19. return new String[] { "/" };
20. }
21.
22. }
为你的REST API添加CORS支持
当访问REST API时,你可能需要面对“同源策略”问题。
错误如下:
” No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://127.0.0.1:8080′ is therefore not allowed access.” OR
” XMLHttpRequest cannot load http://abc.com/bla. Origin http://localhost:12345 is not allowed by Access-Control-Allow-Origin.”
一般来说,在服务器端,我们在响应中返回额外的CORS访问控制头,实现跨域链接。
用 Spring的话,我么可以写一个简单的过滤器为每个响应添加CORS特征头。
[java] view plain copy
1. package com.websystique.springmvc.configuration;
2.
3. import java.io.IOException;
4.
5. import javax.servlet.Filter;
6. import javax.servlet.FilterChain;
7. import javax.servlet.FilterConfig;
8. import javax.servlet.ServletException;
9. import javax.servlet.ServletRequest;
10. import javax.servlet.ServletResponse;
11. import javax.servlet.http.HttpServletResponse;
12.
13.
14. public class CORSFilter implements Filter {
15.
16. public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
17. "Filtering on...........................................................");
18. HttpServletResponse response = (HttpServletResponse) res;
19. "Access-Control-Allow-Origin", "*");
20. "Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
21. "Access-Control-Max-Age", "3600");
22. "Access-Control-Allow-Headers", "x-requested-with");
23. chain.doFilter(req, res);
24. }
25.
26. public void init(FilterConfig filterConfig) {}
27.
28. public void destroy() {}
29.
30. }
需要将其添加在Spring 配置中:
[java] view plain copy
1. package com.websystique.springmvc.configuration;
2.
3. import javax.servlet.Filter;
4.
5. import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
6.
7. public class HelloWorldInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
8.
9. @Override
10. protected Class<?>[] getRootConfigClasses() {
11. return new Class[] { HelloWorldConfiguration.class };
12. }
13.
14. @Override
15. protected Class<?>[] getServletConfigClasses() {
16. return null;
17. }
18.
19. @Override
20. protected String[] getServletMappings() {
21. return new String[] { "/" };
22. }
23.
24. @Override
25. protected Filter[] getServletFilters() {
26. new CORSFilter()};
27. return singleton;
28. }
29.
30. }
源码下载:http://websystique.com/?smd_process_download=1&download_id=1689
源码(带SORS)下载: http://websystique.com/?smd_process_download=1&download_id=1890
特别说明:此系列教程有的童鞋下载下来运行 经常404 或者改成xml方式以后
缺少org.springframework.web.context.ContextLoaderServlet等