J2EE信息系統集成解決方案

 在當前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的可執行文件,執行一下,效果同上.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章