前言
一般情况下很少用到Java调C++的场景,我们这次的场景是有个旧的打印服务是C++实现,旧服务重构较麻烦,就直接使用Java方式调用了
实现
dll方式
-
在idea中加一个external工具
-
Java代码生成接口方法,即native方法
-
使用第一步中的添加的工具生成C++ .h文件
-
加载C++的dll文件
Jni的这种调用容易引起jvm崩溃,大多数原因网络上说的内存溢出,有C++代码没有及时释放内存导致的内存泄漏也有创建线程过多等原因
@Slf4j
@Configuration
public class PrintOrderDllLoad {
@Autowired
private LegacyHHTServerService legacyHHTServerService;
@Bean
public PrintOrderDllHandler initDllHandler() {
if (!SystemUtils.IS_OS_WINDOWS) {
LoggerUtil.warn(log, "不是windows机器, 不load dll");
return createDllHandler();
}
LoggerUtil.info(log,"开始加载dll文件");
//注:使用System.load()时,文件名必须包含.dll后缀
String path = legacyHHTServerService.getBasePath() + "/" + "HHTPrinterLibrary.dll";
File file = new File(path);
if(!file.exists()){
LoggerUtil.error(log,"dll文件路径:{}不存在",path);
throw new CommonBizException(CommonErrorCode.LOAD_DLL_FAILED, "dll文件加载失败,路径不存在");
}
try {
System.load(path);
} catch (Exception e) {
LoggerUtil.error(log,"dll文件加载失败");
throw new CommonBizException(CommonErrorCode.LOAD_DLL_FAILED, "dll文件加载失败");
}
LoggerUtil.info(log,"加载dll文件成功");
/**
* 初始化dllHandler类
*/
return createDllHandler();
}
private PrintOrderDllHandler createDllHandler() {
PrintOrderDllHandler printOrderDllHandler = new PrintOrderDllHandler();
NativeMethod nativeMethod = new NativeMethod();
printOrderDllHandler.setNativeMethod(nativeMethod);
return printOrderDllHandler;
}
}
cmd命令行方式
这种方式不存在jvm崩溃的问题,他只是新启动一个进程,代码中可以看出是启动了一个 .exe程序并给它传参数而已,不会影响jvm的进程
@Slf4j
@Service
public class CmdPrintInvoker {
@Autowired
private LegacyHHTServerService legacyHHTServerService;
/**
* cmd启动进程方式的打印
*/
public void printOrderWithCmd(CmdPrintInputParamDTO cmdPrintInputParamDTO){
LoggerUtil.info(log,"cmd命令开启打印进程,打印单据:{}",cmdPrintInputParamDTO.getPrintTypeParam().getOrderId());
invokeCmd(cmdPrintInputParamDTO);
LoggerUtil.info(log,"cmd命令结束打印进程,打印单据:{}",cmdPrintInputParamDTO.getPrintTypeParam().getOrderId());
}
private void invokeCmd(CmdPrintInputParamDTO cmdPrintInputParamDTO) {
if (ObjectUtils.isEmpty(cmdPrintInputParamDTO)) {
throw new CommonBizException(CommonErrorCode.NULL_PARAMETER, "调用cmd启动进程,参数不能为空");
}
List<String> cmdParamList = new ArrayList<>();
String cmdParamJson = JSONObject.toJSONString(cmdPrintInputParamDTO);
cmdParamJson = cmdParamJson.replaceAll("\"","\\\\\"");
String printProcessExePath = legacyHHTServerService.getBasePath() + "/HHTPrinterLibraryConsoleWrapper.exe";
cmdParamList.add(printProcessExePath);
cmdParamList.add(cmdParamJson);
CmdPrintExecuter cmdPrintExecuter = new CmdPrintExecuter();
int result = cmdPrintExecuter.execute(cmdParamList);
if(CmdPrintResultEnum.NORMAL_EXIT.getType() != result){
CmdPrintResultEnum cmdPrintResultEnum = CmdPrintResultEnum.getByType(result);
String exitDesc = null;
if(!ObjectUtils.isEmpty(cmdPrintResultEnum)){
exitDesc = cmdPrintResultEnum.getDesc();
}
LoggerUtil.info(log,"调用打印进程执行打印,进程退出异常:{}",exitDesc);
throw new CommonBizException(CommonErrorCode.CMD_EXCUTE_ERROR,exitDesc);
}
}
}
@Slf4j
public class CmdPrintExecuter {
public int execute(List<String> cmdStrList) {
if (!SystemUtils.IS_OS_WINDOWS) {
LoggerUtil.warn(log, "不是windows机器, 不调用打印cmd");
return 0;
}
if(CollectionUtils.isEmpty(cmdStrList)){
LoggerUtil.error(log,"命令行启动并执行进程,命令行参数不能为空");
throw new CommonBizException(CommonErrorCode.ILLEGAL_PARAMETERS,"命令行启动并执行进程,命令行参数不能为空");
}
try {
LoggerUtil.info(log,"命令行启动并执行进程开始,命令行参数:{}",cmdStrList);
ProcessBuilder processBuilder = new ProcessBuilder(cmdStrList);
processBuilder.redirectErrorStream(true);
Process process = processBuilder.start();
StringBuilder sb = new StringBuilder();
new BufferedReader(new InputStreamReader(process.getInputStream())).lines()
.forEach(logStr -> {
sb.append(logStr);
sb.append("; ");
});
LoggerUtil.info(log,"命令行启动进程输出日志:{}", sb.toString());
int exitCode = process.waitFor();
LoggerUtil.info(log,"命令行启动并执行进程结束,进程退出码exitCode:{}",exitCode);
return exitCode;
} catch (Exception e) {
LoggerUtil.error(log, e,"命令行启动进程异常,命令行参数:{}",cmdStrList);
throw new CommonBizException(CommonErrorCode.CMD_EXCUTE_ERROR,"命令行执行进程异常");
}
}
}