深入理解Servlet间通信:原理、方法与实践
立即解锁
发布时间: 2025-08-18 02:12:17 阅读量: 1 订阅数: 2 

### 深入理解 Servlet 间通信:原理、方法与实践
#### 1. 引言
在服务器环境中,多个 Servlet 常常需要相互通信以实现复杂的功能。这种 Servlet 间的通信主要有三个目的:直接的 Servlet 操作、Servlet 复用以及 Servlet 协作。下面将详细介绍这三种通信方式及其实现方法。
#### 2. Servlet 操作
##### 2.1 获取 Servlet 信息
Servlet 可以通过 `ServletContext` 对象获取服务器上已加载的其他 Servlet 的信息。可以使用 `getServlet()` 方法获取特定名称的 Servlet:
```java
public Servlet ServletContext.getServlet (String name) throws ServletException
```
该方法返回指定名称的 Servlet,如果未找到则返回 `null`。指定的名称可以是 Servlet 的注册名称或类名。例如:
```java
Servlet servlet = context.getServlet("MyServlet");
```
也可以使用 `getServlets()` 方法获取所有已加载的 Servlet,但该方法在 Servlet API 2.0 中已被弃用,推荐使用 `getServletNames()` 方法:
```java
public Enumeration ServletContext.getServletNames()
```
该方法返回当前 `ServletContext` 中已加载的 Servlet 对象名称的枚举。
##### 2.2 避免 `ClassCastException`
在将 `getServlet()` 或 `getServlets()` 返回的 `Servlet` 对象强制转换为其特定子类时,可能会抛出 `ClassCastException`。这是因为 Servlet 类文件更改时会自动重新加载,使用了不同的 `ClassLoader`。可以通过以下三种方法解决:
1. **使用反射**:避免对返回的 `Servlet` 对象进行强制类型转换,而是使用反射调用其方法。
2. **防止 Servlet 重新加载**:将需要进行类型转换的 Servlet 从默认的 Servlet 目录移到服务器的标准类路径中。
3. **使用接口**:将返回的 Servlet 强制转换为声明了相关方法的接口,并将该接口放在服务器的标准类路径中。
##### 2.3 查看当前加载的 Servlet
以下是一个示例代码,用于显示当前加载的 Servlet 的信息:
```java
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class Loaded extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/plain");
PrintWriter out = res.getWriter();
ServletContext context = getServletContext();
Enumeration names = context.getServletNames();
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
Servlet servlet = context.getServlet(name);
out.println("Servlet name: " + name);
out.println("Servlet class: " + servlet.getClass().getName());
out.println("Servlet info: " + servlet.getServletInfo());
out.println();
}
}
}
```
##### 2.4 保存当前加载的 Servlet 的状态
以下示例代码尝试调用每个 Servlet 的 `saveState()` 方法(如果存在),以防止服务器崩溃时的数据丢失:
```java
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class SaveState extends HttpServlet {
public void doGet(HttpServletRequest re, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/plain");
PrintWriter out = res.getWriter();
ServletContext context = getServletContext();
Enumeration names = context.getServletNames();
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
Servlet servlet = context.getServlet(name);
out.println("Trying to save the state of " + name + "...");
out.flush();
try {
Method save = servlet.getClass().getMethod("saveState", null);
save.invoke(servlet, null);
out.println("Saved!");
} catch (NoSuchMethodException e) {
out.println("Not saved. This servlet has no saveState() method.");
} catch (SecurityException e) {
out.println("Not saved. SecurityException: " + e.getMessage());
} catch (InvocationTargetException e) {
out.print("Not saved. The saveState() method threw an exception: ");
Throwable t = e.getTargetException();
out.println(t.getClass().getName() + ": " + t.getMessage());
} catch (Exception e) {
out.println("Not saved. " + e.getClass().getName() + ": " + e.getMessage());
}
out.println();
}
}
public String getServletInfo() {
return "Calls the saveState() method (if it exists) for all the currently loaded servlets";
}
}
```
#### 3. Servlet 复用
##### 3.1 复用的挑战
一个 Servlet 可以复用另一个 Servlet 的能力(公共方法),但主要挑战是在被复用的 Servlet 尚未加载到服务器时,“用户” Servlet 如何获取其正确的实例。`getServlet()` 方法不能保证在所有服务器上加载指定名称的 Servlet,直接创建新实例也不可行,因为新创建的 Servlet 无法访问其 `ServletConfig` 和 `ServletContext` 对象。
##### 3.2 解决方案
可以通过以下方法解决:用户 Servlet 向服务器发送 HTTP 请求,请求未加载的 Servlet,从而强制服务器加载该 Servlet,然后调用 `getServlet()` 方法获取其正确实例。`com.oreilly.servlet.ServletUtils` 类提供了一个改进的 `getServlet()` 方法:
```java
public static Servlet getServlet(String name,
ServletRequest req,
ServletContext context) {
try {
// 尝试以传统方式获取 Servlet
Servlet servlet = context.getServlet(name);
if (servlet != null) return servlet;
// 如果未获取到,则通过 HTTP 请求加载
Socket socket = new Socket(req.getServerName(), req.getServerPort());
socket.setSoTimeout(4000);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
out.println("GET /servlet/" + name + " HTTP/1.0");
out.println();
try {
socket.getInputStream().read();
} catch (InterruptedIOException e) {
// 超时:忽略,尽力而为
}
out.close();
// 再次尝试获取 Servlet
return context.getServlet(name);
} catch (Exception e) {
return null;
}
}
```
##### 3.3 复用 `ChatServlet`
以 `ChatServlet` 为例,创建一个名为 `ChatPage` 的 Servlet 来复用 `ChatServlet` 的功能:
```java
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import com.oreilly.servlet.ServletUtils;
public class ChatPage extends HttpServlet implements Runnable {
static final int MESSAGE_ARCHIVE_SIZE = 10;
ChatServlet chat = null;
String[] messages = new String[MESSAGE_ARCHIVE_SIZE];
int messageIndex = 0;
Thread update = null;
public void run() {
while (true) {
String message = chat.getNextMessage();
synchronized (this) {
messages[messageIndex] = message;
messageIndex = (messageIndex + 1) % MESSAGE_ARCHIVE_SIZE;
}
}
}
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html");
PrintWriter out = res.getWriter();
res.setHeader("Pragma", "no-cache");
if (chat == null) {
chat = (ChatServlet) ServletUtils.getServlet("ChatServlet", req, getServletContext());
if (chat != null) {
update = new Thread(this);
update.start();
}
}
// 打印页面内容
out.println("<HTML><HEAD>");
out.println("<TITLE>ChatPage</TITLE>");
out.println("</HEAD><BODY>");
out.println("<CENTER><H1>Welcome to ChatPage!</H1></CENTER>");
synchronized (this) {
out.println("<FONT SIZE=4>Recent messages:</FONT><P>");
int i = messageIndex;
do {
String message = messages[i];
if (message != null) out.println(message + "<P>");
i = (i + 1) % MESSAGE_ARCHIVE_SIZE;
} while (i != messageIndex);
}
out.println("<FORM METHOD=GET>");
out.println("<INPUT TYPE=submit VALUE=\"Get New Messages\">");
out.println("</FORM>");
out.println("<HR>");
out.println("<FORM METHOD=POST>");
out.println("<FONT SIZE=4>Submit a message:</FONT>");
out.println("<INPUT TYPE=text NAME=message>");
out.println("</FORM>");
out.println("<HR>");
out.println("<CENTER><FONT SIZE=2><B>");
out.println("Special thanks to ChatServlet for acting as our back-end");
out.println("</B></FONT></CENTER>");
out.println("</BODY></HTML>");
}
public void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
if (chat == null) {
chat = (ChatServlet) ServletUtils.getServlet("ChatServlet", req, getServletContext());
if (chat != null) {
update = new Thread(this);
update.start();
Thread.currentThread().yield();
}
}
String user = req.getRemoteUser();
if (user == null) user = "anonymous";
String message = req.getParameter("message"
```
0
0
复制全文
相关推荐










