24.5 Web 服务

Spring提供了对标准Java Web服务API的全面支持:

  • 使用JAX-WS暴露Web服务
  • 使用JAX-WS访问Web服务

除了在Spring Core中支持 JAX-WS,Spring portfolio也提供了一种特性Spring Web Services,一种为契约优先和文档驱动的web服务所提供的方案,强烈建议用来创建现代化的,面向未来的web服务。

24.5.1使用JAX- WS暴露基于servlet的web服务

Spring为JAX-WS servlet的端点实现提供了一个方便的基类 – SpringBeanAutowiringSupport. 为了暴露我们的AccountService,我们扩展Spring的SpringBeanAutowiringSupport类并在这里实现了我们的业务逻辑,通常委派调用业务层。我们在Spring管理的bean里面简单地使用Spring的@Autowired 注解来表达这样的依赖关系。

  1. /**
  2. * JAX-WS compliant AccountService implementation that simply delegates
  3. * to the AccountService implementation in the root web application context.
  4. *
  5. * This wrapper class is necessary because JAX-WS requires working with dedicated
  6. * endpoint classes. If an existing service needs to be exported, a wrapper that
  7. * extends SpringBeanAutowiringSupport for simple Spring bean autowiring (through
  8. * the @Autowired annotation) is the simplest JAX-WS compliant way.
  9. *
  10. * This is the class registered with the server-side JAX-WS implementation.
  11. * In the case of a Java EE 5 server, this would simply be defined as a servlet
  12. * in web.xml, with the server detecting that this is a JAX-WS endpoint and reacting
  13. * accordingly. The servlet name usually needs to match the specified WS service name.
  14. *
  15. * The web service engine manages the lifecycle of instances of this class.
  16. * Spring bean references will just be wired in here.
  17. */
  18. import org.springframework.web.context.support.SpringBeanAutowiringSupport;
  19. @WebService(serviceName="AccountService")
  20. public class AccountServiceEndpoint extends SpringBeanAutowiringSupport {
  21. @Autowired
  22. private AccountService biz;
  23. @WebMethod
  24. public void insertAccount(Account acc) {
  25. biz.insertAccount(acc);
  26. }
  27. @WebMethod
  28. public Account[] getAccounts(String name) {
  29. return biz.getAccounts(name);
  30. }
  31. }

我们的AccountServletEndpoint需要和Spring在同一个上下文的web应用里运行,以允许访问Spring的功能。为JAX-WS servlet端点部署使用标准规约是Java EE 5 环境下的默认情况。

24.5.2 使用JAX-WS暴露单独web服务

Oracle JDK 1.6附带的内置JAX-WS provider 使用内置的HTTP服务器来暴露web服务。Spring的SimpleJaxWsServiceExporter类检测所有在Spring应用上下文中配置有@WebService注解的bean,然后通过默认的JAX-WS服务器(JDK 1.6 HTTP服务器)导出。

在这种场景下,端点实例将被作为Spring bean来定义和管理。它们将使用JAX-WS引擎来注册,但其生命周期将由Spring应用程序上下文决定。这意味着Spring的显示依赖注入可用于端点实例。当然通过@Autowired来进行注解驱动的注入也会起作用。

  1. <bean class="org.springframework.remoting.jaxws.SimpleJaxWsServiceExporter">
  2. <property name="baseAddress" value="http://localhost:8080/"/>
  3. </bean>
  4. <bean id="accountServiceEndpoint" class="example.AccountServiceEndpoint">
  5. ...
  6. </bean>
  7. ...

AccountServiceEndpoint可能来自于Spring的SpringBeanAutowiringSupport,也可能不是。因为这里的端点是由Spring完全管理的bean。这意味着端点实现可能像下面这样没有任何父类定义 – 而且Spring的@Autowired配置注解仍然能够使用:

  1. @WebService(serviceName="AccountService")
  2. public class AccountServiceEndpoint {
  3. @Autowired
  4. private AccountService biz;
  5. @WebMethod
  6. public void insertAccount(Account acc) {
  7. biz.insertAccount(acc);
  8. }
  9. @WebMethod
  10. public List<Account> getAccounts(String name) {
  11. return biz.getAccounts(name);
  12. }
  13. }

24.5.3 使用JAX-WS RI的Spring支持来暴露服务

Oracle的JAX-WS RI被作为GlassFish项目的一部分来开发,它使用了Spring支持来作为JAX-WS Commons项目的一部分。这允许把JAX-WS端点作为Spring管理的bean来定义。这与前面章节讨论的单独模式类似 – 但这次是在Servlet环境中。注意这在Java EE 5环境中是不可迁移的,建议在没有EE的web应用环境如Tomcat中嵌入JAX-WS RI。 与标准的暴露基于servlet的端点方式不同之处在于端点实例的生命周期将被Spring管理。这里在web.xml将只有一个JAX-WS servlet定义。在标准的Java EE 5风格中(如上所示),你将对每个服务端点定义一个servlet,每个服务端点都代理到Spring bean (通过使用@Autowired,如上所示)。 关于安装和使用详情请查阅https://jax-ws-commons.dev.java.net/spring/

24.5.4 使用JAX-WS访问web服务

Spring提供了2个工厂bean来创建JAX-WS web服务代理,它们是LocalJaxWsServiceFactoryBean和JaxWsPortProxyFactoryBean。前一个只能返回一个JAX-WS服务对象来让我们使用。后面的是可以返回我们业务服务接口的代理实现的完整版本。这个例子中我们使用后者来为AccountService端点再创建一个代理:

  1. <bean id="accountWebService" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean">
  2. <property name="serviceInterface" value="example.AccountService"/>
  3. <property name="wsdlDocumentUrl" value="http://localhost:8888/AccountServiceEndpoint?WSDL"/>
  4. <property name="namespaceUri" value="http://example/"/>
  5. <property name="serviceName" value="AccountService"/>
  6. <property name="portName" value="AccountServiceEndpointPort"/>
  7. </bean>

serviceInterface是我们客户端将使用的远程业务接口。wsdlDocumentUrl是WSDL文件的URL. Spring需要用它作为启动点来创建JAX-WS服务。namespaceUri对应.wsdl文件中的targetNamespace。serviceName对应.wsdl文件中的服务名。portName对应.wsdl文件中的端口号。 现在我们可以很方便的访问web服务,因为我们有一个可以将它暴露为AccountService接口的bean工厂。我们可以在Spring中这样使用:

  1. <bean id="client" class="example.AccountClientImpl">
  2. ...
  3. <property name="service" ref="accountWebService"/>
  4. </bean>

从客户端代码上我们可以把这个web服务当成一个普通的类进行访问:

  1. public class AccountClientImpl {
  2. private AccountService service;
  3. public void setService(AccountService service) {
  4. this.service = service;
  5. }
  6. public void foo() {
  7. service.insertAccount(...);
  8. }
  9. }

Note: 上面例子被稍微简化了,因为JAX-WS需要端点接口及实现类来使用@WebService,@SOAPBinding等注解。 这意味着你不能简单地使用普通的Java接口和实现来作为JAX-WS端点,你需要首先对它们进行相应的注解。这些需求详情请查阅JAX-WS文档。