因为涉及到网络传送,所以对象肯定是序列化传送的,但是我们在ejb3.0中自己定义的会话bean等却没有实现可序列化接口,为什么呢,
这是因为,ejb采用的是rmi(Remote Method Invocation)实现的,因为rmi已经封装好传递的细节,所以我们来看一下rmi的工作原理这样我们
就可以大概了解ejb的分布式调用原理了,RMI的本质就是实现在不同JVM之间的调用,它的实现方法就是在两个JVM中各开一个Stub和Skeleton,二者通过socket通信来实现参数和返回值的传递。
jmi使用小实例:
接口:
package senssic.ejb.imitate;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface JmiDemoBean extends Remote {
public String sayHello() throws RemoteException;
}
实现类:
package senssic.ejb.imitate;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class JmiDemoBeanImpl extends UnicastRemoteObject implements JmiDemoBean {
//必须继承UnicastRemoteObject类,不然报错,因为UnicastRemoteObject 实现了底层细节,需要继承
// 因为UnicastRemoteObject的构造方法抛出了RemoteException异常,因此这里默认的构造方法必须写,必须声明抛出RemoteException异常
protected JmiDemoBeanImpl() throws RemoteException {
super();
// TODO Auto-generated constructor stub
}
@Override
public String sayHello() throws RemoteException {
return "你好世界";
}
}
服务端:
package senssic.ejb.imitate;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
public class JmiDemoSer {
/**
* @param args
*/
public static void main(String[] args) {
try {
// 创建一个远程对象
JmiDemoBean jmiImpl = new JmiDemoBeanImpl();
// 本地主机上的远程对象注册表Registry的实例,并指定端口为8888,缺少注册表创建,则无法绑定对象到远程注册表上
LocateRegistry.createRegistry(8888);
// 把远程对象注册到RMI注册服务器上,并命名为jmiImpl
Naming.bind("rmi://localhost:8888/RHello", jmiImpl);
System.out.println("远程IHello对象绑定成功!");
} catch (RemoteException e) {
System.out.println("创建远程对象发生异常!");
e.printStackTrace();
} catch (java.rmi.AlreadyBoundException e) {
System.out.println("发生重复绑定对象异常!");
e.printStackTrace();
} catch (MalformedURLException e) {
System.out.println("发生URL畸形异常!");
e.printStackTrace();
}
}
}
客户端:
package senssic.ejb.imitate;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
public class JmiDemoClent {
/**
* @param args
*/
public static void main(String[] args) {
try {
// 在RMI服务注册表中查找名称为JmiDemoBean的对象,并调用其上的方法
JmiDemoBean jBean = (JmiDemoBean) Naming
.lookup("rmi://localhost:8888/RHello");
System.out.println(jBean.sayHello());
} catch (NotBoundException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
以上jmi的分布式远程调用实例完毕下面我们来看看原理吧
首先我们看下实现序列化的对象通过socket传递的原理:
bean代码:
package senssic.ejb.imitate;
import java.io.Serializable;
//因为是整个序列化,必须实现序列化接口,否则会报错
public class EjbBean implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
服务端:
package senssic.ejb.imitate;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class EjbSockSer extends Thread {
private final EjbBean eBean;
private final Socket socket;
public EjbSockSer(EjbBean eBean, Socket socket) {
this.eBean = eBean;
this.socket = socket;
}
@Override
public void run() {
try {
while (socket != null) {
ObjectOutputStream oStream = new ObjectOutputStream(
socket.getOutputStream());
oStream.writeObject(eBean);
System.out.println("发给老婆的对象完成。。。");
oStream.flush();
oStream.close();
}
} catch (Exception e) {
}
}
public static void main(String[] args) throws Exception {
EjbBean eBean = new EjbBean();
eBean.setAge(20);
eBean.setName("senssic");
ServerSocket serSocket = new ServerSocket(9000);
System.out.println("今天女朋友生气了,哎,启动女朋友服务!");
while (true) {
System.out.println("启动一个爱老婆线程………………");
Socket socket = serSocket.accept();
System.out.println("爱老婆服务完成。");
EjbSockSer eSer = new EjbSockSer(eBean, socket);
eSer.start();
}
}
}
客户端:
package senssic.ejb.imitate;
import java.io.ObjectInputStream;
import java.net.Socket;
public class EjbSockClent {
public static void main(String[] args) throws Exception {
Socket socket = new Socket("127.0.0.1", 9000);
ObjectInputStream inputStream = new ObjectInputStream(
socket.getInputStream());
EjbBean eBean = (EjbBean) inputStream.readObject();
System.out.println("老婆接受到对象---对象姓名:" + eBean.getName() + "对象年龄:"
+ eBean.getAge());
}
}
好了,既然是ejb3.0不需要实现序列化接口,那么他是怎么做的呢,rmi是怎么做的呢,我们来看看rmi的原理吧
接口:
package senssic.ejb.imitate;
public interface EjbSenssicBean {
public String getName() throws Throwable;
public int getAge() throws Throwable;
}
实现类:
package senssic.ejb.imitate;
public class EjbSenssicBeanImpl implements EjbSenssicBean {
private final String name;
private final int age;
public EjbSenssicBeanImpl(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String getName() {
return name;
}
@Override
public int getAge() {
return age;
}
}
服务端:
package senssic.ejb.imitate;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class EjbSenssicSer extends Thread {
private final EjbSenssicBean eBean;
private final Socket socket;
public EjbSenssicSer(EjbSenssicBean eBean, Socket socket) {
this.eBean = eBean;
this.socket = socket;
}
@Override
public void run() {
try {
while (socket != null) {
ObjectInputStream iStream = new ObjectInputStream(
socket.getInputStream());
String method = (String) iStream.readObject();
if (method.equals("age")) {
ObjectOutputStream oStream = new ObjectOutputStream(
socket.getOutputStream());
try {
oStream.writeInt(eBean.getAge());
} catch (Throwable e) {
e.printStackTrace();
}
oStream.flush();
}
if (method.equals("name")) {
ObjectOutputStream oStream = new ObjectOutputStream(
socket.getOutputStream());
try {
oStream.writeObject(eBean.getName());
} catch (Throwable e) {
e.printStackTrace();
}
oStream.flush();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
ServerSocket serSocket = new ServerSocket(9000);
EjbSenssicBean eBean = new EjbSenssicBeanImpl("senssic", 20);
while (true) {
Socket socket = serSocket.accept();
EjbSenssicSer ejbSenssicSer = new EjbSenssicSer(eBean, socket);
ejbSenssicSer.start();
}
}
}
客户端:
package senssic.ejb.imitate;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
public class EjbSenssicClent implements EjbSenssicBean {
private Socket socket;
public EjbSenssicClent() {
try {
socket = new Socket("127.0.0.1", 9000);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public String getName() throws Throwable {
ObjectOutputStream oStream = new ObjectOutputStream(
socket.getOutputStream());
oStream.writeObject("name");
ObjectInputStream InputStream = new ObjectInputStream(
socket.getInputStream());
String name = (String) InputStream.readObject();
oStream.flush();
return name;
}
@Override
public int getAge() throws Throwable {
ObjectOutputStream oStream = new ObjectOutputStream(
socket.getOutputStream());
oStream.writeObject("age");
ObjectInputStream InputStream = new ObjectInputStream(
socket.getInputStream());
int age = InputStream.readInt();
oStream.flush();
return age;
}
public static void main(String[] args) throws Throwable {
EjbSenssicBean eBean = new EjbSenssicClent();
System.out.println("远方的名字:" + eBean.getName() + "远方的年龄:"
+ eBean.getAge());
}
}
这样我们不用实现序列化接口也能获得远端实例好的对象了