Java JNI开发实践记录

简介:

  下面是使用JNI常见三种场景:

    1.在Java应用中标准Java类库不支持平台相关的特性

    2.已经存在用其它语言写好的类库,希望通过Java JNI类访问

    3.需要使用低级语言(如汇编)来实现时效性要求很高的一小部分代码


    这次使用JNI属于第2中场景,由于加解密库使用C来实现的,而在Java应用中使用到其加密后的密文数据,所以解密部分需要此库。


    在1和3这两种场景下使用JNI做法相对容易一些,通常先定义好本地方法,然后通过javah生成头文件,接着用其它语言(如C)来实现相应的功能,而2中场景则需要做一些简单的适配,因为类库已经存在,而在Java中定义好的本地方法并不能直接对应类库的具体实现,所以得通过调用已存在的类库中的方法来实现本地方法。


    在开始之前有一个坑先看看:

    

    本地编译好的动态库头信息:

1
2
3
4
5
6
7
8
9
10
[ enc]$ readelf -a libfdsi.so 
ELF Header:
   Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
   Class:                             ELF64
   Data:                              2's complement, little endian *******
   Version:                           1 (current)
   OS/ABI:                            UNIX - System V
   ABI Version:                       0
   Type:                              DYN (Shared object file)
   Machine:                           Advanced Micro Devices X86-64

    

    提供方静态库信息:

 

1
2
3
4
5
6
7
8
9
ELF Header:
   Magic:   7f 45 4c 46 02 02 01 00 00 00 00 00 00 00 00 00 
   Class:                             ELF64
   Data:                              2's complement, big endian
   Version:                           1 (current)
   OS/ABI:                            UNIX - System V
   ABI Version:                       0
   Type:                              REL (Relocatable file)
   Machine:                           PowerPC64


   通过对比应该很清楚了,数据存储模式不同。这里需要明确的是环境一致性很重要。


   接下来来从头到尾实现一个Java调用C的一个解密方法。


  1.定义Java的本地方法(DataDecryt.java)

    

1
2
3
4
5
6
7
package  com.cto;
 
public  class  DataDecrypt{
 
     native  public  static  String decrypt(String data);
 
}


  2.通过javah命令生成头文件(dd.h)

1
./javah -classpath . -jni -o dd.h com.cto.DataDecrypt

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_cto_DataDecrypt */
 
#ifndef _Included_com_cto_DataDecrypt
#define _Included_com_cto_DataDecrypt
#ifdef __cplusplus
extern  "C"  {
#endif
/*
  * Class:     com_cto_DataDecrypt
  * Method:    decrypt
  * Signature: (Ljava/lang/String;)Ljava/lang/String;
  */
JNIEXPORT jstring JNICALL Java_com_cto_DataDecrypt_decrypt
   (JNIEnv *, jclass, jstring);
 
#ifdef __cplusplus
}
#endif
#endif


  3.定义使用静态库中的方法的头文件(dec.h)

1
2
3
4
5
6
7
8
9
10
11
12
13
#ifndef __DEC__
#define _DEC__
 
#ifdef __cplusplus
extern  "C" {
#endif
 
   int  ts_comm_dec( const  char * in ,  int  inlen,  char * out,  int * outlen);
 
#ifdef __cplusplus
}
#endif
#endif

    ts_comm_dec方法即为已经实现了的解密方法。


 4.创建实现dd.h头文件方法的cto.c文件,cto.c中将调用ts_common_dec方法

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <jni.h>
#include <stdio.h>
#include "dec.h"
#include "dd.h"
 
//about JNI http://doc.oracle.com/javase/1.5.0/docs/guide/jni/spec/functions.html
JNIEXPORT jstring JNICALL Java_com_cto_DataDecrypt_decrypt
   (JNIEnv *env, jclass jc, jstring data){
 
   char  out_str[48];
 
   const  char  *enc_str = (*env)->GetStringUTFChars(env, data, 0);
   const  jsize  enc_len = (*env)->GetStringUTFLength(env, data);
 
   int   out_len =  sizeof (out_str);
 
   ts_comm_dec(enc_str, enc_len, out_str, &out_len);
 
   jstring plain_text = (*env)->NewStringUTF(env, out_str);
 
   (*env)->ReleaseStringUTFChars(env, data, enc_str);
 
   return  plain_text;
}


 5.编写测试用例(TestDataDecrypt.java)

   这里加载的类库cto即为libcto.so。关于动态库静态库命名规则可百度之。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package  com.cto;
import  com.cto.DataDecrypt;
 
public  class  TestDataDecrypt{
 
     static  {
          System.loadLibrary( "cto" );
     }
 
     public  static  void  main(String [] args){
         String plainText= DataDecrypt.decrypt(args[ 0 ]);
 
         System.out.println(plainText);
         System.out.println( "解密之后的长度是:" +plainText.length());
     }
}


 6.编译动态库

 

1
gcc cto.c -shared -fPIC -lstdc++  -I~ /soft/jdk1 .6.0_45 /include  -I~ /soft/jdk1 .6.0_45 /include/linux  -I~ /native/enc   libtsbase.a  -o libcto.so


 7.运行测试


1
2
3
. /java  - cp  . -Djava.library.path=. com.cto.TestDataDecrypt Qt96BsMOKGjZ0oiqqhRqcA==               
13********1
解密之后的长度是:11

  解密后的结果和预期一致。


8.需要注意的事项

  命令:javac java javah是同一版本,有时候可能系统中有多个版本的JDK

  权限:从其它地方复制的文件,需要确认读写执行权限

  其它:即便按照文中方法来,同样会遇到各种各样的问题,需要多多查看和发现



本文转自 secondriver 51CTO博客,原文链接:http://blog.51cto.com/aiilive/1623404,如需转载请自行联系原作者

相关文章
|
2天前
|
Java 调度 开发者
Java 并发编程的探索与实践
【5月更文挑战第3天】在当今多核处理器普及的时代,并发编程已经成为提高程序性能的重要手段。本文将深入探讨 Java 并发编程的基本概念、原理及其在实际项目中的应用,帮助读者更好地理解和掌握 Java 并发编程技巧。
|
3天前
|
存储 前端开发 安全
13:会话跟踪技术Session的深度应用与实践-Java Web
13:会话跟踪技术Session的深度应用与实践-Java Web
16 3
|
3天前
|
存储 前端开发 搜索推荐
12:会话跟踪技术Cookie的深度应用与实践-Java Web
12:会话跟踪技术Cookie的深度应用与实践-Java Web
15 4
|
3天前
|
Java 调度 开发者
Java中的多线程编程:基础与实践
【5月更文挑战第2天】本文将深入探讨Java中的多线程编程,从基础概念到实际应用,为读者提供全面的理解和实践指导。我们将首先介绍线程的基本概念和重要性,然后详细解析Java中实现多线程的两种主要方式:继承Thread类和实现Runnable接口。接着,我们将探讨线程同步的问题,包括synchronized关键字和Lock接口的使用。最后,我们将通过一个实际的生产者-消费者模型来演示多线程编程的实践应用。
|
3天前
|
安全 Java 程序员
Java中的多线程编程:从理论到实践
【5月更文挑战第2天】 在计算机科学中,多线程编程是一项重要的技术,它允许多个任务在同一时间段内并发执行。在Java中,多线程编程是通过创建并管理线程来实现的。本文将深入探讨Java中的多线程编程,包括线程的概念、如何创建和管理线程、以及多线程编程的一些常见问题和解决方案。
12 1
|
4天前
|
并行计算 Java 数据处理
Java中的多线程编程:基础知识与实践
【5月更文挑战第1天】本文将深入探讨Java中的多线程编程,包括其基本概念、实现方式以及实际应用。我们将从理论和实践两个角度出发,详细解析线程的创建、启动、控制以及同步等关键问题,并通过实例代码演示如何在Java中有效地使用多线程。
|
4天前
|
Java 程序员
Java中的多线程编程:从理论到实践
【5月更文挑战第1天】 在现代计算机科学中,多线程编程是一个重要的概念,它允许程序员在同一程序中并行运行多个任务。Java作为一种广泛使用的编程语言,提供了一套丰富的多线程编程工具。本文将介绍Java中多线程编程的基本概念,包括线程的创建、启动、控制和同步,以及一些常见的多线程问题和解决方案。
|
4天前
|
存储 Java 程序员
Java中的多线程编程:基础知识与实践
【5月更文挑战第1天】在现代计算机科学中,多线程是一种重要的并行计算技术,允许多个执行流程并发运行。本文将深入探讨Java语言中的多线程编程,从基础概念到实际应用,帮助读者理解多线程的核心原理,并通过实例学习如何在Java中创建和管理线程。我们将涵盖线程的生命周期、同步机制以及如何利用高级类如Executor框架来优化多线程应用的性能。通过本文的学习,读者将具备设计和实现高效、稳定多线程Java应用程序的能力。
6 2
|
5天前
|
Java 调度 开发者
Java中的多线程编程:基础知识与实践
【4月更文挑战第30天】 在现代软件开发中,多线程编程是提高程序性能和响应能力的关键。Java作为一款广泛使用的编程语言,提供了丰富的多线程支持。本文将介绍Java多线程的基础概念、实现方法以及常见问题的解决策略。我们将从线程的创建和管理入手,逐步深入到同步机制、死锁避免以及高级并发工具类的应用。通过实例代码演示和理论分析,旨在帮助读者掌握Java多线程编程的核心技能,提升软件项目的并行处理能力。
|
5天前
|
安全 Java API
Java 8新特性概述及其对编程实践的影响
【4月更文挑战第30天】本文将详细讨论Java 8的新特性,包括Lambda表达式、Stream API以及Optional类等,并探讨这些新特性如何改变了Java编程的实践。我们将通过实例代码展示这些新特性的用法,并分析其对提高代码可读性和编写效率的影响。