在当前Java开发中,Web占据很大空间, 10个Java程序员就至少有6个是搞Web方面开发的,但不管是C/S还是B/S, 软件最终的目的只有一个,那就是对各种服务的集成. 在软件技术发展到今天,EIS的集成出现了2大主流,即SUN的J2EE(JavaEE)方案和MS的.NET方案,他们要作的都是将不同的服务进行集成后统一接口暴露给客户端,比如在J2EE里,实现对分布环境下服务的集成---RMI等,传统的CORBA等,以及.NET的remoting等,当然我们这里只讨论Java的集成方案.
在J2EE集成中,集成大体分为2中,第一是Java系统之间的集成,另外一个是Java与其他语言开发的信息系统之间的集成.下面分开讨论.
第一: Java系统之间的集成: 该情况下集成比较简单,只要双方提供服务接口即可,比如服务方提供RMI远程接口或者直接以EJB的形式把接口暴露给客户端,在这中情况下,最核心的部分就是RMI,应为EJB也是架设在RMI基础之上的一个规范,下面简要介绍RMI的数据流原理以及给出一个开发实例:
RMI开发分为2个部分,一个是服务器端开发,一个是客户端开发,服务器端提供远程业务接口以及服务实现,而客户端需要提供和其想匹配的远程业务接口和Stub存根,在此需要特别说明的是,客户端的远程接口和服务器端的远程接口一定要具有相通的SerialUID,否则他们是无法通信的,在这里我详悉说明下Java对象在网络上传输的原理:
如果一个对象需要在网络上传输,它一定是一个可序列化的,这个我想几乎所有Java程序员都知道,实现java.io.Serializable接口即可,但通信双方,一边将该对象发出去,另外一边就必需用该类进行接收,我们要作的就是分别把这个dto打2个jar包,一个放到服务器端,一个放到客户端 ,这样它们就能通信, 这里我们作一个实验,你第一次编译的时候(javac),把生成的class打到jar包里,然后再javac一次,把class打包到另外一个 jar包里,分别把这2个jar包放到客户端和服务器端, 运行程序,你会发现会出现序列号不一致的文体, 读者肯定会问这是为什么? 原因就是这2个类不一样,应为根据java规范,可许列化的类,必需提供一个private static final long serialUID 这个Field, 我们可以在程序里 显式 设置,如果不显式 设置, javac在编译源代码的时候会自动生成一个放到class文件里 (不信你javap下看看),正是由于2次javac产生的随机serialUID不一致,所以才造成了刚刚上面的哪个错误出现,现在解决的办法有2个,第一是 只javac一次,把同一个 class打2 次包, 分别给客户端和服务端,第2是显式指明serialUID在代码里,这样即时javac N次也不要紧. }
现在继续讲RMI , 当用户调用客户端远程接口的时候,比如:Naming.lookup("rmi://192.168.0,1//serviceName")的时候,JVM会察觉到这是一个RMI请求,这时它会把这个路由信息(192.168.0.1/serviceName)通知给Stub,而Stub就负责向远程服务器的RMI 中央注册机发送请求,中央注册机调用服务通过这个服务名子(serviceName)来找到注册进来的对象实例,这时候调用这个实例来处理,完了返回值按原路逆向返回,接收端进行反序列话, (注: 序列化和反序列化的任务都是由 2 端的Stub和框架完成的).这样一个远程调用过程就完成了,下面代码说明:
A. 创建一个远程接口: Mclaren.java
package org.mclaren.remote;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Mclaren extends Remote {
public String sayHello(String name) throws RemoteException;
}
B. 创建一个服务实现: MclarenService.java
package org.mclaren.remote;
import java.rmi.server.UnicastRemoteObject;
import java.rmi.RemoteException;
import java.rmi.Naming;
public class MclarenService extends UnicastRemoteObject implements Mclaren {
public MclarenService() throws RemoteException {
super();
}
public void registerService(String serviceName) {
try {
//绑定名称后注册到中央注册机
Naming.rebind(name,this);
System.out.println("服务已经起动");
} catch(Exception e) {
e.printStackTrace();
}
}
public String sayHello(String name) throws RemoteException {
return "Hello :"+name+" this is processed by RMI service":
}
}
C. 编写服务起起动类: StartUp.java
package org.mclaren.remote;
import java.rmi.RMISecuriryManager;
public class StartUp {
public static void main(String[] args) {
try {
System.setSecurityManager(new RMISecurityManager());
MclarenService mclaren = new MclarenService();
mclaren.registerService("//MclarenService");
} catch(Exception e) {
e.printStackTrace();
}
}
}
D. 编写安全策略文件 mclaren.policy
grant {
Permission java.security.AllPermission ("192.168.0.2,192.168.0.3","");
};
注解: 这段话的意思是 允许 192.168.0.2,192.168.0.3这2个机器的任意端口使用本服务作任何事情,当然你可以改 .
E. 为服务作准备: 打开cmd 或者 终端(Linux),到JDK_HOME/bin 下执行 rmiregistry明令起动RMI中央注册机 ( rmiregistry的语法是: rmiregistry <端口> ,默认为1099).
到刚哪个工程的classes下,执行明令: rmic org.mclaren.remote.MclarenService 来生成客户端 Stub,然后把stub和远程接口一起打包成client.jar文件:
F. 起动服务: 假设: 我们工程的类输出目录在: F:/RMITest/classes下,而mclaren.policy 在F:/RMITest下 .那么在cmd里执行:
java -classpath F:/RMITest/classes org.mclaren.remote.StartUp -Djava.rmi.server.codebase=file:///F://RMITest//classes/ -Djava.security.policy=file:///F://RMITest//mclaren.policy
完了后会发现输出: 服务已经起动
现在开发客户端: 把client.jar导进来
public class Client {
public static void main(String[] args) {
try {
Mclaren mclaren = (Mclaren) Naming.lookup("rmi://192.168.0.1:1099//MclarenService");
mclaren.sayhello("mclaren");
} catch(Exception e) {
e.printStackTrace();
}
}
}
执行后会 出现 : Hello : mclaren this is processed by RMI service"
对于EJB就没这么复杂了,大家自给看书,下面将Java如何与C/C++进行集成.
A. Java调用C++: Java 调用C++,就得用JNI接口,关于JNI的概念我就不多废话了,自己去so,(在国内别的不好着,找概念的东西还是一找一堆). 下面举例子:
(1) 首先创建一个Java文件: Mclaren.java
package org.mclaren.jni;
public class Mclaren {
static {
System.loadLibrary("Mclaren'); // load Dll or so
}
public native void sayHello(String name);
}
(2) 生成头文件: 在classes目录下输入: javah org.mclaren.jni.Mclaren 会生成一个C的头文件 org_mclaren_jni_Mclaren.h
(3) 新建一个VC工程,把 %JAVA_HOME%/include 和 %JAVA_HOME%/include/win32添加到include里,或者直接把jni.h 和jni_md.h copy过来也可以 ,然后新建一个cpp文件 Mclaren.cpp(我采用C++写 ) ,这样你VC工程目录下将含有4个文件: jni.h, jni_md.h. org_mclaren_jni_Mclaren.h, Mclaren.cpp
其中Mclaren.cpp内容为:
#include <iostream>
#include "org_mclaren_jni_Mclaren.h"
using namespace std;
JNIEXPORT JNICALL org_mclaren_jni_Mclaren_SayHello(
JNIEnv * env, jobject obj, jstring name) {
char *str = (char *) name;
cout<<"C++输出 :"<<str<<endl;
delete str;
}
将上面的工程打包成一个dll后放到硬盘上任意地方,假设D://lib//Mclaren.dll, 我们只要把这个dll放到程序的运行时 librarypath 里就可以 .
下面编写测试类:
public class Test {
public static void main(String[] args) {
Mclaren mclaren = new Mclaren();
mclaren.sayHello("Mclaren");
}
}
假设这个工程的 classes目录是: F://JNIJ2C/classes
那么: java -classpath F://JNIJ2C/classes Test -Djava.library.path=D://lib//Mclaren.dll
这时会输出: C++输出 Mclaren.
B. C++ 调用Java: C++调用Java的时候,就需要用C++来起动Java虚拟机,来装载类,下面我分别在Windows环境下和Linux环境下讲述:
(1) Windows环境下:
a. 新建一个C++工程(不需要支持MFC), 把JAVA_HOME/include/jni.h JAVA_HOME/include/win32下的 jni_md.h copy 到C++工程目录下,然后把 JAVA_HOME/jre/bin/client/jvm.dll 写进系统PATH ,再把JAVA_HOME/lib/jvm.h 添加到VC的工程lib里.
b. 新建Mclaren.cpp ,内容如下:
#include "jni.h"
#include <iostream>
using namespace std;
int main() {
JNIEnv *env;
JavaVM *jvm;
JavaVMOption options[3];
JavaVMInitArgs args;
memset(&args,0,sizeof(&args),)
args.version = JNI_VERION_1_4;
args.nOptions = 3;
args.option = options;
args.ignoreUnrecognized = JNI_TRUE;
options[0] = "-Djava.class.path=.";
options[1] = "-Djava.library.path=.";
options[2] = "-verbose:jni";
int ret = JNI_CreateJavaVM(&jvm,(void **)&env,&args);
if(ret < 0) {
cout<<"起动JVM 失败"<<endl;
return -1;
}
//我们加载上面的RMI Client类
jclass cla = env->FindClass("org/mclaren/remote/Client");
//取得 main方法, 第3个参数是Java签名
jmethodID init = env->GetStaticMethodID(cla,"main","([-Ljava.lang.String)V");
env->CallStaticMethod(cla,init,NULL);
jvm->DestroyJavaVM();
return 0;
}
VC编译后,执行. 取得的效果和用java.exe起动是一样
(2) Linux环境下 :
a. 代码不变,因为上面用的是标准C++ 库
b. 设置好环境变量JAVA_HOME, CLASSPATH和 PATH
c. 在 .bash_profile里追加一条:
export LD_LOBRARY_PATH=$JAVA_HOME/jre/lib/i386/client
到Mclaren.cpp所在的目录下输入命令 :
g++ -I $JAVA_HOME/include -I $JAVA_HOME/include/linux -L $LD_LIBRARY_PATH -o Mclaren Mclaren.cpp -ljvm
注意" -ljvm是小写的L
这样就会产生一个Mclaren的可执行文件,执行一下,效果同上.
在J2EE集成中,集成大体分为2中,第一是Java系统之间的集成,另外一个是Java与其他语言开发的信息系统之间的集成.下面分开讨论.
第一: Java系统之间的集成: 该情况下集成比较简单,只要双方提供服务接口即可,比如服务方提供RMI远程接口或者直接以EJB的形式把接口暴露给客户端,在这中情况下,最核心的部分就是RMI,应为EJB也是架设在RMI基础之上的一个规范,下面简要介绍RMI的数据流原理以及给出一个开发实例:
RMI开发分为2个部分,一个是服务器端开发,一个是客户端开发,服务器端提供远程业务接口以及服务实现,而客户端需要提供和其想匹配的远程业务接口和Stub存根,在此需要特别说明的是,客户端的远程接口和服务器端的远程接口一定要具有相通的SerialUID,否则他们是无法通信的,在这里我详悉说明下Java对象在网络上传输的原理:
如果一个对象需要在网络上传输,它一定是一个可序列化的,这个我想几乎所有Java程序员都知道,实现java.io.Serializable接口即可,但通信双方,一边将该对象发出去,另外一边就必需用该类进行接收,我们要作的就是分别把这个dto打2个jar包,一个放到服务器端,一个放到客户端 ,这样它们就能通信, 这里我们作一个实验,你第一次编译的时候(javac),把生成的class打到jar包里,然后再javac一次,把class打包到另外一个 jar包里,分别把这2个jar包放到客户端和服务器端, 运行程序,你会发现会出现序列号不一致的文体, 读者肯定会问这是为什么? 原因就是这2个类不一样,应为根据java规范,可许列化的类,必需提供一个private static final long serialUID 这个Field, 我们可以在程序里 显式 设置,如果不显式 设置, javac在编译源代码的时候会自动生成一个放到class文件里 (不信你javap下看看),正是由于2次javac产生的随机serialUID不一致,所以才造成了刚刚上面的哪个错误出现,现在解决的办法有2个,第一是 只javac一次,把同一个 class打2 次包, 分别给客户端和服务端,第2是显式指明serialUID在代码里,这样即时javac N次也不要紧. }
现在继续讲RMI , 当用户调用客户端远程接口的时候,比如:Naming.lookup("rmi://192.168.0,1//serviceName")的时候,JVM会察觉到这是一个RMI请求,这时它会把这个路由信息(192.168.0.1/serviceName)通知给Stub,而Stub就负责向远程服务器的RMI 中央注册机发送请求,中央注册机调用服务通过这个服务名子(serviceName)来找到注册进来的对象实例,这时候调用这个实例来处理,完了返回值按原路逆向返回,接收端进行反序列话, (注: 序列化和反序列化的任务都是由 2 端的Stub和框架完成的).这样一个远程调用过程就完成了,下面代码说明:
A. 创建一个远程接口: Mclaren.java
package org.mclaren.remote;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Mclaren extends Remote {
public String sayHello(String name) throws RemoteException;
}
B. 创建一个服务实现: MclarenService.java
package org.mclaren.remote;
import java.rmi.server.UnicastRemoteObject;
import java.rmi.RemoteException;
import java.rmi.Naming;
public class MclarenService extends UnicastRemoteObject implements Mclaren {
public MclarenService() throws RemoteException {
super();
}
public void registerService(String serviceName) {
try {
//绑定名称后注册到中央注册机
Naming.rebind(name,this);
System.out.println("服务已经起动");
} catch(Exception e) {
e.printStackTrace();
}
}
public String sayHello(String name) throws RemoteException {
return "Hello :"+name+" this is processed by RMI service":
}
}
C. 编写服务起起动类: StartUp.java
package org.mclaren.remote;
import java.rmi.RMISecuriryManager;
public class StartUp {
public static void main(String[] args) {
try {
System.setSecurityManager(new RMISecurityManager());
MclarenService mclaren = new MclarenService();
mclaren.registerService("//MclarenService");
} catch(Exception e) {
e.printStackTrace();
}
}
}
D. 编写安全策略文件 mclaren.policy
grant {
Permission java.security.AllPermission ("192.168.0.2,192.168.0.3","");
};
注解: 这段话的意思是 允许 192.168.0.2,192.168.0.3这2个机器的任意端口使用本服务作任何事情,当然你可以改 .
E. 为服务作准备: 打开cmd 或者 终端(Linux),到JDK_HOME/bin 下执行 rmiregistry明令起动RMI中央注册机 ( rmiregistry的语法是: rmiregistry <端口> ,默认为1099).
到刚哪个工程的classes下,执行明令: rmic org.mclaren.remote.MclarenService 来生成客户端 Stub,然后把stub和远程接口一起打包成client.jar文件:
F. 起动服务: 假设: 我们工程的类输出目录在: F:/RMITest/classes下,而mclaren.policy 在F:/RMITest下 .那么在cmd里执行:
java -classpath F:/RMITest/classes org.mclaren.remote.StartUp -Djava.rmi.server.codebase=file:///F://RMITest//classes/ -Djava.security.policy=file:///F://RMITest//mclaren.policy
完了后会发现输出: 服务已经起动
现在开发客户端: 把client.jar导进来
public class Client {
public static void main(String[] args) {
try {
Mclaren mclaren = (Mclaren) Naming.lookup("rmi://192.168.0.1:1099//MclarenService");
mclaren.sayhello("mclaren");
} catch(Exception e) {
e.printStackTrace();
}
}
}
执行后会 出现 : Hello : mclaren this is processed by RMI service"
对于EJB就没这么复杂了,大家自给看书,下面将Java如何与C/C++进行集成.
A. Java调用C++: Java 调用C++,就得用JNI接口,关于JNI的概念我就不多废话了,自己去so,(在国内别的不好着,找概念的东西还是一找一堆). 下面举例子:
(1) 首先创建一个Java文件: Mclaren.java
package org.mclaren.jni;
public class Mclaren {
static {
System.loadLibrary("Mclaren'); // load Dll or so
}
public native void sayHello(String name);
}
(2) 生成头文件: 在classes目录下输入: javah org.mclaren.jni.Mclaren 会生成一个C的头文件 org_mclaren_jni_Mclaren.h
(3) 新建一个VC工程,把 %JAVA_HOME%/include 和 %JAVA_HOME%/include/win32添加到include里,或者直接把jni.h 和jni_md.h copy过来也可以 ,然后新建一个cpp文件 Mclaren.cpp(我采用C++写 ) ,这样你VC工程目录下将含有4个文件: jni.h, jni_md.h. org_mclaren_jni_Mclaren.h, Mclaren.cpp
其中Mclaren.cpp内容为:
#include <iostream>
#include "org_mclaren_jni_Mclaren.h"
using namespace std;
JNIEXPORT JNICALL org_mclaren_jni_Mclaren_SayHello(
JNIEnv * env, jobject obj, jstring name) {
char *str = (char *) name;
cout<<"C++输出 :"<<str<<endl;
delete str;
}
将上面的工程打包成一个dll后放到硬盘上任意地方,假设D://lib//Mclaren.dll, 我们只要把这个dll放到程序的运行时 librarypath 里就可以 .
下面编写测试类:
public class Test {
public static void main(String[] args) {
Mclaren mclaren = new Mclaren();
mclaren.sayHello("Mclaren");
}
}
假设这个工程的 classes目录是: F://JNIJ2C/classes
那么: java -classpath F://JNIJ2C/classes Test -Djava.library.path=D://lib//Mclaren.dll
这时会输出: C++输出 Mclaren.
B. C++ 调用Java: C++调用Java的时候,就需要用C++来起动Java虚拟机,来装载类,下面我分别在Windows环境下和Linux环境下讲述:
(1) Windows环境下:
a. 新建一个C++工程(不需要支持MFC), 把JAVA_HOME/include/jni.h JAVA_HOME/include/win32下的 jni_md.h copy 到C++工程目录下,然后把 JAVA_HOME/jre/bin/client/jvm.dll 写进系统PATH ,再把JAVA_HOME/lib/jvm.h 添加到VC的工程lib里.
b. 新建Mclaren.cpp ,内容如下:
#include "jni.h"
#include <iostream>
using namespace std;
int main() {
JNIEnv *env;
JavaVM *jvm;
JavaVMOption options[3];
JavaVMInitArgs args;
memset(&args,0,sizeof(&args),)
args.version = JNI_VERION_1_4;
args.nOptions = 3;
args.option = options;
args.ignoreUnrecognized = JNI_TRUE;
options[0] = "-Djava.class.path=.";
options[1] = "-Djava.library.path=.";
options[2] = "-verbose:jni";
int ret = JNI_CreateJavaVM(&jvm,(void **)&env,&args);
if(ret < 0) {
cout<<"起动JVM 失败"<<endl;
return -1;
}
//我们加载上面的RMI Client类
jclass cla = env->FindClass("org/mclaren/remote/Client");
//取得 main方法, 第3个参数是Java签名
jmethodID init = env->GetStaticMethodID(cla,"main","([-Ljava.lang.String)V");
env->CallStaticMethod(cla,init,NULL);
jvm->DestroyJavaVM();
return 0;
}
VC编译后,执行. 取得的效果和用java.exe起动是一样
(2) Linux环境下 :
a. 代码不变,因为上面用的是标准C++ 库
b. 设置好环境变量JAVA_HOME, CLASSPATH和 PATH
c. 在 .bash_profile里追加一条:
export LD_LOBRARY_PATH=$JAVA_HOME/jre/lib/i386/client
到Mclaren.cpp所在的目录下输入命令 :
g++ -I $JAVA_HOME/include -I $JAVA_HOME/include/linux -L $LD_LIBRARY_PATH -o Mclaren Mclaren.cpp -ljvm
注意" -ljvm是小写的L
这样就会产生一个Mclaren的可执行文件,执行一下,效果同上.