Java组件与其他平台组件的通信
1. EJB 3和Java持久化API概述
起初,在企业级开发中,Java EE的EJB 3和Java持久化API带来了显著的变革。以往开发企业级组件时,开发者需要编写大量的类,并且要配置复杂的部署描述符。而现在,EJB 3和Java持久化API的规范修改,让开发者的工作更加高效。
1.1 EJB 3的改进
- 减少类的编写 :现在EJB/web容器承担了更多的工作,开发者无需像过去那样编写大量类。
- 简化接口需求 : EJB工件不再需要之前必需的home和对象接口,仅需一个业务接口即可。
- 使用注解管理事务 :通过在EJB组件中使用注解,代替了之前的部署描述符配置,使容器能够管理事务。
- 简化O/R映射 :在POJO中引入注解,让开发者可以轻松地将Java对象映射到关系型数据存储,极大地简化了开发和维护工作。
1.2 注解的优势
注解的引入不仅简化了部署描述符的要求,还为开发者提供了更多便利。开发者可以使用注解来定义Web服务、将Java方法映射到操作和组件、将组件映射到XML和数据库持久化机制,以及更高效地指定EJB应用的外部依赖。
2. 组件通信概述
Java是服务器端开发的理想平台,Java EE在服务器应用领域占据主导地位。其核心原则之一是能够将软件系统的各个组件分离并分布到不同的机器上,实现Java对象和组件之间的远程通信。同时,Java EE也定义了外部对象和其他编程语言组件与Java EE组件的通信方式,这种跨平台通信在当今的异构计算环境中至关重要。
2.1 组件的定义
在本文中,组件指的是任何具有网络感知能力的软件对象或对象集合,它们可以向其他组件发送信息或接收信息。例如,Web服务器、企业JavaBean(EJBs)和Web服务都可以被视为组件。
2.2 组件通信的重要性
在分布式软件应用开发中,不同进程中的组件之间需要进行通信。例如,数据库在服务器的一个进程中运行,而读取和写入数据库信息的客户端应用程序在另一个进程(可能在不同的机器上)中运行。因此,必须有一种机制来实现这些进程之间的通信。
2.3 组件通信的层次结构
为了更好地理解组件通信,我们可以将其分为三个主要层次:
| 层次 | 描述 |
| ---- | ---- |
| 应用层 | 代表高级协议,如HTTP或FTP。 |
| 协议层 | 代表运行在IP之上的低级传输协议,如TCP或UDP。 |
| 传输层 | 代表实际的物理传输,如以太网及其相应的数据发送和接收机制。 |
不同的组件要进行通信,必须在应用层使用相同的协议。例如,在Web应用中,HTTP是应用层协议,它决定了许多应用的设计决策。由于HTTP不支持有状态连接,因此必须使用会话cookie或会话标识参数来模拟用户会话的状态。
3. 组件通信场景
以下是一些组件通信的实际场景,这些场景有助于理解不同通信技术在应用架构中的应用。
3.1 新闻阅读器:自动网页浏览
开发一个监控网页的应用程序,它可以每隔十分钟与远程Web服务器通信,检查各种新闻网站是否有新的故事和感兴趣的信息。一旦有符合条件的新故事出现,就会通知用户,从而避免用户不断手动检查和刷新网页。
3.2 银行应用:EJB/Java EE客户端
银行希望升级柜员使用的客户端软件,以访问银行基础设施。柜员的终端运行的是Microsoft Windows 2000,而银行已经有一个基于Java EE的后端来管理所有银行数据。因此,需要开发一个胖客户端来访问服务器上的EJB组件。
3.3 门户:集成异构数据源和服务
许多Web门户(如Yahoo!)需要集成各种数据,如股票行情、体育比分和新闻头条。这些门户的软件设计必须足够灵活,能够集成来自不同位置的不同数据。组件之间的通信对于访问数据库、文件和其他软件应用中的信息至关重要。
4. 进程间通信和基本网络架构
在分布式软件应用开发中,不同进程中的组件之间需要进行通信。这种通信必须遵循一定的协议,协议就像是不同组件之间交流的语言。
4.1 协议的重要性
不同的应用和服务都使用特定的协议进行通信。例如,Web浏览器使用HTTP协议从Web服务器获取网页内容,即时通讯客户端使用特定协议与服务器和其他用户通信,点对点文件共享服务使用协议来搜索和共享文件。
4.2 网络传输的考虑
所有这些应用和协议都可以通过网络进行通信,也可以在同一台机器上的不同进程之间通信。这是因为协议与传输方式是分离的,它们可以在本地运行,也可以通过TCP/IP网络运行。在进行Java组件与其他平台组件的通信时,必须考虑可能的网络传输方式。
4.3 网络通信的层次结构
为了更好地理解组件通信,我们可以将其分为三个主要层次:
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
A([应用层]):::startend --> B([协议层]):::startend
B --> C([传输层]):::startend
- 应用层 :代表高级协议,如HTTP或FTP。
- 协议层 :代表运行在IP之上的低级传输协议,如TCP或UDP。
- 传输层 :代表实际的物理传输,如以太网及其相应的数据发送和接收机制。
不同的组件要进行通信,必须在应用层使用相同的协议。例如,在Web应用中,HTTP是应用层协议,它决定了许多应用的设计决策。由于HTTP不支持有状态连接,因此必须使用会话cookie或会话标识参数来模拟用户会话的状态。
5. 套接字
套接字是操作系统提供的进程间通信的基本机制。虽然在大多数开发项目中,开发者可能不会直接使用套接字,但所有类型的进程间通信都是基于套接字构建的,因此了解套接字的工作原理是很有必要的。
5.1 套接字的类型
套接字主要分为两种类型:
-
用户数据报协议(UDP)
:使用UDP的套接字提供数据报服务,它们接收和发送离散的数据报。UDP是一种无连接协议,没有像TCP那样的连接建立时间,但它不可靠,数据包不能保证按正确的顺序发送或接收。UDP主要用于多媒体流和在线游戏等应用,这些应用不需要所有数据都准确无误。
-
传输控制协议(TCP)
:使用TCP的套接字提供可靠的字节流服务,TCP保证所有发送的数据包都能按正确的顺序接收。TCP是一种面向连接的协议,适用于不能容忍数据丢失的应用,如文件传输、网页浏览或Telnet。
5.2 Java套接字API
Java套接字API是Java网络编程的核心接口,所有核心套接字类都位于
java.net
包中。Java实现了两种类型的套接字:TCP套接字和UDP套接字。此外,Java还提供了UDP多播套接字实现,可以同时向多个客户端发送数据。
5.3 关键类
以下是Java中用于套接字通信的四个主要类:
| 类(来自
java.net
) | 功能 |
| ---- | ---- |
|
Socket
| 用于表示通过TCP连接发送和接收数据的客户端套接字端点。 |
|
DatagramSocket
| 用于通过UDP发送和接收数据的客户端和服务器类。 |
|
ServerSocket
| 用于TCP服务器,当客户端连接时,返回一个
Socket
类来实际发送和接收数据。 |
|
InetSocketAddress
| 表示IP地址(或主机名)和端口号的组合,例如
www.example.com:8080
。 |
5.4 客户端编程
客户端使用
Socket
和
InetSocketAddress
类来连接到运行在另一个进程(远程或本地)中的服务器。以下是客户端编程的具体步骤:
1.
创建地址对象
:定义要连接的服务器和端口。
InetSocketAddress address = new InetSocketAddress("www.example.com", 80);
也可以使用IP地址创建
InetSocketAddress
对象:
InetSocketAddress address = new InetSocketAddress("127.0.0.1", 80);
-
尝试连接
:创建一个新的
Socket类并尝试连接到指定的地址。
Socket socket = new Socket();
try {
socket.connect(address);
} catch (java.io.IOException e) {
e.printStackTrace();
}
-
获取输入和输出流
:如果连接成功,可以使用
InputStream和OutputStream对象进行数据的发送和接收。
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
-
包装流对象
:通常会将
InputStream和OutputStream对象包装在更高级和易用的I/O类中。例如,如果要发送和接收文本数据,可以使用BufferedReader和PrintWriter。
PrintWriter writer = new PrintWriter(out);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
writer.println("Hello, remote computer");
writer.flush();
String serverResponse = br.readLine();
5.5 服务器编程
服务器端使用
ServerSocket
类来监听特定端口上的客户端连接。以下是服务器编程的具体步骤:
1.
创建
ServerSocket
:创建一个
ServerSocket
并准备在指定端口上接受传入的连接。
ServerSocket serverSocket = new ServerSocket(1500);
-
接受连接
:调用
accept()方法等待客户端连接。该方法会阻塞直到有客户端连接,然后返回一个Socket实例,表示与远程进程的连接。
Socket incomingClient = serverSocket.accept();
- 获取输入和输出流 :使用与客户端相同的机制获取输入和输出流,以便进行通信。
InputStream in = incomingClient.getInputStream();
OutputStream out = incomingClient.getOutputStream();
- 处理多个客户端连接 :为了处理多个客户端连接,可以使用线程。以下是一个简单的示例:
boolean conditionToKeepRunning = true;
while (conditionToKeepRunning) {
Socket client = serverSocket.accept();
Thread clientServiceThread = new Thread(new ClassThatImplementsRunnable(client));
clientServiceThread.start();
}
5.6 回声服务器示例
下面是一个简单的回声服务器示例,它会将客户端发送的任何文本原样返回给客户端。
5.6.1
SocketEcho
类
package book;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class SocketEcho implements Runnable {
private Socket socket;
public SocketEcho(Socket socket) {
this.socket = socket;
}
public void run() {
try {
BufferedReader br = new BufferedReader(new
InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream());
// 打印欢迎消息
out.println("Hello, you’ve contacted the Echo Server.");
out.println("\tWhatever you type, I will type back to you...");
out.println("\tPress ‘?’ to close the connection.");
out.println();
out.println();
out.flush();
int currChar = 0;
while ((currChar = br.read()) != -1) {
char c = (char) currChar;
// 如果输入‘?’,关闭连接
if (c == '?')
break;
out.print(c);
out.flush();
}
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
try {
if (socket != null) {
socket.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
public static void main(String[] args) {
// 默认端口
int port = 1500;
// 如果命令行传入了端口号,则使用该端口
if (args.length >= 1) {
try {
port = Integer.parseInt(args[0]);
} catch (NumberFormatException nfe) {
System.out.println("Error: port must be a number -- using 1500 instead.");
}
}
try {
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("Echo Server Running...");
int counter = 0;
while (true) {
Socket client = serverSocket.accept();
System.out.println("Accepted a connection from " +
client.getInetAddress().getHostName());
// 使用多线程处理同时连接
Thread t = new Thread(new SocketEcho(client));
t.setName(client.getInetAddress().getHostName() + ":" + counter++);
t.start(); // 启动新线程并调用SocketEcho.run()
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
5.6.2 运行回声服务器
要启动回声服务器,只需在命令提示符下像运行其他Java应用程序一样运行它:
java book.SocketEcho
服务器启动后,将开始在端口1500(或命令行指定的端口)上接受连接。要连接到客户端,可以使用Telnet:
telnet localhost 1500
通过上述步骤,我们可以实现一个简单的回声服务器,它可以处理多个客户端的连接,并将客户端发送的文本原样返回。在实际开发中,我们可以根据具体需求对服务器进行扩展和优化。
6. 协议理解与实现示例
6.1 协议的重要性
在网络通信中,协议是不同组件之间进行有效通信的关键。它规定了数据的格式、传输方式以及交互的规则。不同的应用场景需要不同的协议来支持,例如HTTP用于网页传输,FTP用于文件传输等。了解协议的特点和限制对于设计网络应用至关重要。
6.2 HTTP协议示例
HTTP是一种应用层协议,广泛用于Web应用中。它是无状态的,即每次请求都是独立的,不保留之前请求的信息。为了模拟用户会话的状态,通常会使用会话cookie或会话标识参数。
以下是一个简单的HTTP请求和响应的流程:
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
A(客户端):::startend -->|发送HTTP请求| B(服务器):::startend
B -->|返回HTTP响应| A
具体步骤如下:
1.
客户端发送请求
:客户端向服务器发送HTTP请求,请求中包含请求方法(如GET、POST等)、请求的资源路径、请求头信息等。
2.
服务器处理请求
:服务器接收到请求后,根据请求的内容进行相应的处理,可能会查询数据库、调用其他服务等。
3.
服务器返回响应
:服务器处理完请求后,返回HTTP响应,响应中包含状态码(如200表示成功,404表示未找到资源等)、响应头信息和响应体(如HTML页面、JSON数据等)。
4.
客户端处理响应
:客户端接收到响应后,根据响应的内容进行相应的处理,如显示网页、解析JSON数据等。
6.3 实现部分HTTP协议
下面是一个简单的示例,展示如何使用Java实现一个基本的HTTP服务器,该服务器可以处理简单的GET请求并返回一个静态的HTML页面:
import java.io.*;
import java.net.*;
public class SimpleHttpServer {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(8080);
System.out.println("HTTP Server running on port 8080...");
while (true) {
Socket clientSocket = serverSocket.accept();
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
// 读取请求行
String requestLine = in.readLine();
System.out.println("Request: " + requestLine);
// 简单处理,假设只处理GET请求
if (requestLine.startsWith("GET")) {
// 返回HTTP响应
out.println("HTTP/1.1 200 OK");
out.println("Content-Type: text/html");
out.println();
out.println("<html><body><h1>Hello, World!</h1></body></html>");
}
// 关闭连接
clientSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
6.4 运行HTTP服务器
要启动这个简单的HTTP服务器,只需在命令提示符下运行以下命令:
java SimpleHttpServer
服务器启动后,将在端口8080上监听客户端的请求。你可以在浏览器中访问
http://localhost:8080
,应该会看到一个显示“Hello, World!”的页面。
7. 远程方法调用(RMI)和公共对象请求代理体系结构(CORBA)
7.1 远程方法调用(RMI)
RMI是Java提供的一种机制,允许一个Java虚拟机(JVM)中的对象调用另一个JVM中的对象的方法。它基于Java的面向对象特性,使得远程调用就像本地调用一样简单。
RMI的工作原理如下:
1.
定义远程接口
:定义一个接口,该接口继承自
java.rmi.Remote
,接口中的方法声明抛出
java.rmi.RemoteException
。
2.
实现远程接口
:实现远程接口的类,该类必须继承自
java.rmi.server.UnicastRemoteObject
。
3.
注册远程对象
:使用
java.rmi.Naming
类将远程对象注册到RMI注册表中。
4.
客户端查找和调用
:客户端使用
java.rmi.Naming
类查找远程对象,并调用其方法。
以下是一个简单的RMI示例:
7.1.1 定义远程接口
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Hello extends Remote {
String sayHello() throws RemoteException;
}
7.1.2 实现远程接口
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class HelloImpl extends UnicastRemoteObject implements Hello {
protected HelloImpl() throws RemoteException {
super();
}
@Override
public String sayHello() throws RemoteException {
return "Hello, RMI!";
}
}
7.1.3 服务器端代码
import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;
public class RMIServer {
public static void main(String[] args) {
try {
// 创建RMI注册表
LocateRegistry.createRegistry(1099);
// 创建远程对象
Hello hello = new HelloImpl();
// 注册远程对象
Naming.rebind("rmi://localhost:1099/Hello", hello);
System.out.println("RMI Server is running...");
} catch (Exception e) {
e.printStackTrace();
}
}
}
7.1.4 客户端代码
import java.rmi.Naming;
public class RMIClient {
public static void main(String[] args) {
try {
// 查找远程对象
Hello hello = (Hello) Naming.lookup("rmi://localhost:1099/Hello");
// 调用远程方法
String result = hello.sayHello();
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
7.2 公共对象请求代理体系结构(CORBA)
CORBA是一种跨语言、跨平台的分布式对象计算标准,允许不同的对象在不同的编程语言和操作系统之间进行通信。它通过对象请求代理(ORB)来实现对象之间的远程调用。
CORBA的工作原理如下:
1.
定义接口描述语言(IDL)
:使用IDL定义对象的接口,包括方法的签名、参数和返回值。
2.
生成代码
:使用IDL编译器生成客户端和服务器端的代码。
3.
实现对象
:在服务器端实现IDL定义的接口。
4.
注册对象
:将实现的对象注册到ORB中。
5.
客户端查找和调用
:客户端通过ORB查找对象并调用其方法。
CORBA的使用相对复杂,需要使用专门的工具和库。以下是一个简单的CORBA示例的步骤:
1.
定义IDL文件
:
module HelloApp {
interface Hello {
string sayHello();
};
};
-
生成代码
:使用IDL编译器(如
idlj)生成Java代码。 - 实现对象 :
import HelloApp.*;
import org.omg.CORBA.*;
import org.omg.PortableServer.*;
import org.omg.PortableServer.POA;
class HelloImpl extends HelloPOA {
@Override
public String sayHello() {
return "Hello, CORBA!";
}
}
- 服务器端代码 :
import HelloApp.*;
import org.omg.CORBA.*;
import org.omg.PortableServer.*;
import org.omg.PortableServer.POA;
public class CORBAServer {
public static void main(String[] args) {
try {
// 初始化ORB
ORB orb = ORB.init(args, null);
// 获取根POA
POA rootPOA = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));
rootPOA.the_POAManager().activate();
// 创建并实现对象
HelloImpl helloImpl = new HelloImpl();
org.omg.CORBA.Object ref = rootPOA.servant_to_reference(helloImpl);
Hello href = HelloHelper.narrow(ref);
// 打印对象引用
String ior = orb.object_to_string(href);
System.out.println(ior);
// 等待请求
orb.run();
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 客户端代码 :
import HelloApp.*;
import org.omg.CORBA.*;
public class CORBAClient {
public static void main(String[] args) {
try {
// 初始化ORB
ORB orb = ORB.init(args, null);
// 获取对象引用
org.omg.CORBA.Object objRef = orb.string_to_object(args[0]);
Hello href = HelloHelper.narrow(objRef);
// 调用方法
String result = href.sayHello();
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
8. Web服务
8.1 Web服务概述
Web服务是一种基于网络的、可互操作的分布式应用程序。它使用标准的Web协议(如HTTP、XML等)进行通信,允许不同的应用程序在不同的平台和编程语言之间进行交互。Web服务具有松耦合、可重用等优点,逐渐成为跨平台组件通信的首选技术。
8.2 JDK 6中的Web服务
JDK 6引入了对Web服务的支持,提供了简单易用的API来开发和部署Web服务。以下是一个简单的Web服务示例:
8.2.1 定义Web服务接口
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService
public interface HelloWebService {
@WebMethod
String sayHello(String name);
}
8.2.2 实现Web服务接口
import javax.jws.WebService;
@WebService(endpointInterface = "HelloWebService")
public class HelloWebServiceImpl implements HelloWebService {
@Override
public String sayHello(String name) {
return "Hello, " + name + "!";
}
}
8.2.3 发布Web服务
import javax.xml.ws.Endpoint;
public class WebServicePublisher {
public static void main(String[] args) {
HelloWebService helloWebService = new HelloWebServiceImpl();
String url = "http://localhost:8080/HelloWebService";
Endpoint.publish(url, helloWebService);
System.out.println("Web Service published at: " + url);
}
}
8.2.4 调用Web服务
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
public class WebServiceClient {
public static void main(String[] args) {
try {
URL wsdlURL = new URL("http://localhost:8080/HelloWebService?wsdl");
QName serviceName = new QName("http://impl/", "HelloWebServiceImplService");
Service service = Service.create(wsdlURL, serviceName);
HelloWebService helloWebService = service.getPort(HelloWebService.class);
String result = helloWebService.sayHello("World");
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
8.3 Web服务的优势
- 跨平台和跨语言 :Web服务使用标准的Web协议和数据格式(如XML),可以在不同的平台和编程语言之间进行交互。
- 松耦合 :服务提供者和服务消费者之间是松耦合的,服务的实现可以独立于调用者进行修改和升级。
- 可重用 :Web服务可以被多个应用程序共享和重用,提高了开发效率和代码的可维护性。
9. 总结
在网络通信和分布式应用开发中,选择合适的通信技术至关重要。不同的技术适用于不同的场景,例如:
| 技术 | 适用场景 |
| ---- | ---- |
| 套接字 | 适用于底层的网络通信,对于需要自定义协议或对性能要求较高的场景较为合适。 |
| HTTP协议 | 广泛用于Web应用中,适用于无状态的请求和响应场景。 |
| RMI | 适用于Java应用程序之间的远程方法调用,基于Java的面向对象特性,使用方便。 |
| CORBA | 适用于跨语言、跨平台的分布式对象计算,需要使用专门的工具和库,使用相对复杂。 |
| Web服务 | 适用于跨平台、跨语言的组件通信,具有松耦合、可重用等优点,逐渐成为首选技术。 |
在实际开发中,我们需要根据具体的需求和场景选择合适的技术,并结合多线程、错误处理等技术来提高应用的性能和可靠性。同时,随着技术的不断发展,新的通信技术和框架也在不断涌现,我们需要不断学习和掌握新的知识,以适应不断变化的开发需求。
超级会员免费看

被折叠的 条评论
为什么被折叠?



