在 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>

执行:

执行后的结果:

换一个数据:

执行后的结果:

 

posted @ 2018-03-08 22:34  半生戎马,共话桑麻、  阅读(256)  评论(0)    收藏  举报
levels of contents