Web 服务客户端的服务定位器
作者:Sean Brydon
问题描述
许多应用程序都使用服务定位器模式。目前,Web 服务在应用程序中越来越通用,为了支持 Web 服务,需要更新服务定位器代码。此外,如果应用程序目前尚未应用服务定位器模式,则应该考虑使用该模式。
企业应用程序需要一种查找引用的方法,这些引用提供对分布式组件和 Web 服务的访问。Java[TM] 2 Platform, Enterprise Edition (J2EE[TM]) 应用程序使用 Java 命名和目录接口 (JNDI) 查找 Enterprise Bean home 接口、Java Message Service (JMS) 组件、数据源、连接、连接工厂,现在也可以查找 Web 服务。重复的查找代码会使代码难以读取和维护。服务定位器模式将该代码集中到一个类中,并消除了冗余代码,否则冗余代码会分散在应用程序的各个部分。
此解决方案适用于以下情况:
- 客户端应用程序使用的是 J2EE1.4 平台。
- 客户端应用程序具有 J2EE 组件,这些组件充当调用目标 Web 服务的客户端。
解决方案
服务定位器模式已经应用了一段时间,许多 J2EE 应用程序都使用服务定位器类查找 J2EE 资源。现在,获得了可以查找的 Web 服务和引用的服务,我们需要考虑如何利用该解决方案来支持 Web 服务。
该解决方案包括以下内容:
- 提供服务定位器模式的简要说明。如果您熟悉服务定位器模式,则可以跳过此部分,直接跳转到解决方案策略部分。
- 提供一种策略,为查找 Web 服务的客户端添加支持,并提供重构应用程序代码的示例,从而在 Web 服务中应用服务定位器。然后我们将提出一些策略作为不同情况下的解决方案。
- 简要显示了如何应用服务定位器经常使用的缓存策略。
检查服务定位器的模式
让我们看一下来自 Java Adventure Builder 的早期服务定位器示例,其中添加了 Web 服务支持。从下面的代码可以看出,有多种方法可以获取各种 J2EE 资源(如 EJBHome、JMS ConnectionFactory 或数据库之类的数据源)的引用。
public class ServiceLocator {
private transient InitialContext ic;
public ServiceLocator() throws ServiceLocatorException {
try {
ic = new InitialContext();
} catch (Exception e) {
throw new ServiceLocatorException(e);
}
}
/**
* @return the EJB Home factory corresponding to the homeName
*/
public EJBHome getRemoteHome(String jndiHomeName, Class
className)
throws ServiceLocatorException {
try {
Object objref = ic.lookup(jndiHomeName);
return (EJBHome)
PortableRemoteObject.narrow(objref, className);
} catch (Exception e) {
throw new ServiceLocatorException(e);
}
}
/**
* @return the factory for the factory to get JMS connections from
*/
public ConnectionFactory getJMSConnectionFactory(String
jmsConnFactoryName)
throws ServiceLocatorException {
try {
return (ConnectionFactory)
ic.lookup(jmsConnFactoryName);
} catch (Exception e) {
throw new ServiceLocatorException(e);
}
}
/*
* @return the DataSource corresponding to the name parameter
*/
public DataSource getDataSource(String dataSourceName) throws
ServiceLocatorException {
try {
return
(DataSource)ic.lookup(dataSourceName);
} catch (Exception e) {
throw new ServiceLocatorException(e);
}
}
...//and other J2EE resources
}
代码示例 1:J2EE 资源的服务定位器,不支持 Web 服务
我们来看一下服务定位器模式中的一些主要成员:

在使用业务对象或服务的客户端上,首先需要查找并获取该服务的引用。使用服务定位器模式时,将遇到以下主要成员:
- 客户端对象:这是要调用目标 Web 服务的 Servlet/JSP[TM]/EJB[TM] 或 Java 对象。它使用服务定位器查找服务。
- ServiceLocator:这是服务定位器的实现。
- InitialContext:这是用来查找 JNDI 命名目录中的引用的初始上下文。
- 资源工厂:这是一个资源工厂(如 EJB Home)。
- 组件或资源:这是用来访问包含业务逻辑的服务、资源或组件的引用。例如,它可以是一个 EJB 会话 Bean。
现在,我们已经对服务定位器有了一定的了解,让我们再来看一下如何添加 Web 服务支持。
将 Web 服务支持添加到服务定位器
现在,我们来看一下要支持 Web 服务,需要向服务定位器添加哪些方法。
在本示例中,假定存在一个正在运行某服务的部署的应用程序,该应用程序是一个 war 文件,包含了访问正在运行的服务的 Servlet。在 J2EE 中,对于像 Web 应用程序中要访问 Web 服务应用程序的 Servlet 组件,需要编写调用服务的代码,同时,在 web.xml 部署描述符中需要包含一个对服务的引用(请参见代码示例 2)。
<service-ref>
<description>Client of Some
Service</description>
<service-ref-name>service/
SomeService
</service-ref-name>
<service-interface>
com.sun.j2ee.blueprints.myclientapplication.
SomeService
</service-interface>
<wsdl-file>WEB-INF/wsdl/SomeService.wsdl</wsdl-file>
<jaxrpc-mapping-file>WEB-INF/someservice-mapping.xml</jaxrpc-mapping-file>
<service-qname
xmlns:servicens="urn:
SomeService
">servicens:
SomeService
</service-qname>
</service-ref>
代码示例 2:web.xml 部署描述符中的服务引用
如果部署了客户端应用程序,并为该服务引用绑定了 JNDI 名称,则应用程序只需访问此命名服务即可获取对该服务的访问。在本示例中,假定服务引用在命名目录中绑定为 "java:comp/env/service/SomeService"
。表 1 左侧的代码显示了未使用服务定位器类的代码。现在,需要重构此代码才能使用服务定位器,如表右侧所示。
未使用服务定位器 |
重构以使用服务定位器 |
String s=
"java:comp/env/service/SomeService";
try {
Context ic = new InitialContext();
Service myService =(Service)ic.lookup(s);
SomeServiceSEI port =(SomeServiceSEI)
myService
.getPort(SomeServiceSEI.class);
//now just call the web service method
port.someMethod(someParameter);
} catch(Exception exe){
...
}
|
String s=
"java:comp/env/service/SomeService";
try
{
ServiceLocator sLoc = new ServiceLocator();
SomeService SEI port = ( SomeService SEI)
sLoc .getServicePort( s , SomeService SEI.class);
//now just call the web service method
port.someMethod(someParameter);
}
catch(ServiceLocatorException sle){
...
} catch(Exception exe){
...
}
|
表 1:重构以使用服务定位器模式
正如您看到的那样,为 Web 服务更新服务定位器只需进行简单的更改。尽管只需很少的代码更改,但这却非常有用,因为在应用程序中的许多地方都会重复使用该代码。在一段时间内,如果在多个地方出现重复代码,则这些代码会出现某些不一致、杂乱的情况,维护起来很困难。因此使用服务定位器可在一定程度上统一代码。
此外,还需要更新服务定位器类以支持查找 Web 服务。代码示例 3 显示了支持查找 Web 服务引用的服务定位器。
/**
* Implements Service Locator pattern for Web services
*/
public class ServiceLocator {
private transient InitialContext ic;
public ServiceLocator() throws
ServiceLocatorException {
try {
ic =
new InitialContext();
} catch (Exception e) {
throw new ServiceLocatorException(e);
}
}
public Remote getServicePort(String jndiHomeName,
Class className) throws ServiceLocatorException {
try {
Service service = (Service) ic.lookup(jndiHomeName);
return service.getPort(className);
} catch (Exception e) {
throw new ServiceLocatorException(e);
}
}
}
代码示例 3:支持 J2EE 组件充当 Web 服务客户端的服务定位器
代码如上所示。该模式非常易于使用。它确实已将额外的类添加到了应用程序中,这可以在一定程度上统一您的代码。
策略:将 Web 服务支持添加到实现缓存策略的服务定位器中
有些应用程序设计了使用缓存策略的服务定位器。这是为了避免创建不必要的 JNDI 初始上下文和进行不必要的服务对象查找。下面的代码示例 4 显示了应用缓存策略的服务定位器查找 Web 服务的信息。
public class ServiceLocator {
private InitialContext ic;
//used to hold references to resources
private Map cache = Collections.synchronizedMap(new
HashMap());
private static ServiceLocator instance = new
ServiceLocator();
public static ServiceLocator getInstance() {
return instance;
}
private ServiceLocator() throws
ServiceLocatorException {
try {
ic =
new InitialContext();
} catch (Exception e) {
throw new ServiceLocatorException(e);
}
}
public Remote getServicePort(String jndiHomeName,
Class className) throws ServiceLocatorException {
Remote servicePort = (Remote)
cache.get(jndiHomeName);
if
(servicePort ==
null) {
try {
Service service = (Service) ic.lookup(jndiHomeName);
servicePort =
service.getPort(className);
cache.put(jndiHomeName, servicePort);
} catch
(Exception e) {
throw new ServiceLocatorException(e);
}
}
return
servicePort;
}
}
代码示例 4:应用缓存策略的服务定位器查找 Web 服务引用
可以从上述代码示例中看出,需要查找 Web 服务的应用程序可以使用服务定位器模式,这与查找其他 J2EE 资源的方式基本相同。
参考资料
有关本主题的详细信息,请参阅以下资料:
- 《Core J2EE Patterns: Best Practices and Design Strategies, Second Edition》,作者:D Alur、D. Malks 和 J. Crupi。版权所有 2003,Prentice Hall PTR.
- 使用服务定位器的示例应用程序的设计文档包含了更详细的解释。
- 服务定位器模式通过 Java BluePrints Web 站点上的其他示例演示了该模式的更多一般用途。
© Sun Microsystems 2005。Java BluePrints Solutions Catalog 中的所有内容受版权保护,未经 Sun Microsystems 的明确书面许可,不得在其他产品中发布。