这几天需求需要使用java调用python算法来计算出结果,踩了很多坑,于是整理出来这一份资料供大家参考
1、使用 jpython(无法实现复杂调用)
jpython的用法较为简单,但不支持多个python文件的同时调用,很有局限性
1.1.1 导入jar包
<dependency>
<groupId>org.python</groupId>
<artifactId>jython-standalone</artifactId>
<version>2.7.0</version>
</dependency>
1.1.2 调用python脚本中的method()方法
PythonInterpreter interpreter = new PythonInterpreter();
interpreter.execfile("E:\\demo.py");
// 调用demo.py中的method方法
PyFunction func = interpreter.get("method1",PyFunction.class);
Integer a = 10;
Integer b = 10;
PyObject pyobj = func.__call__(new PyInteger(a), new PyInteger(b));
System.out.println("获得方法的返回值 = " + pyobj.toString());
2、使用ProcessBuilder
调用工具类,其中options和price为json对象
PythonExecutor.execPythonFileSync("main.py", options.toString(),price.toString());
终极工具类(同步执行)
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* java调用python的执行器
*/
@Component
public class PythonExecutor {
private static final Logger logger = LoggerFactory.getLogger(PythonExecutor.class);
private static final String OS = System.getProperty("os.name");
private static final String WINDOWS_PATH = "E://suanfa//"; // windows为获取项目根路径即可
private static final String LINUX_PATH = "/home/myuser/project/energy_storage/python/";// linux为python文件所在目录
private static ExecutorService taskPool = new ThreadPoolExecutor(8, 16
, 200L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(600)
, new ThreadFactoryBuilder()
.setNameFormat("thread-Python-runner-%d").build());
/**
* 执行python文件 【同步 会等待py执行完毕】
*
* @param fileName python文件地址
* @throws IOException
*/
public static void execPythonFileSync(String fileName, String options, String price) {
try {
execSync(fileName, options, price);
} catch (IOException e) {
logger.error("读取python文件 fileName=" + fileName + " 异常", e);
}
}
private static void execSync(String fileName, String options, String price) throws IOException {
logger.info("同步读取python文件 init fileName={}&path={}", fileName);
Process process;
if (OS.startsWith("Windows")) {
// windows执行脚本需要使用 cmd.exe /c 才能正确执行脚本
process = new ProcessBuilder("cmd.exe", "/c", "python", WINDOWS_PATH + fileName, options, price).start();
} else {
// linux执行脚本一般是使用python3 + 文件所在路径
process = new ProcessBuilder("python3", LINUX_PATH + fileName, options, price).start();
}
taskPool.submit(() -> {
logger.info("读取python文件 开始 fileName={}", fileName);
BufferedReader errorReader = null;
// 脚本执行异常时的输出信息
errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
List<String> errorString = read(fileName, errorReader);
logger.info("读取python文件 异常 fileName={}&errorString={}", fileName, errorString);
});
taskPool.submit(() -> {
// 脚本执行正常时的输出信息
BufferedReader inputReader = null;
inputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
List<String> returnString = read(fileName, inputReader);
logger.info("读取python文件 fileName={}&returnString={}", fileName, returnString);
});
try {
logger.info("同步读取python文件 wait fileName={}", fileName);
process.waitFor();
} catch (InterruptedException e) {
logger.error("同步读取python文件 fileName=" + fileName + " 等待结果返回异常", e);
}
logger.info("同步读取python文件 fileName={} == 结束 ==", fileName);
}
private static List<String> read(String fileName, BufferedReader reader) {
List<String> resultList = Lists.newArrayList();
String res = "";
while (true) {
try {
if (!((res = reader.readLine()) != null)) {
break;
}
} catch (IOException e) {
logger.error("读取python文件 fileName=" + fileName + " 读取结果异常", e);
}
resultList.add(res);
}
return resultList;
}
}
python代码
# coding:utf-8
import sys
import main
import demjson
if __name__ == '__main__':
print('恭喜您!java调用python代码成功')
print('脚本名为:%s' % (sys.argv[0]))
print('传入的参数为:')
for i in range(1, len(sys.argv)):
print('参数:%s' % (sys.argv[i]))
option_data = sys.argv[1]
price_data = sys.argv[2]
#给接收到的json中的key增加引号
option_data = demjson.decode(option_data)
price_data = demjson.decode(price_data)
print(option_data)
print(price_data)
print("------------------")
#继续调用别的python代码
main.StorageOPT(option_data,price_data)
注意:
1、java中的json字符串传到python代码中,json的key字符串是被取消调了的,需要用demjson.decode来接收,这样就会将数据转成json格式
2、如果传递了时间(yyyy-MM-dd HH:mm:ss),dd和HH中间的空格会将这个json字符串在此分开
优点:
1、可以实现java调用python传递json字符串(python中需要使用demjson.decode来接收转换)
2、可以实现调用的python中含有其他第三方包
3、可以实现调用的python中继续调用别的python类和方法