伴隨着 Jython 的出現,使 Java 代碼和 Python 代碼做到了無縫交互。而 Python 自身強大的字符處理能力和動態執行能力更佳彌補了大型 J2EE 應用的一些先天缺陷。本文將分爲幾個章節,配合生動的實例,着重闡述如何利用 Jython 讓我們的大型 Java 應用系統更加靈活。
引言
伴隨信息時代的的迅猛發展,互聯網已經全面滲透到電子商務,金融,電信,物流,資源等各個領域並逐漸成爲其不可或缺的一部分。
信息時代,時間就意味着金錢。伴隨着各種大型應用系統的普及,人們對於系統的穩定性,對與零宕機硬件不斷的提出更高要求,卻往往會忽視應用系統自身升級所帶來的問題。
什麼是 Jython
Jython 是一種完整的語言,而不是一個簡單的 Java 翻譯器或 Python 編譯器,它是 Python 在 Java 中的完整實現。由於 Jython 繼承了 Java 和 Python 二者的特性從而使其顯得很獨特。
看到這裏讀者一定會問,那 Jython,Java 以及 Python 之間的關係到底是怎麼樣的呢?其實,我理解 Jython 說簡單一些,就是用 Java 語言實現的 Python 解釋器,這種關係也就意味着你可以用 Python 語言編寫程序而同時使用 Java 庫。
在這裏我不想大篇幅的闡述 Jython 是什麼,如果各位讀者感興趣,請參閱 Jython 介紹
發現問題
大型的應用系統從不同的應用層面大致可以分爲 :
- UI 層
該層應該說是比較穩定的,或者說它的變化對於整個系統的運行不會帶來特別嚴重的影響。
- 接口層
該層與上層的 UI 以及底層的實現,都有着緊密的聯繫,所謂牽一髮而動全身。作爲一位大型應用系統的架構師或是系統設計人員,往往會出於維護成本以及開發成本的考慮,儘可能會讓這個層面的設計與定義相對靈活。
- 實現層
該層可以說是系統最核心的層面,整個系統的核心處理邏輯都會放在這一層面上。而往往該層,在實際使用過程中也是最不穩定的,最容易從需求角度發生變化。
- 數據層
該層其實說簡單一些就是數據庫層,用於用戶數據的查詢、組裝、更新等等,提供持久的數據服務,我們可以認爲它也是個比較穩定的層面。
問題焦點似乎發現了,如何讓我們的實現層更加靈活成爲核心問題所在。那麼如何能讓我們系統的核心“與時俱進”呢?
分析問題
接下來,讓我們看看一個系統的實現層都可能會幹哪些事情:
- 接收通過接口傳遞過來的用戶數據
- 依據一定的業務邏輯,處理用戶數據
- 將處理結果更新到數據庫中(可選)
- 將處理結果響應給 UI 層並渲染出來
分析到這裏。我們對於實現層的內部數據流向又有了更進一步直觀的認識。而我們對於實現層定義的所謂變數所在,也就比較明顯了,如何實現業務處理邏輯的分離成爲我們的目標。
解決問題
動態算法核心
本段以一個簡單的 Python 方法爲例,介紹如何藉助 Jython 的動態執行能力來實現系統核心算法,以供 Java 代碼調用。
Java 層代碼片段
PythonInterpreter interpreter=new PythonInterpreter(); PySystemState sys = Py.getSystemState(); <em>// 將 Jython 庫加入系統的 classpath 中或直接通過這種方式動態引入</em> sys.path.add("D:\\jython2.5.2\\Lib"); <em>// 執行算法所在的 python 文件</em> interpreter.execfile("impl/demo.py");
Jython 層代碼片段
def getValue(o): result = 0 if o < 2: return -1 else: for i in range(o): result +=i return result
該方法很簡單,當用戶數據小於 2 時返回運算結果 -1,當用戶數據大於等於 2 時,返回從 0 到輸入數據的和。
Java 調用
PyFunction pyFunction = (PyFunction)interpreter.get("getValue",PyFunction.class ); System.<em>out</em>.println("Result: "+pyFunction.__call__(new PyInteger(100)));
通過 PyFunction 對象的內置方法 __call__() 傳入用戶數據。因爲 Python 文件本身具備動態編譯執行能力,所以我們只需要更新算法核心部分的 Python 代碼,就可以達到動態改變系統底層算法的目的,而不需要更新 jar 包或重新部署應用而重啓應用系統。
上邊這個例子其實很簡單,我們假設這個簡單的 Jython 方法就是我們的業務邏輯,它會返回處理結果供上層渲染,但當業務邏輯發生了變化,比如當用戶輸入等於 2 時也要求返回值是 -1,此時對於這樣一個簡單的需求,我們只需要修改 Jython 代碼就好了,並不需要更新 Java 層的東西。
動態配置文件
本段着重介紹,當系統中配置文件中若該項的數值需要發生變化時,我們如何來解決動態獲取最新配置的問題。
配置文件 ( demo.
properties )
假設我們爲每一次查詢結果定義了最大的返回數目爲 100
MaxValue=100 …
Java 層代碼片段
PythonInterpreter interpreter=new PythonInterpreter(); PySystemState sys = Py.getSystemState(); sys.path.add("D:\\jython2.5.2\\Lib"); <em>//‘re’是 Python 自身提供的正則表達式庫,在 Python 方法中我們需要用到這個庫中的方法,所以需要提前導入進來</em> interpreter.exec("import re"); interpreter.execfile("impl/demo.py"); PyFunction pyFunction = (PyFunction)interpreter.get("getconfig",PyFunction.class );
Jython 層代碼片段
import re def getconfig(flag): result='' f=open('demo.properties','r') for i in f: g=re.findall(flag+'.*=.*',i) if len(g)>0: al=g[0].split('=') result=al[1].strip() break return result
這個方法很簡單根據我們傳入的條目名稱,獲取配置文件中對應條目的定義數值。
Java 調用
System.out.println("Config value: "+pyFunction.__call__(new PyString("MaxValue")));
藉助 Python 強大的文本處理能力,由 Python 作爲 Java 代碼和配置文件之間的橋樑,從而動態的獲取最新配置項數值。
相比純 Java 內存操作的實現方式,這種實現機制從運行速度上可能沒有什麼優勢,但是卻可以給我們帶來一種新的理念。也許我們可以將這兩種方式融合起來找到某種平衡,既不影響系統的運行效率又保留了 Python 動態的運行機制,達到雙贏的目的。
動態後臺實現
前邊已經提到了系統接口定義層和實現層之間的關係。那麼我們是否可以藉助 Jython 來幫助我們動態實現定義的接口呢?答案很明顯,當然可以。
這裏我就以一個簡單的例子,介紹一下如何藉助 Jython 實現可插拔式的後臺實現。利用 Jython 來實現 Java 定義的接口。從而可以被其他的 Java 方法調用。
Java 接口定義
package com; public interface Worker { public String getFirstName(); public String getLastName(); public String getId(); public void setId(); }
上邊是一個簡單的人員接口,接口的實現類中我們就需要實現這裏定義的 4 個方法。
Jython 層代碼片段 ( 實現接口 )
from com import Worker class Worker(Worker): def __init__(self): self.first = "FirstName" self.last = "LastName" def getFirstName(self): return self.first def getLastName(self): return self.last def getId(self): return self.Id def setId(self, newId): self.Id = newId
我們在 Worker 類的構造函數中爲該類的兩個私有成員賦值,當調用 getFirstName() 或 getLastName() 方法時我們返回這兩個私有成員的當前值。
Java 層代碼片段
Object javaObject; PythonInterpreter interpreter=new PythonInterpreter(); PySystemState sys = Py.getSystemState(); sys.path.add("D:\\jython2.5.2\\Lib"); interpreter.execfile("impl/demo.py"); interpreter.exec("worker=Worker()"); PyObject pyObject = interpreter.get("worker"); pyObject.invoke("setId",new PyString("8888")); try { Class Interface = Class.forName(interfaceName); javaObject = pyObject.__tojava__(Interface); } catch (ClassNotFoundException e) { e.printStackTrace(); }
Java 調用
Worker worker = (Worker) javaObject; System.out.println("Name:"+worker.getFirstName()+worker.getLastName()); System.out.println("ID:"+worker.getId());
看到這裏就不必多說了,只需要動態的替換 Python 實現部分的代碼,就可以實現可插拔式後臺實現啦。而對於調用這個接口的 Java 對象來說,並不知道這個實現類是否發生了變化。