Java本地方法调用

JNI介绍

JNI是Java Native Interface的缩写,中文为JAVA本地调用。从Java 1.1 开始,Java Native Interface (JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。

使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的,比如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少保证本地代码能工作在任何Java 虚拟机实现下。

JNI的使用场景

  1. Java应用需要与本地其它非Java应用交互
  2. 遇到性能瓶颈,通常本地代码执行效率要优于Java代码
  3. JDK提供的功能不足以帮我们实现需要的功能

JNI的问题

  1. 因为JNI有一个Native这个特点,一点有项目用了JNI,也就说明这个项目基本不能跨平台了。
  2. JNI调用是相当慢的,在实际使用的之前一定要先想明白是否有这个必要。
  3. 因为C++和C这样的语言非常灵活,一不小心就容易出错,比如代码就没有写析构字符串释放内存,就存在内存泄漏的问题,对于java developer来说因为有了GC 垃圾回收机制,所以大多数人没有写析构函数这样的概念。所以JNI也会增加程序中的风险,增大程序的不稳定性

JNI实战

主要分为以下几个步骤

  • 编写带有native声明的方法的java类

  • 使用javac命令编译所编写的java类

  • 使用javah -jni 类的全限定名 生成扩展名为h的头文件

  • 使用C/C++实现本地方法

  • 将C/C++编译的文件生成动态连接库(dll文件)

  • 在java项目中引入生成的C/C++库文件

1.编写java程序

1.这里以HelloWorld为例,java代码如下:

package com.aliencat.javabase.jni;

public class HelloJni {

    public native String sayHello(String name);

    static{
        System.loadLibrary("DLL1");
    }

    public static void main(String[] args) {
        System.out.println(new HelloJni().sayHello("hello"));
    }
}

声明native方法:如果你想将一个方法做为一个本地方法的话,那么你就必须声明改方法为 native的,并且不能实现。

Load动态库:System.loadLibrary(“hello”);加载动态库(我们可以这样理解:我们的方法 displayHelloWorld()没有实现,但是我们在下面就直接使用了,所以必须在使用之前对它进行初始化)这里一般是以static块进行加载的。同时需要注意的是System.loadLibrary();的参数“hello”是动态库的名字。

2.然后进行编译

打开控制台,进入java目录下输入以下指令:

javah -jni com.aliencat.javabase.jni.HelloJni

即可生成一个一个头文件com_aliencat_javabase_jni_HelloJni.h,内容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class com_aliencat_javabase_jni_HelloJni */

#ifndef _Included_com_aliencat_javabase_jni_HelloJni
#define _Included_com_aliencat_javabase_jni_HelloJni
#ifdef __cplusplus
extern "C" {
#endif
	/*
	 * Class:     com_aliencat_javabase_jni_HelloJni
	 * Method:    sayHello
	 * Signature: (Ljava/lang/String;)V
	 */
	JNIEXPORT jstring JNICALL Java_com_aliencat_javabase_jni_HelloJni_sayHello
	(JNIEnv*, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif

这个h文件相当于我们在java里面的接口,这里声明了一个Java_com_aliencat_javabase_jni_HelloJni_sayHello
(JNIEnv*, jobject, jstring);方法,然后在我们的本地方法里面实现这个方法,也就是说我们在编写C/C++程序的时候所使用的方法名必须和这里的一致。其中jobject为java的this引用,而jstring即是传入的字符串参数。

3.使用Visual Studio编写c/c++本地方法实现

下载安装Visual studi

下载地址:https://visualstudio.microsoft.com/zh-hant/vs/whatsnew/

然后安装选择以下组件即可:

在这里插入图片描述
新建项目

选择创建动态链接库

在这里插入图片描述

引入头文件

JDK目录的include目录下有一个jni.h的文件,include的win32目录下有个jni_md.h文件,还有java类生成C头文件HelloJni.h,也一起拷贝到DLL工程的目录下。主要在目录的头文件上右键选择添加->现有项:

在这里插入图片描述

用visual studio编写C代码

hello.cpp即为我创建的c++文件。

本地方法和javah命令生成的头文件里面声明的方法名相同。
代码如下:

#include "pch.h"
#include "com_aliencat_javabase_jni_HelloJni.h"
#include <iostream>
#include <stdio.h>


JNIEXPORT jstring JNICALL Java_com_aliencat_javabase_jni_HelloJni_sayHello
(JNIEnv* env, jobject, jstring jstr) {

    //将jstring类型转换成char*类型
    char* rtn = NULL;
    jclass clsstring = env->FindClass("java/lang/String");
    jstring strencode = env->NewStringUTF("GB2312");
    jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
    jbyteArray barr = (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
    jsize alen = env->GetArrayLength(barr);
    jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
    if (alen > 0)
    {
        rtn = (char*)malloc(alen + 1); //new char[alen+1];  
        memcpy(rtn, ba, alen);
        rtn[alen] = 0;
    }
    env->ReleaseByteArrayElements(barr, ba, 0);

    printf("%s ", rtn);
    printf("this is C++ print");
    const char* str = "this is come from C++ ";
    return env->NewStringUTF(str);
}

4.生成动态库

首先选中项目根目录后右键点击属性,选择配置管理器,配置如下:

在这里插入图片描述

选中项目根目录后右键点击生成即可,控制台输出如下即表示成功:

在这里插入图片描述

5.在java项目中引入本地库

以idea为例:

在这里插入图片描述

选择你的vs编译输出的文件夹(看你的vs控制台输出的位置)即可,我的是在以下面路径下:

在这里插入图片描述

6.运行程序 java HelloJni.

加载本地库后即可使用

    static{
        System.loadLibrary("DLL1");
    }

执行main方法后输出如下:

this is come from C++ 
hello this is C++ print

JNI数据类型

Java类型本地类型(JNI)描述
boolean(布尔型)jboolean无符号8比特
byte(字节型)jbyte有符号8比特
char(字符型)jchar无符号16比特
short(短整型)jshort有符号16比特
int(整型)jint有符号32比特
long(长整型)jlong有符号64比特
float(浮点型)jfloat32比特
double(双精度浮点型)jdouble64比特
void(空型)voidN/A
  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
以下是在Java中本地调用Dubbo代码的示例: 1. 引入Dubbo相关依赖 在项目的pom.xml文件中,添加以下依赖: ``` <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.6.5</version> </dependency> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.9</version> </dependency> ``` 2. 配置Dubbo 在项目中添加Dubbo的配置文件,例如dubbo.properties,内容如下: ``` # 注册中心地址 dubbo.registry.address=zookeeper://127.0.0.1:2181 # 服务协议 dubbo.protocol.name=dubbo dubbo.protocol.port=20880 ``` 3. 编写Dubbo服务接口 定义Dubbo服务的接口,例如: ``` public interface UserService { User getUserById(long id); } ``` 其中,User是一个自定义的实体类。 4. 实现Dubbo服务接口 实现UserService接口中定义的方法,例如: ``` public class UserServiceImpl implements UserService { public User getUserById(long id) { // 从数据库中获取用户信息 User user = userDao.getUserById(id); return user; } } ``` 其中,userDao是一个自定义的数据访问对象。 5. 本地调用Dubbo服务 (1)使用Dubbo的API 在Java代码中,通过Dubbo的API获取Dubbo服务的代理对象,然后调用代理对象的方法。例如: ``` public class UserServiceTest { public static void main(String[] args) { // 初始化Dubbo ApplicationConfig application = new ApplicationConfig(); application.setName("user-service-consumer"); RegistryConfig registry = new RegistryConfig(); registry.setAddress("zookeeper://127.0.0.1:2181"); registry.setClient("curator"); DubboBootstrap.getInstance() .application(application) .registry(registry) .start(); // 获取Dubbo服务代理对象 ReferenceConfig<UserService> reference = new ReferenceConfig<>(); reference.setInterface(UserService.class); reference.setVersion("1.0.0"); UserService userService = reference.get(); // 调用Dubbo服务方法 User user = userService.getUserById(123); System.out.println(user.toString()); // 关闭Dubbo DubboBootstrap.getInstance().stop(); } } ``` (2)使用Spring的注解 在Java类中使用@Reference注解引入Dubbo服务,然后直接调用注入的服务对象的方法。例如: ``` @Service public class UserServiceConsumer { @Reference(version = "1.0.0") private UserService userService; public User getUserById(long id) { User user = userService.getUserById(id); return user; } } ``` 以上是本地调用Dubbo服务的示例,具体实现方式可以根据项目需求进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值