EJB 工作原理
编者注:本文为历史博文归档;涉及 JDK、框架与工具链版本请以当前官方文档为准。引用外链图片可能失效,阅读时请注意时效性。
引言
日前在论坛精华区查阅到 Robbin 关于 EJB 调用原理的分析文章,受益匪浅。不过原文主要以文字阐述为主,直观性稍显不足,且对 RMI(Remote Method Invocation,远程方法调用)的讲解篇幅有限。鉴于 EJB 底层基于 RMI 实现,本文将在原帖基础上,结合个人体会补充一些图解与代码示例,供大家参考。
RMI 工作原理
既然 EJB 基于 RMI,我们首先需理清 RMI 的工作机制。简而言之,RMI 的本质是实现不同 JVM(Java Virtual Machine,Java 虚拟机)之间的方法调用。其工作原理如下图所示:

实现的核心在于分别在两个 JVM 中创建 Stub(客户端桩)和 Skeleton(服务端骨架),二者通过 Socket 通信来完成参数与返回值的传递。
代码示例
网上关于 RMI 的示例代码大多通过继承 java.rmi.Remote 接口实现,封装较为完善,但也容易让人产生“雾里看花”的感觉,难以窥见底层细节。以下示例参考自《Enterprise JavaBeans》一书,虽然实现较为粗糙,但胜在直观,有助于快速理解其工作原理。
1. 定义业务接口
首先定义一个 Person 接口,包含两个业务方法:getAge() 和 getName()。
public interface Person {
public int getAge() throws Throwable;
public String getName() throws Throwable;
}2. 服务端实现
编写 Person 接口的服务端实现类 PersonServer。
public class PersonServer implements Person {
int age;
String name;
public PersonServer(String name, int age) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
}3. 客户端 Stub 实现
若要在 Client 端调用上述业务方法,需编写相应的 Stub(客户端)和 Skeleton(服务端)程序。以下是 Stub 的实现逻辑:
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.net.Socket;
public class Person_Stub implements Person {
Socket socket;
public Person_Stub() throws Throwable {
// 连接 Skeleton
socket = new Socket("computer_name", 9000);
}
public int getAge() throws Throwable {
// 向 Skeleton 传递方法名
ObjectOutputStream outStream =
new ObjectOutputStream(socket.getOutputStream());
outStream.writeObject("age");
outStream.flush();
ObjectInputStream inStream =
new ObjectInputStream(socket.getInputStream());
return inStream.readInt();
}
public String getName() throws Throwable {
// 向 Skeleton 传递方法名
ObjectOutputStream outStream =
new ObjectOutputStream(socket.getOutputStream());
outStream.writeObject("name");
outStream.flush();
ObjectInputStream inStream =
new ObjectInputStream(socket.getInputStream());
return (String) inStream.readObject();
}
}注意,Person_Stub 与 PersonServer 一样,都实现了 Person 接口。区别在于:PersonServer 是真实的业务逻辑实现,而 Person_Stub 负责建立 Socket 连接,向 Skeleton 发送请求,并通过 Skeleton 调用 PersonServer 的方法,最后接收返回结果。
4. 服务端 Skeleton 实现
Skeleton 类继承自 Thread,长驻后台运行,随时接收 Client 发来的请求,并根据请求键值调用相应的业务方法。
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.net.Socket;
import java.net.ServerSocket;
public class Person_Skeleton extends Thread {
PersonServer myServer;
public Person_Skeleton(PersonServer server) {
// 获取服务对象引用
this.myServer = server;
}
public void run() {
try {
// 在 9000 端口新建 Socket
ServerSocket serverSocket = new ServerSocket(9000);
// 接受 Stub 的请求
Socket socket = serverSocket.accept();
while (socket != null) {
// 获取 Stub 的请求
ObjectInputStream inStream =
new ObjectInputStream(socket.getInputStream());
String method = (String) inStream.readObject();
// 检查方法名
if (method.equals("age")) {
// 执行服务端的业务方法
int age = myServer.getAge();
ObjectOutputStream outStream =
new ObjectOutputStream(socket.getOutputStream());
// 返回结果给 Stub
outStream.writeInt(age);
outStream.flush();
}
if (method.equals("name")) {
// 执行服务端的业务方法
String name = myServer.getName();
ObjectOutputStream outStream =
new ObjectOutputStream(socket.getOutputStream());
// 返回结果给 Stub
outStream.writeObject(name);
outStream.flush();
}
}
} catch (Throwable t) {
t.printStackTrace();
System.exit(0);
}
}
public static void main(String[] args) {
// 新建服务对象
PersonServer person = new PersonServer("Richard", 34);
Person_Skeleton skel = new Person_Skeleton(person);
skel.start();
}
}5. 客户端调用实现
最后是实现 Client 端的调用逻辑。
public class PersonClient {
public static void main(String[] args) {
try {
Person person = new Person_Stub();
int age = person.getAge();
String name = person.getName();
System.out.println(name + " is " + age + " years old");
} catch (Throwable t) {
t.printStackTrace();
}
}
}Client 的本质是知晓 Person 接口的定义,并实例化一个 Person_Stub,通过 Stub 调用业务方法。至于 Stub 如何与 Server 沟通,Client 无需关心。
请注意实例化的写法:
Person person = new Person_Stub();而不是:
Person_Stub person = new Person_Stub();原因在于遵循面向接口编程的原则。
结语
感谢您耐心阅读至此。关于 RMI 的核心机制,本文就先介绍到这里。原本计划接着深入探讨 EJB 的具体工作原理,但篇幅所限,今日先到这里,后续再继续补充。
说明:本文内容基于早期 JDK 版本(JDK 1.4/5 时代)及 EJB 2.x 规范编写。现代 JDK(Java 5+)已不再需要手动生成 Stub/Skeleton 代码,RMI 机制已高度自动化;同时 EJB 3.0 及以上版本已采用注解驱动,与文中涉及的底层机制有较大差异。阅读时请注意技术栈的时效性。
版权声明:本文为原创文章,版权归 戴老师的博客 所有,转载请联系博主获得授权。
本文地址:https://1diff.fun/archives/ejb-gong-zuo-yuan-li.html
如果对本文有什么问题或疑问都可以在评论区留言,我看到后会尽量解答。