最近項目中經常需要將Javascript或者Python中的算法發布為服務,而發布Tomcat服務則需要在Java中調用這些算法,因此就不免要進行跨語言調用,即在Java程序中調用這些算法。
不管是調用Javascript文件還是python腳本,都需要將原來的算法文件進行適當的更改,以便可以在Java中傳入參數,並且得到算法運算結果。
一、Java調用Javascript
需要注意的是Javascript是弱類型語言,定義變量只需要一個var就可以搞定,但是在Java中卻要注意變量類型,不同的輸入參數會有不同的類型。
調用js文件時,需要對其進行調整,設置好需要調用的function和相關參數,使用的js文件代碼如下(其中有些核心算法不能展示):
1 function get3DCode(Latitude,Longitude,Height,level){ 2 var latcode=[];var lngcode=[]; 3 latcode=GeoSOTCode1D(Latitude,level); 4 lngcode=GeoSOTCode1D(Longitude,level); 5 var heicode=[];var geosot3Dcode=[]; 6 heicode=Altcode(Height,level); 7 geosot3Dcode=GeoSOT3D(latcode,lngcode, heicode,level);//三維網格編碼 8 var d3code=[]; 9 d3code=getQuantcodeString(geosot3Dcode); 10 return d3code; 11 }
在Java中使用對應的接口就可以調用,需要設置js文件路徑和輸入參數,調用的代碼如下;
1 package whu.get.three.beidou; 2 3 import java.io.FileReader; 4 import javax.script.Invocable; 5 import javax.script.ScriptEngine; 6 import javax.script.ScriptEngineManager; 7 8 /** * Java調用並執行js文件,傳遞參數,並獲得返回值 */ 9 public class ThreeD_GetBeidouCode { 10 //獲取經緯度及高度,返回三維碼 11 public static String main(String Latitude,String Longitude,String Height,int CodeSize) throws Exception { 12 //獲取經緯度及高度,保存為double類型 13 Double latitude = Double.parseDouble(Latitude); 14 Double longitude = Double.parseDouble(Longitude); 15 Double height = Double.parseDouble(Height); 16 int level = CodeSize; 17 18 //調用js文件 19 ScriptEngineManager manager = new ScriptEngineManager(); 20 ScriptEngine engine = manager.getEngineByName("javascript"); 21 String jsFileName = System.getProperty("catalina.home") + "/webapps/3DBeiDouCode/WEB-INF/classes/3Dcode.js"; // 讀取js文件 22 FileReader reader = new FileReader(jsFileName); // 執行指定腳本 23 engine.eval(reader); 24 String c = ""; 25 if(engine instanceof Invocable) { 26 Invocable invoke = (Invocable)engine; // 調用merge方法,並傳入兩個參數 27 c = String.valueOf(invoke.invokeFunction("get3DCode", latitude, longitude, height, level)); 28 } 29 reader.close(); 30 return c; //返回三維碼 31 } 32 }
這里的ThreeD_GetBeidouCode類只是一個普通的類,需要在其他可運行的主函數中調用這個類的main方法,傳入運行參數就可以得到結果。
二、Java調用Python
Java調用python腳本有好幾種方法,最簡單的是通過Jython來直接運行python代碼,但是這種方法不支持python中引用的第三方庫,因此我使用了Runtime來調用的方法,這也相當於是在控制台執行腳本。
需要注意的是,Java調用python時,不能通過return語句來獲取返回值,而只能通過print將結果寫入到標准輸出流中,然后在Java中通過標准輸入流來讀取到返回結果。
如果對python環境有要求,比如在特定的環境中安裝了需要引用的第三方庫,則還要在Java工程中添加運行環境,在eclipse中點擊Run->Run Configurations->environment,添加Path,值設置為python安裝的路徑。
在python程序中做適當修改:添加引用 import sys,將調用的函數參數設定為sys.argv[1],sys.argv[2]...注意必須是從1開始計數,將需要返回的結果用print函數打印。
本例中python代碼如下:
1 # -*- coding:utf-8 -*- 2 import BaseFunction 3 import numpy as np 4 import itertools 5 import math 6 import sys 7 8 #計算中心要素 9 def cal_central_feature(path,x,y): 10 sf = BaseFunction.open_shpfile(path) 11 x_records = BaseFunction.get_attr_records(sf,x) 12 y_records = BaseFunction.get_attr_records(sf,y) 13 14 dis = [] 15 for x0,y0 in zip(x_records,y_records): 16 distance = 0 17 18 for x1,y1 in zip(x_records,y_records): 19 distance = distance + get_distance(x0,y0,x1,y1) 20 21 dis.append(distance) 22 23 i = dis.index(np.min(dis)) 24 25 result = [x_records[i],y_records[i]] 26 27 return result 28 29 #計算兩點之間的距離 30 def get_distance(x0,y0,x1,y1): 31 xd = x1 - x0 32 yd = y1 - y0 33 distance = math.sqrt(xd**2+yd**2) 34 return distance 35 36 if __name__ == '__main__': 37 result = cal_central_feature(sys.argv[1],sys.argv[2],sys.argv[3]) 38 print(result[0]) 39 print(result[1])
Java中調用的代碼如下:
1 package whu.get.three.beidou; 2 3 import java.io.BufferedReader; 4 import java.io.InputStreamReader; 5 6 /** * Java調用並執行js文件,傳遞參數,並活動返回值 */ 7 public class CalCentralFeatureClass { 8 //輸入shp路徑,獲取坐標 9 public static String main(String filepath) { 10 String pyPath = System.getProperty("catalina.home") + "/webapps/CalCentralFeature/WEB-INF/classes/CalCentralFeature.py"; //python文件路徑 11 String[] args = new String[] { "python", pyPath, filepath, "x","y"}; 12 String c = ""; //記錄返回值 13 try { 14 Process proc = Runtime.getRuntime().exec(args); //執行py文件 15 BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream())); 16 String line = null; 17 while ((line = in.readLine()) != null) { 18 c = c+line+' '; 19 } 20 in.close(); 21 proc.waitFor(); 22 } catch (Exception e) { 23 e.printStackTrace(); 24 } 25 return c; //返回結果 26 } 27 }
得到的運算結果中,每一個python中print的結果,對應一個in.readLine(),可以按照需要獲取自己想要的結果。
如果需要將調用python的程序用tomcat發布為服務,也需要配置tomcat的運行環境,同樣是添加一個Path,賦值為python安裝路徑。