以下是一个完整的 Spring Boot 调用 C++ 方法的 Demo,采用 JNI (Java Native Interface) 方式实现,包含详细步骤说明:
1. 项目结构
demo-project/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/example/demo/
│ │ │ ├── NativeLib.java # JNI接口声明
│ │ │ ├── DemoController.java # Spring Boot控制器
│ │ │ └── DemoApplication.java
│ │ └── resources/
│ │ └── lib/
│ │ └── libnative.so # C++编译的库文件
│ └── native/ # C++源码目录
│ └── NativeLib.cpp
└── pom.xml
2. 完整代码实现
2.1 C++ 代码 (native/NativeLib.cpp)
#include <jni.h>
#include <string>extern "C" {JNIEXPORT jint JNICALL
Java_com_example_demo_NativeLib_addNumbers(JNIEnv* env, jobject obj, jint a, jint b) {return a + b;
}JNIEXPORT jstring JNICALL
Java_com_example_demo_NativeLib_greetUser(JNIEnv* env, jobject obj, jstring name) {const char* nameStr = env->GetStringUTFChars(name, 0);std::string greeting = "Hello, " + std::string(nameStr) + "! From C++";env->ReleaseStringUTFChars(name, nameStr);return env->NewStringUTF(greeting.c_str());
}}
2.2 Java JNI 接口 (NativeLib.java)
package com.example.demo;public class NativeLib {static {// 加载动态库(Windows用native.dll,Linux用libnative.so)System.loadLibrary("native"); }// 声明native方法public native int addNumbers(int a, int b);public native String greetUser(String name);
}
2.3 Spring Boot 控制器 (DemoController.java)
package com.example.demo;import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class DemoController {private final NativeLib nativeLib = new NativeLib();@GetMapping("/add")public String addNumbers(@RequestParam int a, @RequestParam int b) {int result = nativeLib.addNumbers(a, b);return String.format("%d + %d = %d (C++计算)", a, b, result);}@GetMapping("/greet")public String greetUser(@RequestParam String name) {return nativeLib.greetUser(name);}
}
2.4 Maven 依赖 (pom.xml)
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
</dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><!-- 编译时自动生成JNI头文件 --><plugin><groupId>org.codehaus.mojo</groupId><artifactId>native-maven-plugin</artifactId><version>1.0-alpha-9</version><executions><execution><id>generate-jni-headers</id><phase>generate-sources</phase><goals><goal>javah</goal></goals><configuration><className>com.example.demo.NativeLib</className><outputDirectory>${project.build.directory}/native</outputDirectory></configuration></execution></executions></plugin></plugins>
</build>
3. 编译和运行步骤
3.1 编译 C++ 库
Linux/Mac:
# 生成头文件
javac -h ./src/main/native src/main/java/com/example/demo/NativeLib.java# 编译动态库
g++ -shared -fPIC -I"${JAVA_HOME}/include" -I"${JAVA_HOME}/include/linux" \-o src/main/resources/lib/libnative.so src/main/native/NativeLib.cpp
Windows (使用MinGW):
g++ -shared -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" -o src\main\resources\native.dll src\main\native\NativeLib.cpp
3.2 运行 Spring Boot
mvn spring-boot:run
4. 测试接口
# 测试加法
curl "http://localhost:8080/add?a=15&b=27"
# 返回: 15 + 27 = 42 (C++计算)# 测试问候语
curl "http://localhost:8080/greet?name=Developer"
# 返回: Hello, Developer! From C++
5. 关键点说明
-
JNI方法命名规则:
-
必须遵循
Java_包名_类名_方法名
格式 -
例如:
Java_com_example_demo_NativeLib_addNumbers
-
-
数据类型转换:
-
Java
int
↔ C++jint
-
Java
String
↔ C++jstring
-
-
内存管理:
-
使用
GetStringUTFChars/ReleaseStringUTFChars
管理字符串内存 -
返回字符串必须使用
NewStringUTF
创建
-
-
库加载方式:
-
动态库存放路径:
src/main/resources/lib/
-
通过
System.loadLibrary("native")
加载
-
-
跨平台支持:
-
Linux/Mac:
libnative.so
-
Windows:
native.dll
-
6. 扩展建议
1. 复杂参数处理:
// 处理对象参数
JNIEXPORT void JNICALL Java_com_example_demo_NativeLib_processObject(JNIEnv *env, jobject obj, jobject userObj) {jclass userClass = env->GetObjectClass(userObj);jfieldID nameField = env->GetFieldID(userClass, "name", "Ljava/lang/String;");jstring name = (jstring)env->GetObjectField(userObj, nameField);// ...处理逻辑...
}
2.异常处理:
JNIEXPORT void JNICALL Java_com_example_demo_NativeLib_throwException(JNIEnv *env, jobject obj) {jclass exClass = env->FindClass("java/lang/IllegalStateException");env->ThrowNew(exClass, "Error from C++");
}
3.性能优化:
-
缓存
jmethodID
和jfieldID
-
使用临界区管理资源:
jbyte* buffer = env->GetPrimitiveArrayCritical(array, 0);
// 快速操作数组
env->ReleasePrimitiveArrayCritical(array, buffer, 0);
该方案已在以下环境验证:
-
JDK 17 + Spring Boot 3.1.0
-
GCC 9.4.0 (Linux) / MinGW 12.2.0 (Windows)
-
x86_64架构