您的位置:首页 > 编程语言 > C语言/C++

servlet中Java调用C++,JNI

2016-02-20 15:43 369 查看

servlet中Java调用C++,JNI

阅读:2452013-01-04 10:22
标签:杂谈

我想尝试用Java调用旧有的c++代码,在servlet中。这样以前的就代码就可以变成web应用了

其实我研究这个是想以前的应用只要满足了servlet的接口是不是就可以部署到云上,因为Amazon是支持这种部署的,所以想做些试验。

c++是很熟悉了,但是JNI或者Java都不大熟悉。

先要搞出个servlet,我还不太会,所以用的是GWT的模板,这样就有了一个可以debug的servlet。

就这个小事还有点故事,IE总是不能运行,总让我下载GWT的插件,我手动下载并且安装了,但是它还是让我下载。

后来我修改IE的security让activeX可以运行就好了,就有个提示需要手动确认。但是我找了activeX的安装路径并没有发现任何新的ActiveX。

实在是不明白没有安装ActiveX,为啥需要运行ActiveX。可能因为我还不够理解GWT到底是什么。

anyway,我的servlet是有了。

下面就是要创建Java会用的C++程序了。

先在Java中定义这个要在C++里实现的函数的原型

package example.HelloServletAAA.server;

public class NativeCaller {

public native int CallNativeGetInt(int argPlayerID);

static

{

System.out.print('start load!!!');

System.loadLibrary('NativeHelloAAA');

System.out.print('end load!!!');

}

}

这个函数接受一个int返回一个int

然后创建C++的项目,我发现这个c++的项目需要是多字节码的

在c++的头文件中声明要实现的那个函数

#include

#ifndef _Included_APCluster

#define _Included_APCluster

#ifdef __cplusplus

extern 'C' {

#endif

JNIEXPORTjint JNICALL Java_APCluster_CallAPClusterDll

(JNIEnv *, jobject, jint);

#ifdef __cplusplus

}

#endif

#endif

注意前面多的参数,一定是转换过程中需要的

头文件和库文件都从JDk中拿来

然后在cpp中实现刚才声明的函数

#ifdef __cplusplus

extern 'C' {

#endif

JNIEXPORT jint JNICALL Java_APCluster_CallAPClusterDll

(JNIEnv *env, jobject _obj, jint _arg_int)

{

return 59;

}

#ifdef __cplusplus

}

#endif

如果Java代码没有找到想加载的dll会报这个异常java.lang.UnsatisfiedLinkError: no NativeHelloAAA in java.library.path

放在这里依然不好,C:\Users\Administrator\workspace\HelloServletAAA\war\WEB-INF\lib。

最后我把加载路径改成绝对路径就可以了

System.load('C:/Users/Administrator/workspace/HelloServletAAA/war/WEB-INF/lib/NativeHelloAAA.dll');

或者放在这里C:\Program Files\Java\jre6\bin

其实可以放的地方可以查到

System.out.println(System.getProperty('java.library.path'));

得到新的错误

java.lang.UnsatisfiedLinkError: C:\Users\Administrator\workspace\HelloServletAAA\war\WEB-INF\lib\NativeHelloAAA.dll: Can't load IA 32-bit .dll on a AMD 64-bit platform

这是由于我的c++项目设置不对,设置成64的以后就可以加载了

设置成64位的项目要注意设置正确的sub-system和entry point。

SubSystem = Windows (/SUBSYSTEM:WINDOWS)

Entry Point = DllMain

然后接着就是这个错误

Caused by: java.lang.UnsatisfiedLinkError: example.HelloServletAAA.server.NativeCaller.CallNativeGetInt(I)I

这个错误看起来是在指定的dll中找不到方法定义

这个错误的原因就是java和c++两边的签名不吻合

其实有个简单的解决办法,就是用java带的工具生成c++项目用的头文件

workspace\HelloJNICallerAAA\bin>javah -jni NativeCaller

要想用这个命令,要设置java的路径到环境变量中另外java class所在的路径要么也设置成环境变量,要么就当成当前的运行路径才可以

这样生成的头文件类似下面

#include

#ifndef _Included_NativeCaller

#define _Included_NativeCaller

#ifdef __cplusplus

extern 'C' {

#endif

JNIEXPORT jint JNICALL Java_NativeCaller_CallerNativeGetInt

(JNIEnv *, jobject, jint);

#ifdef __cplusplus

}

#endif

#endif

我就发现我的错误了,java中的类要和c++的函数名对应,我以前以为的是java的类是和c++的dll文件名对应,实际上dll文件名在整个调用中不重要,只要能加载就可以了

---------------------------------------------------------------------------------

然后我在我的servlet上试验有遇到了一些困难

servlet的路径要复杂,另外还有包的命名也会增加困难。

最后发现正确的命令是这样

C:\Users\Administrator\workspace\HelloServletAAA\war\WEB-INF\classes>javah -jni -classpath . example.HelloServletAAA.server.NativeCaller

生成的头文件类似这样

#include <jni.h>

#ifndef _Included_example_HelloServletAAA_server_NativeCaller

#define _Included_example_HelloServletAAA_server_NativeCaller

#ifdef __cplusplus

extern 'C' {

#endif

JNIEXPORT jint JNICALL Java_example_HelloServletAAA_server_NativeCaller_CallNativeGetInt

(JNIEnv *, jobject, jint);

#ifdef __cplusplus

}

#endif

#endif

函数的签名反映了包和类的名字

-------------------------------------------------------------------------------------------

怎么debug JNI中的c++项目

很简单,先让java这边停在native的调用之前

然后用c++的编译器attach上去,attach JavaW或者Java的进程就可以了

Switch to Visual Studio, and bring up the 'Attach to process' dialog (Ctrl-Alt-P). Select the java process (javaw.exe or java.exe) by typing 'j'

4加载中...
内容加载失败,点击此处重试
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: