使用 Apache Geronimo 和 POJO 构建 SOA 框架
在不考虑库和框架强制执行的应用程序编程接口 (API) 约束的情况下进行软件开发,是一个非常诱人主张。它使许多人接受了普通旧式 Java™ 对象(Plain Old Java™ Object,POJO)编程的范例 —— 能够在 Java 平台上开发软件,而无需使用多余的接口或第三方 API。Apache Geronimo 框架为构建复杂应用程序和服务的 POJO 开发提供了一个可靠的基础设施。本文介绍 Geronimo 框架的一些组件和技巧,用于通过 POJO 策略来实现成功的、面向服务的开发。
面向服务的架构 (SOA) 和面向服务的编程 是两个术语,指软件工程的风格,它们将业务逻辑封装为模块化服务。这些服务以动态运行时环境为目标,在该环境中,服务提供者和服务消费者之间的关联是松散 耦合的。松散耦合的服务通常没有任何编译时关联,所以在运行时,您可以动态地将它们链接在一起,并允许开发人员根据需要灵活地做出开发决策。除松散耦合之 外,下列概念也是面向服务的环境中的公共概念:
- 粗粒度:服务的粒度是指服务公开给出的功能范围。细粒度的服务 表示定义一定程度的功能性的公共接口。粗粒度的服务 表示较一般程度的功能性,通常适合于给定的业务领域。
- 位置透明度 (Location transparency):位置透明度是指客户机在不考虑位置的情况下访问网络上的服务。
- 协议独立性:协议独立性指客户机在不考虑通信/网络协议的情况下访问服务。
将服务与这些概念绑定在一起是一项艰难的任务。但 POJO 编程可以简化这一任务。
POJO 简介
POJO 是无需遵循特定外部接口或第三方 API 的 Java 类。此功能本身就是取消代码与外部关联的耦合。去耦的主要好处之一是让软件开发人员无需开发辅助任务(如持久性、事务支持和远程操作)。许多技术消除了组件/类的去耦,并促进了 POJO 编程,包括:
- 注释是开发工具使用的、并用于生成代码的元数据,它可以 “装饰” 一个类或部分类,以支持给定类型的功能或特性,如远程操作、持久性和框架支持。
- 依赖性注入是构建插入式 组件的技术,对象创建和关联是从组件移除的,并由容器或汇编组件实现。
- 反射是运行时发现的关于给定类或接口的信息,如方法、字段和构造函数。
每种去耦技术都有其优点和缺点。本文将通过 POJO 编程构建一个简单的 SOA 框架,它使用反射和 Geronimo 的 GBean 依赖性注入来让组件去耦。
JMX 和 Geronimo
Geronimo 构建在通用的内核基础上,它使用 Java Management Extensions (JMX) 和称为 GBean 的托管组件的依赖性注入框架。实际上,Geronimo 中的每件事物(适配器、应用程序和容器等)即是一个 GBean,也以 GBean 为基础。GBean 与 JMX 和 JMX Managed Beans (MBeans) 共享许多相似点和相同的底层基础设施。
|
JMX
JMX 规范已经作为系统管理、应用程序管理和资源管理方面的 Java 标准出现。JMX 为使用用于管理目的的属性和操作动态增加 Java 类、接口和运行时对象定义了一个标准。此增加技术也称为 instrumentation。 JMX 可以管理您使用 Java 编程语言抽象的任何资源(如应用程序、驱动程序或服务)。可以将每个托管资源叫做一个 MBean。JMX 定义了四个类型的 MBean:
- 标准 MBean 使用 Java 接口定义它们的管理属性和操作。
- 动态 MBean 使用运行时发现定义它们的管理属性和操作。
- 模型 MBean 充当希望公开管理操作和属性的对象的代理。
- 开放 MBean 使用预定义的元数据词汇公开类和对象的管理属性和操作。
与 MBean 交互的主要接口是 javax.management.MBeanServer。MBeanServer 充当 MBean 的中心库,并促进 MBean 与 MBean 客户机的通信。 ObjectName 对象可惟一地标识 MBean。ObjectName 实例包括:
- 一个域,是给定域的任意名称;推荐使用逆向域名称系统 (DNS) 对域进行命名的约定,方式与 Java 包命名方式相同。
- 一个键属性列表,是一个库,一组任意的、无序的键,并与值关联。
下列代码演示如何构造典型的 ObjectName 对象: String domain = "com.jeffhanson.test"; String keyPropertyList = "domain:Name=TestBean,Type=GenericService"; ObjectName objName = new ObjectName(domain + ":" + keyPropertyList);
将 ObjectName 对象用作许多 MBeanServer 方法的参数,以便检索属性,并调用 MBean 上的操作。 |
|
Geronimo 的 GBean 框架
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 内核
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; } |
Geronimo 中面向服务的 POJO 的可适应框架
本 文中引用的用于 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://:/?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://:/?
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。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/374079/viewspace-130287/,如需转载,请注明出处,否则将追究法律责任。