J2EE从零开始之EJB(2)

发表于:2007-04-29来源:作者:点击数: 标签:ejb开始j2ee
本节演示如何 开发 和 测试 一个无状态会话EJB。本节提供的实例演示了一个简化的股票交易操作,示意图如图4-4所示。 从上图看出,无状态EJB:TraderBean由多个客户端共享使用,不保存客户端的状态。 通过这个实例,读者可以知道: ·如何定义无状态EJB的主接
本节演示如何开发测试一个无状态会话EJB。本节提供的实例演示了一个简化的股票交易操作,示意图如图4-4所示。
   从上图看出,无状态EJB:TraderBean由多个客户端共享使用,不保存客户端的状态。
   通过这个实例,读者可以知道:
   ·如何定义无状态EJB的主接口、远程接口和如何编写EJB实现类
   ·如何使用对象作为远程方法的返回值
   ·如何在ejb-jar.xml中定义和在无状态EJB中通过会话上下文使用环境参数
   本例中包括服务器端程序和客户端程序,把服务器端程序放在C:\bea\wlserver6.0\config\mydomain\applications\DefaultWebApp_myserver\WEB-INF\classes目录下,如果没有classes这个目录,请手工创建;把客户端程序放在一个目录下,这里用C:\bea\wlserver6.0\config\mydomain\clientclasses。如果没有子目录clientclasses,则创建它,如果不创建这两个目录,本实例将不能正常运行。

4.3.2 编写源文件

  这个例子的代码包括两个接口、三个类。我们把它们都定义在包examples.ejb.basic.statelessSession中,因此创建了目录:
   C:\work\examples\ejb\basic\statelessSession
   该实例使用到的文件列表和描述如表4-1所示。
          表4-1 无状态会话EJB文件列表
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
   名称      类名      文件名       所在包
   ─────────────────────────────────
   主接口     TraderHome   TraderHome.java  examples.ejb.basic.statelessSession
   远程接口    Trader     Trader.java    examples.ejb.basic.statelessSession
   远程方法返回类 TraderResult  TraderResult.java examples.ejb.basic.statelessSession
   bean实现类            TraderBean.java examples.ejb.basic.statelessSession
   客户端测试类  Client     Client.java    examples.ejb.basic.statelessSession
   bean说明文件          ejb-jar.xml
   bean部署文件          weblogic-ejb-jar.xml
   ─────────────────────────────────
   1.主接口程序
   无状态Session Bean的主接口定义比较简单。它本身不需维持自身的状态,所以不需要个性化的创建方法,即create方法是不带有参数的。
   无状态Session Bean的主接口定义比较简单。它本身不需维持自身的状态,所以不需要个性化的创建方法,即create方法是不带有参数的。
   编辑TraderHome.java并保存到C:\work\src\examples\ejb\basic\statelessSession目录下(或从附带光盘的src\examples\ejb\basic\statelessSession目录拷贝),其源文件如下:

  //定义本接口在包examples.ejb.basic.statelessSession中
   package examples.ejb.basic.statelessSession;
   //本接口用到的其他
   //javax.ejb.EJBHome定义了EJB主接口,被用户定义的主接口继承
   import java.rmi.RemoteException;
   import javax.ejb.CreateException;
   import javax.ejb.EJBHome;

  /**
   * 这是TradeBean的主接口定义,这个接口是被EJB容器产生的类TraderBeanC实现的。
   * 在这里只需定义EJB创建的方法,这些方法要和EJBean中的"ejbCreate"方法对应。
   */
   //EJBean主接口必须继承javax.ejb.EJBHome接口
   public interface TraderHome extends EJBHome {

  /**
   * 这个方法和"TraderBean.java"中定义的的Bean的"ejbCreate"方法相对应
   * 这两个方法的参数应该相同。当客户端调用"TraderHome.create()"方法时,EJB容器
   * 会找到EJBean的实例,并调用它的"ejbCreate()"方法。
   *
   * @返回 远程对象Trader
   * @异常 RemoteException 当系统通讯发生故障时抛出
   * @异常 CreateException 创建EJBean错误时抛出
   * @参看 examples.ejb.basic.statelessSession.TraderBean
   */
   Trader create() throws CreateException, RemoteException;
   }

  2. 远程接口程序
   在远程接口中定义业务逻辑方法,这个接口中定义了两个业务逻辑方法,buy和sell,分别对应股票交易中的买进和卖出。
   编辑文件Trader.java并保存到C:\work\src\examples\ejb\basic\statelessSession目录下(或从附带光盘的src\examples\ejb\basic\statelessSession目录拷贝),其源文件如下:

  //文件名:Trader.java
   //定义本接口在包examples.ejb.basic.statelessSession中
   package examples.ejb.basic.statelessSession;
   //本接口用到的其他类
   //javax.ejb.*中定义了实现EJBean的接口。
   import java.rmi.RemoteException;
   import javax.ejb.EJBObject;

  /**
   * 这是TradeBean的远程接口定义。远程接口中定义了客户端能远程调用EJBean的方法。这些方法除了
   * 要抛出异常java.rmi.RemoteException之外,和EJBean中的定义是一致的。但并不是EJBean来实
   * 现这个接口,而是由容器自动产生的类TraderBeanE实现的。
   */
   //这个接口必须继承javax.ejb.EJBObject接口
   public interface Trader extends EJBObject {

   /**
    * 远程方法:购买指定股票类别、数量的股票。
    *
    * @参数 stockSymbol 股票代码
    * @参数 shares 购买股票的数量
    * @返回 TradeResult 交易结果对象
    * @异常 RemoteException 当系统通讯发生故障时抛出
    */
   public TradeResult buy (String stockSymbol, int shares)
    throws RemoteException;

   /**
    * 远程方法:出售指定股票类别、数量的股票。
    *
    * @参数 stockSymbol 股票代码
    * @参数 shares 出售股票的数量
    * @返回 TradeResult Trade Result
    * @异常 RemoteException 当系统通讯发生故障时抛出
    */
    public TradeResult sell (String stockSymbol, int shares)
     throws RemoteException;
   }

  3. 远程方法返回结果类
   这个例子演示了在EJB远程调用方法返回中如何使用对象。在远程方法sell和buy定义中我们盾到返回值是一个TradeResult对象。对于这样的类定义,需要注意的是它必须实现java.io.Serializable接口。这是因为远程方法返回对象要在不同主机(或虚拟机)之间的网络流之间传递,而在流之间传递的对象必须要实现Serializable接口,否则会抛出异常。
   编辑文件TradeResult.java并保存到C:\work\src\examples\ejb\basic\statelessSession目录下(或从附带光盘的src\examples\ejb\basic\statelessSession目录下拷贝),其源文件如下:

  //定义本类在包examples.ejb.basic.statelessSession
   package examples.ejb.basic.statelessSession;

  import java.io.Serializable;

  /**
   * 这个类代表买/卖股票的结果
   */
   //远程方法使用的类必须实现Serializable接口
   public final class TradeResult implements Serializable {

  // 真实的买卖数量
   private final int numberTraded;
   //股票代号
   private final String stockSymbol;
   //构造交易结果对象
   public TradeResult(int nt, String ss) {
    numberTraded = nt;
    stockSymbol = ss;
   }
   //get方法
   public int getNumberTraded() { return numberTraded; }
   public String getStockSymbol() { return stockSymbol; }
   }

  4. Bean实现类
   Bean类用来实现在远程接口中定义的业务逻辑方法,这里简单实现了buy和sell方法。
   这个例子演了EJB如何猎取环境参数的方法。作为无状态会话Bean来说,虽然不能在客户端创建的时候指定参数,但可以通过在部署描述符中定义环境参数来给无状态Bean赋值。在实现时可以用下下文InitialContext的lookup()方法猎取参数值。
   同时在这个例子中也演示了怎样使用用户定义的异常。
   编辑文件TraderBean.java并保存到C:\work\src\examples\ejb\basic\statelessSession目录下(或从附带光盘的src\examples\ejb\basic\statelessSession目录拷贝),其源文件如下:

  //定义本类在包examples.ejb.basic.statelessSession中
   package examples.ejb.basic.statelessSession;
   //本类用到的其他类。javax.ejb.*是开发EJB应用需要的类库。javax.naming.*是实现JNDI服务需要的类库。
   import javax.ejb.CreateException;
   import javax.ejb.SessionBean;
   import javax.ejb.SessionContext;
   import javax.naming.InitialContext;
   import javax.naming.NamingException;

  /**
   * TraderBean 是一个无状态EJB,它演示了如何:
   * 在会话过程中不保持用户的状态
   * 用户定义异常
   */
   //这个类是会话Bean,因此必须实现接口 SessionBean
   public class TraderBean implements SessionBean {
    //设置是否打印控制台
    private static final boolean VERBOSE = true;
    //声明会话上下文变量
    private SessionContext ctx;
    //设计交易限制变量
    private int tradeLimit;

   // 使用日志记录
    private void log(String s) {
    if (VERBOSE) System.out.println(s);
   }

   /**
   * 这是本类必须实现的方法,在本例中没有用到
   *
   */
   public void ejbActivate() {
    log("ejbActivate called");
   }

  /**
   * 这是本类必须实现的方法,在本例中没有用到
   *
   */
   public void ejbRemove() {
    log("ejbRemove called");
   }

  /**
   * 这是本类必须实现的方法,在本例中没有用到
   *
   */
   public void ejbPassivate() {
    log("ejbPassivate called");
   }

  /**
   * 设置会话上下文变量
   *
   * @参数 ctx SessionContext Context for session
   */
   public void setSessionContext(SessionContext ctx) {
    log("setSessionContext called");
    this.ctx = ctx;
   }

  /**
   * 这个方法与"TraderHome.java"中定义的主接口中的"create"方法相对应
   * 两个方法的参数相同。当客户端调用主接口的"TraderHome.create()"方法时,
   * 容器会分配一个EJBean实例,并调用它的"ejbCreate()"方法。
   *
   * @异常 javax.ejb.CreateException 创建EJBean出错时抛出的异常
   * @参看 examples.ejb.basic.statefulSession.Trader
   */
   public void ejbCreate () throws CreateException {
    log("ejbCreate called");

   try {
     //初始化JNDI名称服务上下文
     InitialContext ic = new InitialContext();
     //从环境变量上下文中查找交易限制参数值
     Integer tl = (Integer) ic.lookup("java:/comp/env/tradeLimit");

     tradeLimit = tl.intValue();
    } catch (NamingException ne) {
    throw new CreateException("Failed to find environment value "+ne);
   }
   }

  /**
   * 购买指定用户和股票类别、数量的股票。
   *
   * @参数 stockSymbol 股票代码
   * @参数 shares 购买股票的数量
   * @返回 TradeResult 交易结果对象
   */
   public TradeResult buy(String stockSymbol, int shares) {

  if (shares > tradeLimit) {
   log("Attempt to buy "+shares+" is greater than limit of "+tradeLimit);
   shares = tradeLimit;
   }

  log("Buying "+shares+" shares of "+stockSymbol);
   //返回交易结果对象
   return new TradeResult(shares, stockSymbol);
   }

  /**
   * 出售指定用户和股票类别、数量的股票。
   *
   * @参数 stockSymbol 股票代码
   * @参数 shares 出售股票的数量
   * @返回 TradeResult Trade Result
   */
   public TradeResult sell(String stockSymbol, int shares) {

  if (shares > tradeLimit) {
   log("Attempt to sell "+shares+" is greater than limit of "+tradeLimit);
   shares = tradeLimit;
   }

  log("Selling "+shares+" shares of "+stockSymbol);
   //返回交易结果对象
   return new TradeResult(shares, stockSymbol);
   }
   }

  5. 编辑部署文件
   会话EJB使用两个部署说明文件,ejb-jar.xml用来描述EJB的结构、类型、环境参数等。weblogic-ejb-jar.xml用来描述与部署相关的信息。
   编辑ejb-jar.xml和weblogic-ejb-jar.xml并保存到C:\work\src\examples\ejb\basic\statelessSession目录下(或从附带光盘的src\examples\ejb\basic\statelessSession目录拷贝),其源文件如下:

  <?xml version="1.0"?>
   <!DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN' 'http://java.sun.com/j2ee/dtds/ejb-jar_1_1.dtd'>

  <ejb-jar>
   <small-icon>images/green-cube.gif</small-icon>
   <enterprise-beans>
   <session>
   <small-icon>images/orange-cube.gif</small-icon>
   <ejb-name>statelessSession</ejb-name>
   <home>examples.ejb.basic.statelessSession.TraderHome</home>
   <remote>examples.ejb.basic.statelessSession.Trader</remote>
   <ejb-class>examples.ejb.basic.statelessSession.TraderBean</ejb-class>
   <session-type>Stateless</session-type>
   <transaction-type>Container</transaction-type>
   <env-entry>
   <env-entry-name>WEBL</env-entry-name>
   <env-entry-type>java.lang.Double </env-entry-type>
   <env-entry-value>10.0</env-entry-value>
   </env-entry>
   <env-entry>
   <env-entry-name>INTL</env-entry-name>
   <env-entry-type>java.lang.Double </env-entry-type>
   <env-entry-value>15.0</env-entry-value>
   </env-entry>
   <env-entry>
   <env-entry-name>tradeLimit</env-entry-name>
   <env-entry-type>java.lang.Integer </env-entry-type>
   <env-entry-value>500</env-entry-value>
   </env-entry>
   </session>
   </enterprise-beans>
   <assembly-descriptor>
   <container-transaction>
   <method>
   <ejb-name>statelessSession</ejb-name>
   <method-intf>Remote</method-intf>
   <method-name>*</method-name>
   </method>
   <trans-attribute>Required</trans-attribute>
   </container-transaction>
   </assembly-descriptor>
   </ejb-jar>

  ejb-jar.xml可以分为以下几个部分:
   <ejb-name>:定义这个EJB的名字,这里命名为statelessSession。
   <home>:指定主接口,这里指定为examples.ejb.basic.statelessSession.TraderHome。
   <remote>:指定远程接口,这里指定为examples.ejb.basic.statelessSession.Trader。
   <ejb-class>:指定EJB类,这里指定为examples.ejb.basic.statelessSession.TraderBean。
   <session-type>:定义会话类型,这里定义为Stateless,无状态。
   <env-entry>:定义环境参数名和值,可以通过EJB类中上下文对象的lookup方法获取。
   weblogic-ejb-jar.xml的源文件如下:

  <?xml version="1.0"?>

  <!DOCTYPE weblogic-ejb-jar PUBLIC '-//BEA Systems, Inc.//DTD WebLogic 5.1.0 EJB//EN' 'http://www.bea.com/servers/wls510/dtd/weblogic-ejb-jar.dtd'>

  <weblogic-ejb-jar>
   <weblogic-enterprise-bean>
   <ejb-name>statelessSession</ejb-name>
   <caching-descriptor>
   <max-beans-in-free-pool>100</max-beans-in-free-pool>
   </caching-descriptor>
   <jndi-name>statelessSession.TraderHome</jndi-name>
   </weblogic-enterprise-bean>
   </weblogic-ejb-jar>

  weblogic-ejb-jar.xml文件主要定义了与EJB部署相关的信息,主要有:
   <ejb-name>:EJB的名字,这里是statelessSession
   <caching-descriptor>:EJB缓冲池描述,这里定义了自由池的EJB实例数。
   注意 Weblogic Server中,为了提高无状态EJB的访问效率,采用了自由池的技术,所谓自由池技术就是在自由池中为同一个无状态EJB类实例化多个对象(指定最大数),来处理多用户折情况,提高效率,而在客户端看来,却是透明的,和访问同一个实例进行的处理方法一样。
   6. 客户端测试程序代码编写
   编辑client.java文件并保存到C:\work\src\examples\ejb\basic\statelessSession目录下(也可以直接从配套光盘的类似路径下拷贝),其源文件的代码如下:

  //定义本类在包examples.ejb.basic.statelessSession中
   package examples.ejb.basic.statelessSession;
   //本类用到的其他类
   import java.rmi.RemoteException;
   import java.util.Properties;

  import javax.ejb.CreateException;
   import javax.ejb.RemoveException;
   import javax.naming.Context;
   import javax.naming.InitialContext;
   import javax.naming.NamingException;
   import javax.rmi.PortableRemoteObject;

  /**
   * 这个类演示了如何调用一个会话,并进行如下操作:
   * 创建一个Trader远程对象
   * 使用Trader远程对象购买一定数量的股票
   * 出售一定数量的股票
   * 清除Trader远程对象
   */

  public class Client {
   //定义JNDI服务中EJB的名称
   private static final String JNDI_NAME = "statelessSession.TraderHome";
   //声明应用服务器的URL变量
   private String url;
   //声明主接口变量
   private TraderHome home;

  public Client(String url)
   throws NamingException
   {

  this.url = url;
   //寻找主接口
   home = lookupHome();
   }

  /**
   * 在命令行运行这个实例:
   * java examples.ejb.basic.statefulSession.Client "t3://localhost:7001"
   * 参数是可选的
   */
   public static void main(String[] args) throws Exception {

  log("\nBeginning statelessSession.Client...\n");
   //声明应用服务器地址变量
   String url = "t3://localhost:7001";
   // Parse the argument list
   if (args.length != 1) {
   //打印用法提示
   System.out.println("Usage: java examples.ejb.basic.statelessSession.Client t3://hostname:port");
   return;
   } else {
   url = args[0];
   }

  Client client = null;
   try {
   //创建客户端实例
   client = new Client(url);
   } catch (NamingException ne) {
   System.exit(1);
   }

  try {
   //调用测试方法
   client.example();
   } catch (Exception e) {
   //异常处理
   log("There was an exception while creating and using the Trader.");
   log("This indicates that there was a problem communicating with the server: "+e);
   }

  log("\nEnd statelessSession.Client...\n");
   }

  /**
   * 运行实例
   */
   public void example()
   throws CreateException, RemoteException, RemoveException
   {

   // 创建远程对象
   log("Creating a trader");
   Trader trader = (Trader) narrow(home.create(), Trader.class);

  String [] stocks = {"BEAS", "MSFT", "AMZN", "HWP" };

  // 执行一系列购买操作
   for (int i=0; i<stocks.length; i++) {
   int shares = (i+1) * 100;
   log("Buying "+shares+" shares of "+stocks[i]+".");
   trader.buy(stocks[i], shares);
   }

  // 执行一系列出售操作
   for (int i=0; i<stocks.length; i++) {
   int shares = (i+1) * 100;
   log("Selling "+shares+" shares of "+stocks[i]+".");
   trader.sell(stocks[i], shares);
   }

  // 清除远程对象
   log("Removing the trader");
   trader.remove();

  }

  /**
   * 使用RMI/IIOP的narrow方法
   */
   private Object narrow(Object ref, Class c) {
   return PortableRemoteObject.narrow(ref, c);
   }

  /**
   * 在JNDI树中寻找EJB主接口
   */
   private TraderHome lookupHome()
   throws NamingException
   {
   // 使用JNDI寻找EJB
   Context ctx = getInitialContext();

   try {
   Object home = ctx.lookup(JNDI_NAME);
   return (TraderHome) narrow(home, TraderHome.class);
   } catch (NamingException ne) {
   //异常处理
   log("The client was unable to lookup the EJBHome. Please make sure ");
   log("that you have deployed the ejb with the JNDI name "+JNDI_NAME+" on the WebLogic server at "+url);
   throw ne;
   }
   }

  /**
   * 使用属性对象获取JNDI服务上下文
   */
   private Context getInitialContext() throws NamingException {

   try {
   // 声明属性对象
   Properties h = new Properties();
   h.put(Context.INITIAL_CONTEXT_FACTORY,
   "weblogic.jndi.WLInitialContextFactory");
   h.put(Context.PROVIDER_URL, url);
   //返回的特定属性的上下文对象
   return new InitialContext(h);
   } catch (NamingException ne) {
   //异常处理
   log("We were unable to get a connection to the WebLogic server at "+url);
   log("Please make sure that the server is running.");
   throw ne;
   }
   }

  /**
   * 可以使用jndi.properties属性文件
   *
   */
   // private static Context getInitialContext()
   // throws NamingException
   // {
   // return new InitialContext();
   // }
   //打印控制台输出
   private static void log(String s) {
   System.out.println(s);
   }
   }

4.3.3 编译与打包

  1. 编译代码
   ·打开命令窗口,进入c:\work\src\examples\ebj\basic\statelessSession目录。
   ·运行设置环境变量命令脚本setEnv.cmd:
   c:\work\src\examples\ejb\basic\statelessSession>c:\work\setEnv
   setEnv.cmd在附带光盘的work目录下,可以直接把它拷贝到c:\work\目录。
   ·编译好的代码要放在build目录下,因此在编译之前要创建build目录:
   c:\work\src\examples\ebj\basic\statelessSession、>md build
   ·打开命令行窗口,进行c:\work\src\examples\ebj\basic\statelessSession目录,运行:
   c:\work\src\examples\ebj\basic\statelessSession>javac -d build Trader.java TraderHome.java TradeResult.java TraderBean.java
   其中 -d build 表示编译后的字节码文件放在build子目录下。
   这个命令执行的结果是在build目录下生成examples目录。
   2. 打包
   打包之前,拷贝必要的文件到规定的目录。继续使用上面的步骤及命令,运行如下命令:
   c:\work\src\examples\ejb\basic\statelessSession>mkdir build
   c:\work\src\examples\ejb\basic\statelessSession>mkdir build\META-INF
   c:\work\src\examples\ejb\basic\statelessSession>mkdir build\images
   c:\work\src\examples\ejb\basic\statelessSession>copy *.xml build\META-INF
   c:\work\src\examples\ejb\basic\statelessSession>copy *.gif build\images
   用jar命令打包,包括三部分文件,分别在build目录的三个子目录下:
   ·META-INF:xml部署文件
   ·examples:类文件
   ·images:图像文件
   ·在命令窗口中继续运行如下命令:
   c:\work\src\examples\ejb\basic\statelessSession>cd build
   c:\work\src\examples\ejb\basic\statelessSession>jar cv0f std_ejb_basic_statelessSession.jar META-INF examples images
   这个命令执行的结果是在build目录下生成std_ejb_basic_statelessSession.jar文件。
   3.编译容器代码
   这是与应用程序服务器平台相关的一个操作。WebLogic Server 6.0自带了一个容器代码%


原文转自:http://www.ltesting.net

评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)
...

热门标签