@Remote 與 @Local


當 客戶端要使用Session Bean,而客戶端位於容器以外的JVM時,則必須定義其服務介面為遠端介面,即使用@Remote標示,容器會在部署之後,在Bytecode上加入繼 承java.rmi.Remote的動作,你也可以直接自行繼承java.rmi.Remote,例如:
@Remote
public interface HelloBean extends java.rmi.Remote {
    ....
}

不在程式上直接撰寫繼承java.rmi.Remote的好處之一,是不需要撰寫丟出java.rmi.RemoteException,由於實際上 Bean的取得會是透過RMI機制,因此你的方法呼叫若有參數或傳回值,它們都必須實作java.io.Serializable介面。

由於@Remote底層會使用RMI,所以會有RMI呼叫的負擔,若可以的話,應避免使用@Remote而使用@Local,當客戶端使用Session Bean,而客戶端與Session Bean是位於同一個容器(同一個JVM)時,則可以標示Session Bean為@Local。

一個Session Bean的服務介面不能同時是遠端及本地介面,也就是不能同時標示為@Remote及@Local,即以下是不允許的:
@Local
@Remote
public interface HelloBean {
    ....
}

若要讓一個Session Bean實作同時是遠端及本地,則你要分別定義遠端介面及本地介面,例如:
@Local
public interface HelloBeanLocal {
    String doHello(String message);
    ....
}

@Remote
public interface HelloBeanRemote {
    String doHello(String message);
    ....
}

然後讓Session Bean同時實作這兩個介面,例如:
@Stateless(name="ejb/HelloFacade")
public class HelloBeanImpl implements HelloBeanLocal, HelloBeanRemote {
    public String doHello(String message) {
        return message + "processed....";
    }
}

本地介面取用時可以使用HelloBeanLocal介面,例如:
@EJB
private HelloBeanLocal hello;

而遠端介面取用時可以使用HelloBeanRemote介面:
InitialContext context = new InitialContext();
Object obj = context.lookup("onlyfun.caterpillar.HelloBeanRemote");
HelloBeanRemote hello = (HelloBeanRemote)
                  PortableRemoteObject.narrow(obj, HelloBeanRemote.class);

您也可以在定義介面時,不使用@Local、@Remote,例如:
public interface HelloBeanLocal {
    String doHello(String message);
    ....
}

public interface HelloBeanRemote {
    String doHello(String message);
    ....
}

而在實作Session Bean時再加以指定@Local、@Remote:
@Local({HelloBeanLocal.class})
@Remote({HelloBeanRemote.class})
@Stateless(name="ejb/HelloFacade")
public class HelloBeanImpl implements HelloBean {
    public String doHello(String message) {
        return message + "processed....";
    }
}