面向服务的架构 (SOA) 和面向服务的编程 是两个术语,指软件工程的风格,它们将业务逻辑封装为模块化服务。这些服务以动态运行时环境为目标,在该环境中,服务提供者和服务消费者之间的关联是松散耦合的。松散耦合的服务通常没有任何编译时关联,所以在运行时,您可以动态地将它们链接在一起,并允许开发人员根据需要灵活地做出开发决策。除松散耦合之外,下列概念也是面向服务的环境中的公共概念:
- 粗粒度:服务的粒度是指服务公开给出的功能范围。细粒度的服务 表示定义一定程度的功能性的公共接口。粗粒度的服务 表示较一般程度的功能性,通常适合于给定的业务领域。
- 位置透明度 (Location transparency):位置透明度是指客户机在不考虑位置的情况下访问网络上的服务。
- 协议独立性:协议独立性指客户机在不考虑通信/网络协议的情况下访问服务。
将服务与这些概念绑定在一起是一项艰难的任务。但 POJO 编程可以简化这一任务。
POJO 是无需遵循特定外部接口或第三方 API 的 Java 类。此功能本身就是取消代码与外部关联的耦合。去耦的主要好处之一是让软件开发人员无需开发辅助任务(如持久性、事务支持和远程操作)。许多技术消除了组件/类的去耦,并促进了 POJO 编程,包括:
- 注释是开发工具使用的、并用于生成代码的元数据,它可以 “装饰” 一个类或部分类,以支持给定类型的功能或特性,如远程操作、持久性和框架支持。
- 依赖性注入是构建插入式 组件的技术,对象创建和关联是从组件移除的,并由容器或汇编组件实现。
- 反射是运行时发现的关于给定类或接口的信息,如方法、字段和构造函数。
每种去耦技术都有其优点和缺点。本文将通过 POJO 编程构建一个简单的 SOA 框架,它使用反射和 Geronimo 的 GBean 依赖性注入来让组件去耦。
Geronimo 构建在通用的内核基础上,它使用 Java Management Extensions (JMX) 和称为 GBean 的托管组件的依赖性注入框架。实际上,Geronimo 中的每件事物(适配器、应用程序和容器等)即是一个 GBean,也以 GBean 为基础。GBean 与 JMX 和 JMX Managed Beans (MBeans) 共享许多相似点和相同的底层基础设施。
GBean 是 Geronimo 中的托管组件,这些组件共享许多相似点以及与 JMX MBean 的关系,如根据名为 GBeanInfo 的类公开属性和操作,该类与 JMX 替代物 MBeanInfo 类非常相似。Geronimo 将 MX4J 库(请参阅本文结尾的 参考资料)用作其 JMX 的实现。
GBean 维护状态和关联依赖性,并处理生命周期事件。GBean 可以注册为其他 GBean 状态中的相关方。启动相关 GBean 后,它将通过依赖性注入收到相关 GBean 的引用。GBean 在任何给定的时间可以处于下列七个生命周期状态之一:
- 已加载
- 未加载
- 将要开始
- 正在运行
- 将要停止
- 已停止
- 失败
清单 1 给出了一个包含一个属性(消息)的简单 GBean。
清单 1. 典型的 GBean
public class TestGBean
implements GBeanLifecycle
{
private static GBeanInfo GBEAN_INFO = null;
static
{
GBeanInfoBuilder infoFactory =
GBeanInfoBuilder.createStatic(TestGBean.class);
infoFactory.addAttribute("message", String.class, true);
infoFactory.addOperation("getMessage");
GBEAN_INFO = infoFactory.getBeanInfo();
}
private String message;
public String getMessage()
{
return message;
}
...
}
|
您可以使用 清单 2 中给出的代码来启动(激活)和停止 GBean。
清单 2. 启动典型的 GBean
ObjectName testGBeanOName =
ObjectName.newInstance("jeffhanson.test:ID=test");
GBeanData gBeanData =
new GBeanData(testGBeanOName, TestBean.GBEAN_INFO);
gBeanData.setAttribute("message", "Hello world");
geronimoKernel.loadGBean(gBeanData,
Thread.currentThread().
getContextClassLoader());
geronimoKernel.startGBean(testGBeanOName);
...
geronimoKernel.stopGBean(testGBeanOName);
geronimoKernel.unloadGBean(testGBeanOName);
|
您可以在整个 Geronimo 内核中大量地使用 GBean。
Geronimo 内核 是 GBean 的一个框架。使用此框架,您可以建模并构建任何复 杂的系统作为一组 GBean 容器和 GBean 组件,来管理状态、关系和事件处理。
使用 KernelFactory 类,以编程方式创建 Geronimo 内核是一个非常简单的过程。清单 3 说明如何通过启动内核、记录启动时间和加载并启动 servlet GBean 来创建名为 TestGeronimo 的新 Geronimo 内核。
清单 3. 创建一个简单的 Geronimo 内核
try
{
Kernel geronimoKernel =
BasicKernelFactory.newInstance().
createKernel("TestGeronimo");
geronimoKernel.boot();
log.debug("Geronimo BootTime: "
+ geronimoKernel.getBootTime());
// add the servlet GBean
ObjectName servletObjName =
new ObjectName("jeffhanson.test:ID=MyGBean");
GBeanData servletGBeanData = new GBeanData(servletObjName,
GBEAN_INFO);
ClassLoader classLoader = getClass().getClassLoader();
geronimoKernel.loadGBean(servletGBeanData, classLoader);
geronimoKernel.startGBean(servletObjName);
}
catch (Exception e)
{
log.error(e);
}
|
创建并运行内核后,调用 POJO 服务上的方法就变成了练习使用 Geronimo 内核服务器及其反射功能,如 清单 4 所示。
清单 4. 调用注册到内核的服务上的调用
private static
Object invokePOJOService(ObjectName serviceObjName,
String operationName,
String[] params)
throws Exception
{
String[] paramTypes = null;
if (params != null && params.length > 0)
{
paramTypes = new String[params.length];
for (int i = 0; i < params.length; i++)
{
paramTypes[i] = params[i].getClass().getName();
}
}
Kernel geronimoKernel =
KernelManager.getInstance().getGeronimoKernel();
Object retVal =
geronimoKernel.invoke(serviceObjName,
operationName,
(Object[])params,
paramTypes);
return retVal;
}
|
本文中引用的用于 SOA 的 POJO 框架使用 Geronimo 内核实例将 POJO 注册为 GBean,相关客户机可以查询并调用它们,而无需其他接口或 API。框架驻留在多层企业级应用程序环境中的业务层中。服务定位符类负责与内核交互,以查找并注册(如果需要) 用作服务的 POJO。然后服务定位符类将 POJO 返回到调用它们的业务委派组件。图 1 说明了框架中组件的关系。
图 1. 用于 SOA 的 POJO 框架
该框架旨在从客户机接收 HTTP 请求,然后将请求传递到调度程序组件,该组件会发送消息,并将请求分派给业务委派组件。 然后,业务委派组件使用服务定位符找到特定请求的服务。业务委派组件调用该服务,并将任何返回值打包为模型对象。适当的视图组件将处理模型对象,并返回它作为对客户机的格式化响应。图 2 中的顺序图说明了这些步骤。
图 2. 典型 HTTP 请求和服务调用的往返顺序
图 3 中的类图说明了框架的类之间的关系。
图 3. 框架的类之间的关系
框架驻留在企业级应用程序系统的业务层中。该框架公开一个接收 HTTP 请求的 servlet,并将内容分派给框架进行处理。下一节将阐述简单的部署过程。
您可以将框架的类和企业级应用程序打包在 .war 文件中,将其放置在 geronimo_home/deploy 目录下。如果此目录不存在,就创建它。
Geronimo 在启动时会自动部署 .war 文件。放置在 deploy 目录中的应用程序是热加载的,允许 Geronimo 在您做出更改时能够在运行时重新加载应用程序。这样使调试应用程序变得非常便利。
您可以使用位于 geronimo_home/bin 目录中的启动脚本(startup.bat 或 startup.sh)启动 Geronimo 应用服务器。当调用 Geronimo 启动脚本时,可以看到 Geronimo 控制台窗口。部署框架和应用程序后,启动时的 Geronimo 控制台窗口包含类似于 清单 5 所示的行,确认 Web 应用程序已经成功启动。
清单 5. Web 应用程序已经成功启动的确认
0 [main] DEBUG org.apache.geronimo.kernel.basic.BasicKernel - Starting boot 422 [main] DEBUG org.apache.geronimo.gbean.runtime.GBeanInstanceState - GBeanInstanceState for: :role=Kernel State changed from stopped to starting 422 [main] DEBUG org.apache.geronimo.gbean.runtime.GBeanInstanceState - GBeanInstanceState for: :role=Kernel State changed from starting to running 422 [main] DEBUG org.apache.geronimo.kernel.basic.BasicKernel - Booted 640 [main] DEBUG com.jeffhanson.apptier.FrontController - Geronimo BootTime: Sat May 20 18:51:08 MDT 2006 656 [main] DEBUG org.apache.geronimo.gbean.runtime.GBeanInstanceState - GBeanInstanceState for: jeffhanson.test:ID=FrontController State changed from stopped to starting 656 [main] DEBUG org.apache.geronimo.gbean.runtime.GBeanInstanceState - GBeanInstanceState for: jeffhanson.test:ID=FrontController State changed from starting to running |
现在,在 Web 浏览器窗口键入以下 URL,以激活 HelloWorld 服务上的 setMessage
操作:
http://<host>:<port>/<context>?Action=
HelloWorld&Operation=setMessage&Params=Hello+everybody!
当框架处理请求时,控制台的输出结果应类似于 清单 6 所示。
清单 6.
setMessage 操作处理的输出结果719 [main] DEBUG com.jeffhanson.businesstier.ServiceLocator - Adding service [HelloWorld] to kernel... 719 [main] DEBUG com.jeffhanson.businesstier.ServiceLocator - Loading GBean: jeffhanson.test:Name=HelloWorld,Type=GenericService 734 [main] DEBUG org.apache.geronimo.gbean.runtime.GBeanInstanceState - GBeanInstanceState for: jeffhanson.test:Name=HelloWorld,Type=GenericService State changed from stopped to starting 734 [main] DEBUG org.apache.geronimo.gbean.runtime.GBeanInstanceState - GBeanInstanceState for: jeffhanson.test:Name=HelloWorld,Type=GenericService State changed from starting to running |
在 Web 浏览器窗口中键入以下 URL,以激活 HelloWorld 服务上的 sayHello 操作:
http://<host>:<port>/<context>?
Action=HelloWorld&Operation=sayHello
当框架处理请求时,控制台的输出结果应类似于 清单 7 所示。
清单 7.
sayHello 操作处理的输出结果750 [main] DEBUG com.jeffhanson.businesstier.ServiceLocator - serviceObjName: jeffhanson.test:Name=HelloWorld,Type=GenericService 750 [main] DEBUG com.jeffhanson.businesstier.ServiceLocator - Service [HelloWorld] already in kernel 1156 [main] DEBUG com.jeffhanson.businesstier.ServiceLocator - serviceObjName: jeffhanson.test:Name=HelloWorld,Type=GenericService 1156 [main] INFO com.jeffhanson.businesstier.services.HelloWorld - Hello everybody! |
当 servlet 引擎关闭 servlet,并调用 servlet 上的 destroy 方法时,servlet 会关闭 Geronimo 内核。当 servlet 引擎关闭 servlet 时,您控制台的输出结果应类似于 清单 8 所示。
清单 8. servlet 关闭后的输出结果
1156 [main] DEBUG org.apache.geronimo.kernel.basic.BasicKernel - Starting kernel shutdown 1156 [main] DEBUG org.apache.geronimo.kernel.basic.BasicKernel - Kernel shutdown complete |
HelloWorld 类是带有 setMessage 方法、getMessage 方法和 sayHelloWorld 消息的简单 POJO。向 Geronimo 内核注册此类的实例后,您可以动态地调用该实例,并在运行时,使用依赖性注入将其与其他服务和组件关联。清单 9 中的代码说明了简单的 HelloWorld POJO 类。
清单 9. 简单的 HelloWorld 服务
package com.jeffhanson.businesstier.services;
import org.apache.log4j.Logger;
public class HelloWorld
{
private static Logger log = Logger.getLogger(HelloWorld.class);
private String message = "Hello world";
public void setMessage(String message)
{
if (message == null || message.length() <= 0)
{
throw new RuntimeException("HelloWorld.setMessage "
+ "param is not set");
}
this.message = message;
}
public String getMessage()
{
return message;
}
public void sayHello()
{
log.info(message);
}
}
|
设计可以对业务更改和事件做出及时响应的敏捷而又有效的 SOA 是一项复杂的任务,但是,围绕适当设计的 POJO 层构建的 SOA 可以帮助简化这一任务。Geronimo 平台提供了框架和工具,您可以使用它通过 POJO 构建灵活的、可扩展的和可维护的 SOA。
| 描述 | 名字 | 大小 | 下载方法 |
|---|---|---|---|
| SOA with POJOs framework | GeronimoSOAwithPOJOs.zip | 14KB | HTTP |
学习
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文 。
- 获得 POJO 编程基础 的明确定义和解释。
- 使用这些针对 SOA 和依赖性注入 的参考资料。
- 找到关于 MX4J 的信息。
- 更深入地学习 JMX。
- 获取关于 Java 反射 的参考资料。
- 查看 developerWorks Apache Geronimo 项目区域,获得文章、教程和其他参考资料,帮助您现在就使用 Geronimo 进行开发。
- 在 developerWorks 的 现在开始学习 Apache Geronimo 部分,找到对初学者和经验丰富的用户有帮助的参考资料。
- 查看 IBM® Support for Apache Geronimo 产品,让您开发世界级 IBM 支持的 Geronimo 应用程序。
- 访问 developerWorks 开放源码专区,获得广泛的 how-to 信息、工具和项目更新,帮助您使用开放源码技术进行开发,并与 IBM 产品结合使用。
- 浏览 developerWorks 开放源码专区提供的所有 Apache 文章 和 免费 Apache 教程。
- 在 Safari 书店 浏览上述和其他技术主题的书籍。
获得产品和技术
- 下载 Apache Geronimo, Version 1.0。
- 使用 IBM 试用软件 改革下一个开放源码开发项目,这些软件可通过下载或 DVD 获得。
- 下载 IBM WebSphere® Application Server Community Edition V1.0 的免费副本 —— 构建在 Apache Geronimo 开放源码技术之上的轻量级 J2EE 应用服务器,这是为帮助您加速开发和部署工作而设计的。
讨论
- 在 Apache Geronimo blog 上关注 Geronimo 开发的最新情况。
- 通过参与 developerWorks blogs 加入 developerWorks 社区。

Jeff Hanson 有着 20 多年的软件行业从业经验,担任过 Windows OpenDoc 项目的高级工程师,以及 Novell 的 Route 66 框架的主管架构师。现在,Jeff 作为 eReinsure.com, Inc. 的首席架构师,在为基于 Java EE 的再保险系统构建 Web 服务框架和平台。Jeff 写作了大量的文章和书籍,包括 .NET versus J2EE Web Services: A Comparison of Approaches、Pro JMX: Java Management Extensions 和 Web Services Business Strategies and Architectures。