SpringBoot项目,运行在linux环境中,如何引用 .so 动态链接库文件
不同于在windows环境下调用dll文件,在linux下调用dll的话,会报错:Caused by: java.lang.UnsatisfiedLinkError: /root/hgs/Odbc.dll: /root/hgs/Odbc.dll: 无效的 ELF 头 (Possible cause: endianness mismatch),这是因为dll是在windows下的动态库,在linux应该调用so动态库
Caused by: java.lang.UnsatisfiedLinkError: /root/hgs/Odbc.dll: /root/hgs/Odbc.dll: 无效的 ELF 头 (Possible cause: endianness mismatch)
at java.lang.ClassLoader$NativeLibrary.load(Native Method) ~[na:1.8.0_333]
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1937) ~[na:1.8.0_333]
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1820) ~[na:1.8.0_333]
at java.lang.Runtime.load0(Runtime.java:810) ~[na:1.8.0_333]
at java.lang.System.load(System.java:1086) ~[na:1.8.0_333]
at com.example.demo.util.CommonUtil.<clinit>(CommonUtil.java:41) ~[classes!/:1.0.3.RELEASE]
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_333]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_333]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_333]
at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[na:1.8.0_333]
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:204) ~[spring-beans-5.2.7.RELEASE.jar!/:5.2.7.RELEASE]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:87) ~[spring-beans-5.2.7.RELEASE.jar!/:5.2.7.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1312) ~[spring-beans-5.2.7.RELEASE.jar!/:5.2.7.RELEASE]
... 39 common frames omitted
dll与lib是Windows下描述封装代码库的一种格式,.a,.so是linux下的。dll与.so是动态库,lib与.a是静态库。
一、在linux下创建C++文件,并生成so动态库
在linux服务器上,任意一路径下,创建 jna.h和jna.c文件
jna.h
int add(int a, int b);
int minus(int a, int b);
int multiply(int a, int b);
double divide(int a, int b);
jna.c
#include "jna.h"
int add(int a, int b)
{
return a + b;
}
int minus(int a, int b)
{
return a - b;
}
int multiply(int a, int b)
{
return a * b;
}
double divide(int a, int b)
{
double m = (double)a / b;
return m;
}
在同路径下执行命令,生成so文件:
gcc -shared -fPIC jna.c -o libjna.so
目录结构如下
[root@196 jna]# tree
.
├── jna.c
├── jna.h
└── libjna.so
二、SpringBoot项目
代码结构
├─src
│ └─main
│ ├─java
│ │ └─com
│ │ └─example
│ │ └─demo
│ │ │ DemoApplication.java
│ │ │
│ │ ├─controller
│ │ │ JnaController.java
│ │ │
│ │ ├─entity
│ │ │ TestEntity.java
│ │ │
│ │ ├─service
│ │ └─util
│ │ CommonUtil.java
│ │ JnaInterface.java
│ │ Test.java
│ │
│ └─resources
│ │ application.yml
│ │
│ └─static
│ libjna.so --- linux下的动态链接库
JnaController.java
package com.example.demo.controller;
import com.example.demo.entity.TestEntity;
import com.example.demo.util.CommonUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("test")
public class JnaController {
@Autowired
private CommonUtil commonUtil;
@PostMapping("/")
public int addTest(@RequestBody TestEntity testEntity)
{
return commonUtil.test(testEntity);
}
}
TestEntity.java
package com.example.demo.entity;
import lombok.Data;
@Data
public class TestEntity {
private String type;
private int num1;
private int num2;
}
CommonUtil.java
package com.example.demo.util;
import com.example.demo.entity.TestEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
@Component
@Slf4j
public class CommonUtil {
static {
try {
log.info("start-------------------------");
//将项目中的so文件,读取成inputStream
InputStream is = new ClassPathResource("/static/libjna.so").getInputStream();
String libPaths = System.getProperty("java.library.path");
//":/root/hgs/sources:/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib";
if (StringUtils.isNotEmpty(libPaths) && libPaths.split(":").length > 1) {
//linux系统中设置的动态库路径
String sourcePath = libPaths.split(":")[1]; // /root/hgs/sources
//将项目中的so文件,保存到其中一个动态库路径下
File file = new File(sourcePath + "/libjna.so");
inputStreamToFile(is, file);
//因为linux下,访问动态链接库,会自动忽略文件名前面的lib,只需要写lib后面的内容(jna)即可访问到
System.loadLibrary("jna");
}
} catch (Exception e) {
log.error("----error,{}", e.getStackTrace());
}
}
private static void inputStreamToFile(InputStream ins, File file)
{
try {
OutputStream os = new FileOutputStream(file);
int bytesRead = 0;
byte[] buffer = new byte[8192];
while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.close();
ins.close();
} catch (Exception e) {
log.error("--- inputstreamtofile error, {}", e.getMessage());
}
}
public int test(TestEntity testEntity)
{
int num1 = testEntity.getNum1();
int num2 = testEntity.getNum2();
switch (testEntity.getType()) {
case "add":
return JnaInterface.INSTANCE.add(num1, num2);
case "minus":
return JnaInterface.INSTANCE.minus(num1, num2);
case "multiply":
return JnaInterface.INSTANCE.multiply(num1, num2);
case "divide":
double divide = JnaInterface.INSTANCE.divide(num1, num2);
return (int) divide;
default:
return -1;
}
}
}
JnaInterface.java
package com.example.demo.util;
import com.sun.jna.Library;
import com.sun.jna.Native;
public interface JnaInterface extends Library {
//这里跟CommonUtil.java里 System.loadLibrary("jna"); 写一样的名称(jna)
JnaInterface INSTANCE = (JnaInterface) Native.loadLibrary("jna", JnaInterface.class);
int add(int a, int b);
int minus(int a, int b);
int multiply(int a, int b);
double divide(int a, int b);
}
DemoApplication.java
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
@EnableScheduling
@EnableDiscoveryClient
@EnableTransactionManagement
@EnableFeignClients
public class DemoApplication {
public static void main(String[] args)
{
SpringApplication.run(DemoApplication.class, args);
}
}
application.yml
spring:
application:
name: jna-service
server:
port: 16006
http:
encoding:
charset: UTF-8
enabled: true
force: true
port: 16010
maven编译程序,生成jna-service.jar,上传到linux环境中去,然后执行
java -jar jna-service.jar
运行成功!
使用postman调用:
http://xx.xx.xx.xx:16006/test/
POST
入参体:
{
"type": "add",
"num1": 143,
"num2": 151
}
调用成功!
port: 16010
maven编译程序,生成jna-service.jar,上传到linux环境中去,然后执行
~~~shell
java -jar jna-service.jar
[外链图片转存中…(img-V8UNExgd-1680600671513)]
运行成功!
使用postman调用:
http://xx.xx.xx.xx:16006/test/
POST
入参体:
{
"type": "add",
"num1": 143,
"num2": 151
}
调用成功!