在 Java web 项目中加载本地方法【用简单 servlet 实现】
新建web project项目:JavaC
工程结构:
结构说明:JNICls.java这个类是加载本地动态库,调用本地方法的类,同时也是一个servlet类,实现了Servlet接口
各文件如下:
index.jsp:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>JNI_Demo 测试</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> </head> <body> <input type="button" name="sayHello" value="获取JNI数据" onclick="javascript:window.location='sayHello'"/> </body> </html>
web.xml:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <servlet> <servlet-name>JNI</servlet-name> <servlet-class>com.lvshitech.javac.JNICls</servlet-class> </servlet> <servlet-mapping> <servlet-name>JNI</servlet-name> <url-pattern>/sayHello</url-pattern> </servlet-mapping> </web-app>
JNICls.java:在编写这个文件的时候,先写本地方法,其他的接口实现和方法覆写都先不写,等把本地方法编译成.h文件之后再补上接口和覆写的各个方法,
如:
public class JNICls { public native int[] sayHello(int ... inArr); }
C++代码:
#include <iostream> // 导入有关JNI的库文件以及Java函数生成的本地.h文件 #include "com_lvshitech_javac_JNICls.h" #include <jawt_md.h> #include <jni.h> #include <jni_md.h> using namespace std; /* inArr:Java传入的int类型的数组 返回:jintArray,int类型的数组 实现数组元素 +10 的操作 */ JNIEXPORT jintArray JNICALL Java_com_lvshitech_javac_JNICls_sayHello (JNIEnv *env, jobject obj, jintArray inArr) { // 获取传入的数组大小 jsize inArrLen = env->GetArrayLength(inArr); // 如果数组为空数组,则直接结束 if (inArrLen == 0) { return NULL; } // 声明一个jint*类型的指针变量,用以接收传入的数组,遍历数组+10操作 jint* tmp = env->GetIntArrayElements(inArr, false); for (int i = 0; i < inArrLen; i++) { tmp[i] += 10; } // 创建一个jintArray的空数组,用以接收+10后的新数组元素 jintArray retArr = env->NewIntArray(inArrLen); // 接收新数组元素,SetIntArrayRegion(空数组,数组起始下标,数组大小,新数组指针变量) env->SetIntArrayRegion(retArr, 0, inArrLen, tmp); // 释放资源 env->ReleaseIntArrayElements(inArr, tmp, 0); // 返回数组 return retArr; }
完整的文件:
package com.lvshitech.javac; import java.io.IOException; import javax.servlet.Servlet; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; public class JNICls implements Servlet { public native int[] sayHello(int ... inArr); // 加载 windows 下的动态库,Linux 下的加载方法不同 static { System.loadLibrary("demo"); } // 自动执行 init 方法,在这个方法里面加载本地动态库 @Override public void init(ServletConfig arg0) throws ServletException { // 创建类对象 JNICls jniCls = new JNICls(); // 准备操作的数组 int[] inArr = {10, 20, 30, 40}; // 调用本地方法 int[] retArr = jniCls.sayHello(inArr); // 遍历数组 if (retArr != null && retArr.length > 0) { for (int i=0; i<retArr.length; i++) { System.out.println((i+1) + " : " + retArr[i]); } } else { System.out.println("JNI返回的数组长度为0"); } } @Override public void destroy() { System.out.println("**** destroy"); } @Override public ServletConfig getServletConfig() { System.out.println("**** getServletConfig"); return null; } @Override public String getServletInfo() { System.out.println("**** getServletInfo"); return null; } @Override public void service(ServletRequest arg0, ServletResponse arg1) throws ServletException, IOException { System.out.println("**** service"); } }
执行:点击按钮
报错如下:
说的是在加载动态库文件的时候没有找到链接,即在指定路径下找不到该动态库文件demo.dll,而这个路径的key是java.library.path,
这个路径具体是什么呢?在JNICls.java文件中添加下面的测试代码,获取java.library.path的value:
// 加载 windows 下的动态库,Linux 下的加载方法不同 static { String path = System.getProperty("java.library.path"); System.out.println("---->" + path); System.loadLibrary("demo"); }
再次执行,输出:
具体内容是:
---->D:\myeclipse2014pro\myeclipse-pro-2014-GA-offline-installer-windows\finished\binary\com.sun.java.jdk7.win32.x86_64_1.7.0.u45\bin;D:\tomcat7\apache-tomcat-7.0.75\bin
这里有两个路径,一个myeclipse的安装路径,一个tomcat的路径,为了方便项目部署,选择tomcat路径:D:\tomcat7\apache-tomcat-7.0.75\bin
把生成的动态库文件 demo.dll 拷贝到该路径下:
重新启动tomcat,执行:
点击按钮:
正常调用本地方法。
****************************************************************** 下面是案例的改进:
由用户输入数组,在后台调用本地方法处理后(模拟C语言控制底层设备或者采集数据),返回页面显示:
JNICls.java本地方法:
public class JNICls { // num 是数组元素要加上的数,inArr是输入的数组 public native int[] sayHello(int num, int ... inArr); }
使用命令行javac 和 javah命令分别编译得到 .h 文件
C++代码:
#include <iostream> // 导入有关JNI的库文件以及Java函数生成的本地.h文件 #include "com_lvshitech_javac_JNICls.h" #include <jawt_md.h> #include <jni.h> #include <jni_md.h> using namespace std; /* inArr:Java传入的int类型的数组 返回:jintArray,int类型的数组 实现数组元素 +num 的操作 */ JNIEXPORT jintArray JNICALL Java_com_lvshitech_javac_JNICls_sayHello (JNIEnv *env, jobject obj, jint num, jintArray inArr) { // 获取传入的数组大小 jsize inArrLen = env->GetArrayLength(inArr); // 如果数组为空数组,则直接结束 if (inArrLen == 0) { return NULL; } // 声明一个jint*类型的指针变量,用以接收传入的数组,遍历数组 +num 操作 jint* tmp = env->GetIntArrayElements(inArr, false); for (int i = 0; i < inArrLen; i++) { tmp[i] += num; } // 创建一个jintArray的空数组,用以接收+10后的新数组元素 jintArray retArr = env->NewIntArray(inArrLen); // 接收新数组元素,SetIntArrayRegion(空数组,数组起始下标,数组大小,新数组指针变量) env->SetIntArrayRegion(retArr, 0, inArrLen, tmp); // 释放资源 env->ReleaseIntArrayElements(inArr, tmp, 0); // 返回数组 return retArr; }
index.jsp:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>JNI_Demo 测试</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> </head> <body> <form action="getJniData" method="post"> <h3>请填写数组数据:</h3> --------------------------------------<br/> 第一个数:<input type="text" name="num1"/><br/> 第二个数:<input type="text" name="num2"/><br/> 第三个数:<input type="text" name="num3"/><br/> 第四个数:<input type="text" name="num4"/><br/> 第五个数:<input type="text" name="num5"/><br/> --------------------------------------<br/> 增量:<input type="text" name="num"/><br/><br/> <input type="submit" name="subBtn" value="获取 JNI 数据"/> </form> </body> </html>
JNICls.java:
package com.lvshitech.javac; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @SuppressWarnings("serial") public class JNICls extends HttpServlet { // num 是数组元素要加上的数,inArr是输入的数组 public native int[] sayHello(int num, int ... inArr); // 加载 windows 下的动态库,Linux 下的加载方法不同 static { System.loadLibrary("demo"); } // 调用本地方法 @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 接收用户数据 int num = Integer.parseInt(req.getParameter("num")); int num1 = Integer.parseInt(req.getParameter("num1")); int num2 = Integer.parseInt(req.getParameter("num2")); int num3 = Integer.parseInt(req.getParameter("num3")); int num4 = Integer.parseInt(req.getParameter("num4")); int num5 = Integer.parseInt(req.getParameter("num5")); // 填充到数组中 int[] inArr = {num1, num2, num3, num4, num5}; int[] inArrRet = {num1, num2, num3, num4, num5}; // 创建类对象 JNICls jniCls = new JNICls(); // 调用本地方法 int[] retArr = jniCls.sayHello(num, inArr); // 转发到jsp页面 if (retArr != null && retArr.length > 0) { req.setAttribute("inArrRet", inArrRet); req.setAttribute("retData", retArr); } else { req.setAttribute("retStr", "JNI返回的数组长度为0"); } req.getRequestDispatcher("result.jsp").forward(req, resp); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req, resp); } }
web.xml:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <servlet> <servlet-name>JNI</servlet-name> <servlet-class>com.lvshitech.javac.JNICls</servlet-class> </servlet> <servlet-mapping> <servlet-name>JNI</servlet-name> <url-pattern>/getJniData</url-pattern> </servlet-mapping> </web-app>
result.jsp:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>JNI获取到的数据</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <% int[] retData = (int[])request.getAttribute("retData"); // 处理后的数据 int[] inArrRet = (int[])request.getAttribute("inArrRet"); // 原来的数据 String retStr = (String)request.getAttribute("retStr"); %> </head> <body> <h2>原来的数据:</h2> <c:if test="<%= inArrRet.length == 0 %>"> <c:out value="数据为空!"/> </c:if> <c:if test="<%= inArrRet.length > 0 %>"> <font color="blue">第一个数:<%=inArrRet[0] %></font><br/> <font color="blue">第二个数:<%=inArrRet[1] %></font><br/> <font color="blue">第三个数:<%=inArrRet[2] %></font><br/> <font color="blue">第四个数:<%=inArrRet[3] %></font><br/> <font color="blue">第五个数:<%=inArrRet[4] %></font><br/> </c:if> --------------------------------------<br/> <h2>经过 JNI 处理后的数据:</h2> <c:if test="<%= retData.length == 0 %>"> <c:out value="<%= retStr %>"/> </c:if> <c:if test="<%= retData.length > 0 %>"> <font color="red">第一个数:<%=retData[0] %></font><br/> <font color="red">第二个数:<%=retData[1] %></font><br/> <font color="red">第三个数:<%=retData[2] %></font><br/> <font color="red">第四个数:<%=retData[3] %></font><br/> <font color="red">第五个数:<%=retData[4] %></font><br/> </c:if> </body> </html>
执行:
执行后的结果:
换一个数据:
执行后的结果: