基于组件开发 (Component-based development,CBD)已经流行很多年了。无论入行多久的开发人员都无疑在应用程序中利用过某种形式的重用。
它就像在客户端应用程序中使用一个Swing组件一样简单。就像技术的很多领域一样,软件开发和开发工具的进展不再被称为巨大的、惊人的跳跃,而是持续的创新和改进。当Sun Microsystems1997年首次推出Enterprise JavaBean(EJB)规范的时候,没有人认为管理持续性会存在如此巨大的商机。但事实恰恰如此。Sun听取,并在EJB 2.0规范中引入了 容器管理持久性(Container-Managed Persistence,CMP)。
最近,BEA听取客户意见,为一直致力于推广高效率CBD的WebLogic平台提供一种集成的开发环境:BEA WebLogic Workshop。本文将讨论Enterprise Java开发中使用组件时需面临的几个挑战,以及如何利用BEA控件框架来简化流程。
规划
应当在项目规划阶段考虑如何在应用程序中利用重用。与软件开发的很多方面一样,正确的规划是成功的关键。一旦确立了程序要求,第一件事就是查询存储库,确定可能利用到的候选组件。除了提供所需的功能性之外,组件还必须符合应用程序的技术要求。在规划阶段,还需要准备好回答几个很明确的问题:应用程序是基于Web的吗?JavaServer Pages应用程序servlet关联是否适当?是否需要包含具有可伸缩性和跨平台灵活性的 Web 服务?是否构建一种分布式的、瘦客户端应用程序?是否需要EJB提供可伸缩性和/或易于部署/更新特性?
假定组件存储库是良好构建的,在回答上述问题和确立程序要求时就可以毫不费力的确定组件是否有用。在很多种情况中,将组件集成到新项目中所需的结合代码(glue code)量最小。在其他情况中,可能会涉及到很多工作。如果工作量预算超过了最初创建组件的时间,重用就不会导致任何的成本削减,此时就需要寻找另一个组件或者从头创建组件。
规划流程的另一部分是决定创建单个应用程序使用的开发环境。这里有一些灵活性。例如,当构建n层的分布式应用程序时,客户端部分很可能会使用到诸如JBuilder的IDE。也可以使用JBuilder进行服务器端开发,或者可能决定使用WebLogic Workshop。
WebLogic Workshop和BEA控件框架
BEA WebLogic Server是一个强大的Enterprise Java开发平台。BEA WebLogic Workshop是紧密集成的,可以为WebLogic平台的Web服务(以及Web应用程序)提供一种集成的开发和测试环境。同样,考虑BEA控件框架所提供的额外优势也很重要。
例如,假设作为项目规划的结果,您确定了几种希望在新项目中使用的EJB组件。通过利用WebLogic Workshop和BEA控件框架,以及J2EE平台固有的灵活性,就可以创建一种分布式的、可伸缩的、后端系统来支持众多的客户端部署,包括Web应用、瘦客户端Java应用程序、跨平台部署以及其他!
为了证明这一点,我将展示一些利用EJB提供后端业务逻辑的示例应用程序。我们还要考虑BEA 控件框架如何简化将这些EJB集成到Web服务和Web应用程序中的流程。
注:此处引用的应用程序示例和WebLogic Workshop打包在一起。这样就可以关注于JWS项目广泛环境中指定组件的方面,并在自己的Workshop环境中引用示例项目和体验运行演示。
实体Bean
实体bean表现数据。通常,他们要么映射到关系数据库的记录中,要么映射到面向对象数据库的对象中。当状态的改变主要发生在事物环境中时,他们是可处理的。实体bean的实例可以被多个客户端引用。容器 — 在 WebLogic Server 中 — 负责确保bean中的数据和相应的数据库是同步的。图1显示了示例项目“AccountEJBClient.jws”被加载到WebLogic Workshop中。如图所示,Workshop已经将bean作为EJB控件封装起来,并已经实现了映射到相应EJB方法的控件接口。
如果另一个环境中正在使用该bean,就要在使用之前检查一下流程。首先,在JNDI注册表中查找EJB并获取home接口。其次,需要获得EJB实例。最后调用方法。这无疑是一种困难或者复杂的流程,但是利用控件框架可以使bean的实例化和访问更加简单。所需要做的只是添加新的指向EJB(参看图3)EJB控件(参看图2)。如图所示,一旦浏览了EJB,Workshop就可以自动查找home和远程接口。它采用bean的各种方法,并创建相应的控件接口。它为创建的每一个控件创建相应的控件文件(.CTRL)。程序清单1显示了 EJB 控件的控件文件。
注: 某些控件可以通过编辑CTRL文件进行修改。不要编辑EJB的CTRL文件。它是Workshop 根据从 EJB 代码中获得的信息创建的。
程序清单1
package ejbControl;
import weblogic.jws.*;
import weblogic.jws.control.*;
/**
* @jws:ejb home-jndi-name="ejb20-containerManaged-AccountHome"
* @editor-info:ejb home="AccountEJB.jar" bean="AccountEJB.jar"
*/
public interface AccountEJBControl
extends examples.ejb20.basic.containerManaged.AccountHome,
// home interface
examples.ejb20.basic.containerManaged.Account,
// bean interface
weblogic.jws.control.EntityEJBControl
// control interface
{
控件被创建之后就可以自动处理J2EE管道(plumbing),诸如bean的实例化和销毁、访问方法等。谨记,为了便于Workshop发现EJB的home和远程接口,Workshop项目的WEB-INF\lib目录中必须存放有EJB的JAR文件的本地副本。
程序清单2是createNewAccount操作的代码,说明了WebLogic Workshop如何访问EJB。它调用EJB控件(封装了EJB的Create方法的J2EE接口)的Create方法。正如所示,Workshop 已经指定了方法参数,并放入了异常处理。由于这是一个简单的“Web 服务方法到EJB方法”的 映射,因此不需要做其他工作(假设确信EJB的功能性)。但是如果愿意,您可以添加自定义的确认代码。
程序清单2
/**
* <p>Invokes the target EJB's <b>create</b> method and returns the result.</p>
*
* <ul>
* <li><b>key</b> is the account identifier (must be unique for each account).</li>
* <li><b>openingBalance</b> is the starting balance of the account.</li>
* <li><b>type</b> is the account type, e.g. "checking" or
"savings".</li>
* </ul>
*
* @return A String describing the result of the account creation.
*
* @jws:operation
*/
public String createNewAccount(String key, double openingBalance,
String type)
{
try
{
/** We can add custom validation code here
* if we so desire, prior to calling the
* create method.
*/
account.create(key, openingBalance, type);
}
catch (CreateException ce)
{
/** We can put code to process the exception
* here (or below), prior to returning the
* error message.
*/
return "Error " + ce.getLocalizedMessage();
}
catch (RemoteException re)
{
return "Error " + re.getLocalizedMessage();
}
return "Successful";
}
会话Bean
会话bean代表特定的业务流程步骤。他们捕捉用户和系统之间的交互或者会话。会话bean来自两个方面:有状态和无状态。有状态会话bean可以通过会话bean同一个客户端代理的客户端跨多种远程方法调用来维护状态。客户端的每一种方法调用被路由到服务器上相同的会话 bean实例中。这在需要运行长时间的服务器会话和需要服务器对象存储会话状态时非常有用。有状态会话bean的例子是在线购物车,客户可以使用远程方法来添加、去除或者修改购物车中的产品。
无状态会话bean不维护多个远程方法调用之间客户端可以依靠的状态。从客户端的角度看,这在方法执行的时间构成完整的工作单元的情况中非常有用。例如,在无状态会话Bean的例子中,在WebLogic Workshop中打开“TraderEJBClient.jws”示例项目。正如在JWS操作中所示(参见图4),这个组件只有两种方法:Buy和Sell。EJB控件(封装了EJB自身的接口)实际上有第三个接口:Create。事实上,每一个会话和实体EJB都包含这种方法。这就是bean的实例如何创建的。当涉及实体bean的时候,这种方法必须被显式地调用 — 因为创建实bean的实例实际上就是在相应的数据源中创建一行新的代码。但是,利用WebLogic Workshop,发送会话bean的Create方法是不必要的。BEA控件框架可以在调用任何方法的时候自动创建bean的实例。即使显示地调用Create方法,也未必得到新的实例。WebLogic Server可以简单的返回bean的现有实例。
由于这是一个细粒度的组件,因此程序清单3中包含了Buy和Sell方法调用。注意,此处只有很少或者没有任何的验证。显而易见,缺点是没有对用户实际拥有覆盖销售的足够数量的共享进行任何验证。这种验证最好添加到应用程序的客户端部分,或者作为大型Web服务的一部分 — 集成了实体bean来进行账户管理和集成了有状态会话bean来处理扩展的服务器会话(很可能用于处理多种股票销售)。应该在实例化bean之前完成验证。
程序清单3
/**
* <p>Invokes the target EJB's <b>buy</b> method and returns the
result.
* Trades are restricted to 500 shares.</p>
*
* @return A String describing the result of the transaction.
*
* @jws:operation
*/
public String buy(String tickerSymbol, int numberOfShares)
{
TradeResult tr;
try
{
tr = trader.buy(tickerSymbol, numberOfShares);
}
catch (RemoteException re)
{
return "Error " + re.getLocalizedMessage();
}
return String.valueOf(tr.getNumberTraded()) + " shares of " +
tr.getStockSymbol() + " bought.";
}
/**
* <p>Invokes the target EJB's <b>sell</b> method and returns the
result.
* Trades are restricted to 500 shares.</p>
*
* @return A String describing the result of the transaction.
*
* @jws:operation
*/
public String sell(String tickerSymbol, int numberOfShares)
{
TradeResult tr;
try
{
tr = trader.sell(tickerSymbol, numberOfShares);
}
catch (RemoteException re)
{
return "Error " + re.getLocalizedMessage();
}
return String.valueOf(tr.getNumberTraded()) + " shares of " +
tr.getStockSymbol() + " sold.";
}
}
消息驱动Bean
消息驱动Bean(MDB)支持开发异步应用程序。主要在服务器上等待Java Message Service(JMS)消息。他们是匿名的,因此没有客户端可以拥有对MDB的引用 — 只有EJB容器可以直接访问bean。他们既没有home接口也没有远程接口。因此,他们不可以在WebLogic Workshop内部使用。但是,有一种替代方法。BEA控件框架中的JMS控件可以添加到Workshop项目中。它可以用于监听回调。图5说明了一个信用报告的JWS项目,使用JMS控件来接收来自队列的信用信息。程序清单4是一份receiveMessage回调处理程序,可以用于处理回调。它在Workshop环境中提供了与MDB一样的基本功能。
程序清单4
/**
* Callback handler for the CreditScoreControl JMS control's
* receiveMessage callback. This handler is invoked when the
* CreditScoreControl invokes its receiveMessage callback.
*/
private void creditScoreJMS_receiveMessage(int score)
throws java.rmi.RemoteException
{
/*
* Add the score received by the credit scoring application to
* the data known about the applicant.
*/
m_currentApplicant.creditScore = score;
/*
* Using the ValidateCreditControl EJB control, request a credit
* rating from an Enterprise Java Bean (EJB). Store the return
* value in the approvalLevel field of the m_currentApplicant
* object.
*/
m_currentApplicant.approvalLevel =
validateCreditEJB.validate(m_currentApplicant.creditScore);
/*
* If the member variable m_callbackURL has value other than
* null, then send a message to the client via the callback
*/
if( m_callbackURL != null )
{
callback.onCreditReportDone(m_currentApplicant, "Credit
score received.");
}
/*
* If the member variable m_callbackURL has the value null,
* then the client cannot accept callbacks.
* Make the message available to the client through the
* synchronous method checkForResults.
*/
else
{
/*
* Mark the investigation as complete by setting the
* isInvestigationComplete field to "true". Clients that
* cannot accept callbacks will call checkForResults to
* poll for investigation results;
* m_isInvestigationComplete is used to determine what
checkForResults should return.
*/
m_isInvestigationComplete = true;
}
}
结束语
依靠分层和已测试代码的任务关键应用程序得益于组件重用。WebLogic Server — 与WebLogic Workshop结合使用 — 可以使这些组件的集成更加轻松。
|