RESTEasy是JBoss的开源项目之一,是一个RESTful Web Services框架。RESTEasy的开发者Bill Burke同时也是JAX-RS的J2EE标准制定者之一。JAX-RS是一个JCP制订的新标准,用于规范基于HTTP的RESTful Web Services的API。
我们已经有SOAP了,为什么需要Restful WebServices?用Bill自己的话来说:"如果是为了构建SOA应用,从技术选型的角度来讲,我相信REST比SOAP更具优势。开发人员会意 识到使用传统方式有进行SOA架构有多复杂,更不用提使用这些做出来的接口了。这时他们就会发现Restful Web Services的光明之处。"
说了这么多,我们使用RESTEasy做一个项目玩玩看。首先创造一个maven1的web项目
1. mvn archetype:create -DgroupId=org.bluedash \
2.
3. -DartifactId=try-resteasy -DarchetypeArtifactId=maven-archetype-webapp
准备工作完成后,我们就可以开始写代码了,假设我们要撰写一个处理客户信息的Web Service,它包含两个功能:一是添加用户信息;二是通过用户Id,获取某个用户的信息,而交互的方式是标准的WebService形式,数据交换格 式为XML。假设一条用户包含两个属性:Id和用户名。那么我们设计交换的XML数据如下:
1. <user>
2. 1</id>
3. <name>liweinan</name>
4. </user>
首先要做的就是把上述格式转换成XSD2,网上有在线工具可以帮助我们完成这一工作3,在此不详细展开。使用工具转换后,生成如下xsd文件:
1. <?xml version="1.0" encoding="utf-8"?>
2. <xsd:schema attributeFormDefault="unqualified" elementFormDefault="qualified"
3. version="1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
4. "user" type="userType"
5. "userType">
6. <xsd:sequence>
7. "id" type="xsd:int"
8. "name" type="xsd:string"
9. </xsd:sequence>
10. </xsd:complexType>
11. </xsd:schema>
有了xsd文件,我们便可以使用JDK自带工具的xjc将xsd转换成为Java的Class。将上述xsd文件存为 user.xsd
,并使用如下命令进行转换:
1. xjc user.xsd
执行结束后我们会得到一系列的类文件:
1. Li-Weinans-MacBook-Pro:Desktop liweinan$ xjc user.xsd
2. parsing a schema...
3. compiling a schema...
4. generated/ObjectFactory.java
5. generated/UserType.java
这样,我们的XML格式的交换数据便转化为面向对像的Java类了,是不是感觉有点像Hibernate的ORM理念?没错,将XML映射成成面向对象的数据类,这个过程叫做XML Binding,即XML绑定。这个过程也有J2EE标准,叫做JAXB4。而RESTEasy是全面支持JAXB的。可以说RESTEasy所支持的JAX-RS标准,当与JAXB标准结合在一起使用时,就可以发挥出最大优势,让程序员少写一堆一堆的代码。有关JAXB标准,会在独立的篇章中 详细讨论,在此先不展开。总之我们将生成的Java类放进项目中等候使用。我们可以看一下UserType类的内容:
1. package
2.
3. import
4. import
5. import
6. import
7.
8. @XmlAccessorType(XmlAccessType.FIELD)
9. @XmlType(name = "userType", propOrder = {
10. "id",
11. "name"
12. })
13. public class
14.
15. protected int
16. @XmlElement(required = true)
17. protected
18.
19. /**
20. * Gets the value of the id property.
21. *
22. */
23. public int
24. return
25. }
26.
27. /**
28. * Sets the value of the id property.
29. *
30. */
31. public void setId(int
32. this.id = value;
33. }
34.
35. /**
36. * Gets the value of the name property.
37. *
38. * @return
39. * possible object is
40. * {@link String }
41. *
42. */
43. public
44. return
45. }
46.
47. /**
48. * Sets the value of the name property.
49. *
50. * @param value
51. * allowed object is
52. * {@link String }
53. *
54. */
55. public void
56. this.name = value;
57. }
58.
59. }
可以看到,XML格式就是通过一些JAXB的标记被映射成了Java类。我们没写什么代码,已经把数据模型定义清楚了。接下来我们撰写最核心的 WebService API。我们的WebService包含两个接口:一个是添加用户接口createUser,另一个是获取用户接口getUser:
1. package
2.
3. import
4. import
5. import
6. import
7.
8. import
9. import
10. import
11. import
12. import
13. import
14. import
15. import
16.
17. @Path("/users")
18. public class
19.
20. private
21. new
22. private AtomicInteger idGenerator = new
23.
24. @POST
25. @Consumes("application/xml")
26. public
27. user.setId(idGenerator.incrementAndGet());
28. userStore.put(user.getId(), user);
29. " created: "
30. + user.getId());
31. return Response.created(URI.create("/users/"
32. + user.getId())).build();
33. }
34.
35. @GET
36. @Path("{id}")
37. @Produces("application/xml")
38. public UserType getUser(@PathParam("id") int
39. UserType u = userStore.get(id);
40. if (u == null) {
41. throw new
42. Response.Status.NOT_FOUND);
43. }
44. return
45. }
46.
47. }
用几个简单的JAX-RS标记,便把普通的函数变成了WebService接口。而这些标记将由RESTEasy支持生效。接下来我们将要进行 RESTEasy的配置工作。RESTEasy的配置方法有多种多样,可以和Spring等容器集成,也可以独立运行,因为我们用的Servlet的形式 使RESTEasy进行工作,这也是最主流的方式,因此在这里使用web容器来加载它,首先定义一个配置类:
1. package
2.
3. import
4. import
5.
6. import
7.
8. public class BluedashResteasyApplication extends
9. private Set<Object> singletons = new
10. private Set<Class<?>> classes = new
11.
12. public
13. // classes.add(UserServlet.class);
14. new
15. }
16.
17. @Override
18. public
19. return
20. }
21.
22. @Override
23. public
24. return
25. }
26. }
这个类扩展JAX-RS的Application接口,用于封装我们的WebService API方法。我们可以看到JAX-RS支持两种封装方法,一种是classes封装,由容器管理WebServices类的实例化和销毁等动作,一个线程 一个实例,开发者不需要关心线程安全问题。但这种方法可能比较浪费资源。如果开发者想自己管理线程安全,共线程共用一个WebServices实例,那么 就用singletons封装。我们在这里用的singletons封装,这也就解释了为什么我们在 UserServlet中使用了ConcurrentHashMap和AtomicInteger这些保障线程安全的类。接下来就是在web.xml中启 动RESTEasy:
1. <!DOCTYPE web-app PUBLIC
2. "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
3. "http://java.sun.com/dtd/web-app_2_3.dtd"
4.
5. <web-app>
6. <display-name>Archetype Created Web Application</display-name>
7.
8. <context-param>
9. <param-name>javax.ws.rs.core.Application</param-name>
10. <param-value>org.bluedash.resteasy.
11. BluedashResteasyApplication</param-value>
12. </context-param>
13.
14. <listener>
15. class>org.jboss.resteasy.plugins.server.
16. class>
17. </listener>
18.
19. <servlet>
20. <servlet-name>Resteasy</servlet-name>
21. class>org.jboss.resteasy.plugins.server.servlet.
22. class>
23. </servlet>
24.
25. <servlet-mapping>
26. <servlet-name>Resteasy</servlet-name>
27. <url-pattern>/*</url-pattern>
28. </servlet-mapping>
29. </web-app>
没错,就是这么简单,这样,我们的WebService就完成了!还差点什么呢?嗯,还差一个Test Case来使用我们的WebService接口,并验证它的正确性,让我们来写一个TestUserAPI
1. package
2.
3. import
4. import
5. import
6. import
7.
8. import
9.
10. public class TestUserAPI extends
11. public static final
12. "http://127.0.0.1:8080/try-resteasy/users";
13.
14. public void testCreateUserAndGetUser() throws
15. URL url =
16. new
17. HttpURLConnection connection =
18. (HttpURLConnection) url.openConnection();
19. "POST");
20. "Content-Type", "application/xml");
21. true);
22. false);
23. 1000);
24.
25. "<user><name>liweinan</name></user>";
26. OutputStream os = connection.getOutputStream();
27. os.write(userXML.getBytes());
28. os.flush();
29.
30. assertEquals(HttpURLConnection.HTTP_CREATED, connection
31. .getResponseCode());
32. connection.disconnect();
33.
34. }
35. }
一切都已经准备就绪,最后我们要配置一下Maven,让它下载所需的RESTEasy等库,然后配置Maven使用Jetty Web服务器,来把我们的服务和测试跑起来:
1. <project xmlns="http://maven.apache.org/POM/4.0.0"
2. "http://www.w3.org/2001/XMLSchema-instance"
3. //maven.apache.org/POM/4.0.0
4. http://maven.apache.org/maven-v4_0_0.xsd">
5. 4.0.0</modelVersion>
6. <groupId>org.bluedash</groupId>
7. try-resteasy</artifactId>
8. <packaging>war</packaging>
9. 1.0-SNAPSHOT</version>
10. try-resteasy Maven Webapp</name>
11. //maven.apache.org</url>
12. <repositories>
13. <repository>
14. <id>JBossMavenRepo</id>
15. <name>JBoss Maven2 repo</name>
16. //repository.jboss.org/maven2</url>
17. <releases>
18. true</enabled>
19. </releases>
20. <snapshots>
21. false</enabled>
22. </snapshots>
23. </repository>
24. </repositories>
25. <dependencies>
26. <dependency>
27. <groupId>junit</groupId>
28. <artifactId>junit</artifactId>
29. 4.4</version>
30. <scope>test</scope>
31. </dependency>
32. <dependency>
33. <groupId>org.jboss.resteasy</groupId>
34. <artifactId>resteasy-jaxrs</artifactId>
35. 1.2.RC1</version>
36. </dependency>
37. <dependency>
38. <groupId>org.jboss.resteasy</groupId>
39. <artifactId>resteasy-jaxb-provider</artifactId>
40. 1.2.RC1</version>
41. </dependency>
42. <dependency>
43. <groupId>javax.servlet</groupId>
44. <artifactId>servlet-api</artifactId>
45. 2.4</version>
46. </dependency>
47. </dependencies>
48. <build>
49. try-resteasy</finalName>
50. <plugins>
51. <plugin>
52. <groupId>org.apache.maven.plugins</groupId>
53. <artifactId>maven-compiler-plugin</artifactId>
54. <configuration>
55. 1.6</source>
56. 1.6</target>
57. 8</encoding>
58. </configuration>
59. </plugin>
60. <plugin>
61. <groupId>org.apache.maven.plugins</groupId>
62. <artifactId>maven-surefire-plugin</artifactId>
63. <configuration>
64. <excludes>
65. /**</exclude>
66. </excludes>
67. </configuration>
68. <executions>
69. <execution>
70. <id>integration-tests</id>
71. <phase>integration-test</phase>
72. <goals>
73. <goal>test</goal>
74. </goals>
75. <configuration>
76. <skip>false</skip>
77. <excludes>
78. <exclude>none</exclude>
79. </excludes>
80. <includes>
81. <include>**/integration/**</include>
82. </includes>
83. </configuration>
84. </execution>
85. </executions>
86. </plugin>
87. <plugin>
88. <groupId>org.mortbay.jetty</groupId>
89. <artifactId>maven-jetty-plugin</artifactId>
90. 6.1.15</version>
91. <configuration>
92. 5</scanIntervalSeconds>
93. <stopKey>foo</stopKey>
94. 9999</stopPort>
95. </configuration>
96. <executions>
97. <execution>
98. <id>start-jetty</id>
99. <phase>pre-integration-test</phase>
100. <goals>
101. <goal>run</goal>
102. </goals>
103. <configuration>
104. 5</scanIntervalSeconds>
105. true</daemon>
106. </configuration>
107. </execution>
108. <execution>
109. <id>stop-jetty</id>
110. <phase>post-integration-test</phase>
111. <goals>
112. <goal>stop</goal>
113. </goals>
114. </execution>
115. </executions>
116. </plugin>
117. </plugins>
118. </build>
119. </project>
有关Maven的配置就不详细展开了。配置完成后我们便可以运行单元测试,看看WebServices是否正确运行。执行下述命令
1. mvn integration-test
执行结果如下:
1. -------------------------------------------------------
2. T E S T S
3. -------------------------------------------------------
4. Running org.bluedash.resteasy.test.integration.test.TestUserAPI
5. liweinan created: 1
6. Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.372
7.
8. Results :
9.
10. Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
可以看到,我们的测试按预期执行成功了。这篇文章中,我简单向大家介绍了RESTEasy的初步使用方法,希望对大家在架构SOA应用时,有所帮助。JAX-RS标准做为J2EE家庭中相对较新的一员,其应用前景是十分广阔的。