IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope:Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  Open source | Java technology | SOA and Web services  >

使用 Apache Geronimo 和 POJO 构建 SOA 框架

通过 POJO 编程、GBean 和 Geronimo 内核简化任务

developerWorks
文档选项

未显示需要 JavaScript 的文档选项

样例代码


级别: 中级

J. Jeffrey Hanson (jeff@jeffhanson.com), 首席架构师, eReinsure.com, Inc.

2006 年 9 月 11 日

在不考虑库和框架强制执行的应用程序编程接口 (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.MBeanServerMBeanServer 充当 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. 已加载
  2. 未加载
  3. 将要开始
  4. 正在运行
  5. 将要停止
  6. 已停止
  7. 失败

清单 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 框架
用于 SOA 的 POJO 框架

该框架旨在从客户机接收 HTTP 请求,然后将请求传递到调度程序组件,该组件会发送消息,并将请求分派给业务委派组件。 然后,业务委派组件使用服务定位符找到特定请求的服务。业务委派组件调用该服务,并将任何返回值打包为模型对象。适当的视图组件将处理模型对象,并返回它作为对客户机的格式化响应。图 2 中的顺序图说明了这些步骤。


图 2. 典型 HTTP 请求和服务调用的往返顺序
典型 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 frameworkGeronimoSOAwithPOJOs.zip14KBHTTP
关于下载方法的信息


参考资料

学习

获得产品和技术

讨论


关于作者

Jeff Hanson

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




对本文的评价

太差! (1)
需提高 (2)
一般;尚可 (3)
好文章 (4)
真棒!(5)

建议?




回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款