日期:2014-05-20  浏览次数:20752 次

EJB3 透明处理远程和本地调用的问题
———问题提出
 EJB3提出了4中接口类型包括:远程接口(remote interface)、本地接口(local interface)端点接口(endpoint interface)和消息接口(message interface),本文主要议论前两种接口。
 〇远程接口定义了session bean(具体接口的实现类)的业务方法,这些方法可以被来自EJB容器以外的应用访问到。它是个普通的Java接口。被标注了@java.ejb.Remoter注解;
 〇本地接口同样定义了session bean得业务方法,这些方法可以被处于同一EJB容器的其他bean使用:也就是bean提供给运行于同一JVM中其他bean的业务方法。被标注了@javax.ejb.Local注解;
 session bean的客户端端从来不于session bean class直接打交道,相应的,客户端必须始终使用session bean的组件接口所提供的方法来完成工作。尽管本地接口不涉及分布式对象协议,但他们人就为bean class提供了一个代理或存根。
 在实际工作中我们的业务为了支持多种情况(远程或本地访问),我们是否可以让接口同时标注@java.ejb.Remoter和@javax.ejb.Local让其支持两种访问方式,使之在服务器端透明。在Client端我们定义一个全局常量(targetServicerLocation)来标注目前client和Server是否在同一jvm中。然后再到Clent端通过targetServicerLocation的值进行分支处理。不就可以完成在服务器端或者在Client端都透明了吗?
———服务器端透明的处理
以《EnterPrise JavaBean 3.0》中的Tiain系统为假想业务,来探索我们的处理。
Tiain系统是一个船运系统。既然是个船运系统,必然有实体Cabin(船舱),我们就这一实体的CRUD展开议论。
先定义Cabin实体

 

import javax.persistence.* ;

@Entity
@Table(name="CABIN")
public class Cabin {
  private int id;
  private String name;
  private int deckLevel;

  @Id
  @GeneratedValue
  @Column(name="CABIN_ID")
  public int getId( ) { return id; }

  public void setId(int pk) { this.id = pk; }

  @Column(name="CABIN_NAME")
  public String getName( ) { return name; }

  public void setName(String str) { this.name = str; }

  @Column(name="CABIN_DECK_LEVEL")
  public int getDeckLevel( ) { return deckLevel; }

  public void setDeckLevel(int level) { this.deckLevel = level; }
}



业务逻辑的定义

public interface TravelAgent {
/**创建船舱*/
  public void createCabin(Cabin cabin);
  /**获得船舱*/
  public Cabin findCabin(int id);
}

为了让其支持远程和本地调用我们为之标注@java.ejb.Remoter和@javax.ejb.Local变成这样;

@Remote
@Local
public interface TravelAgent {
/**创建船舱*/
  public void createCabin(Cabin cabin);
  /**获得船舱*/
  public Cabin findCabin(int id);
}

接口实现类

package com.titan.travelagent;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import com.titan.domain.Cabin;

@Stateless
public class TravelAgentBean implements TravelAgent{
  @PersistenceContext 
(unitName="titan")
private EntityManager manager;

  public void createCabin(Cabin cabin) {
  manager.persist(cabin);
  }

  public Cabin findCabin(int pKey) {
  return manager.find(Cabin.class, pKey);
  }
}

ok,到目前为止开来一切都非常顺利。我们定义了一个接口,并且标注@Remote和@Local。这样我们就可以通过远程和本地方法调用了。
部署测试!
结果大大的异常
java.lang.RuntimeException: An exception occurred initialising interceptors for class com.titan.travelagent.TravelAgentBean.equals
想想就明白了,如果可以这么搞EJB3规范早就这么搞了,还要我在这费心思!但是要同时实现远程接口和本地接口的需求实在很迫切。难道写两个接口仅仅是名字不同和标注的注解不同吗?我们可不可以将公用的代码提出来。
改造如下
1、将TravelAgent的@Remote和@Local去掉,加入如下两个接口

package com.titan.travelagent;
import javax.ejb.Remote;
@Remote
public interface TravelAgentRemote extends TravelAgent{}


 

package com.titan.travelagent;
import javax.ejb.Local;
@Local
public interface TravelAgentLocal extends TravelAgent{}

改造TravelAgentBean时之实现这两个接口

package com.titan.travelagent;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import com.titan.domain.Cabin;
@Stateless
public class TravelAgentBean implements TravelAgentLocal,TravelAgentRemote{
  @PersistenceContext