node-java的使用及源码分析

上篇文章简单提了下node调用java的方法但也只属于基本提了下怎么输出helloworld的层度,这次将提供一些案例和源码分析让我们更好地了解如何使用node-java库。

前置知识:

1.桥接模式 http://c.biancheng.net/view/1364.html
2.nodejs和c++交互 http://nodejs.cn/api/addons.html
3.java和c++交互 https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/jniTOC.html
4.大概了解classloader机制

node-java原理:

&#x901A;&#x8FC7;c++&#x6865;&#x63A5; [js <-> c++(v8&#x3001;jvm) <-> java(jar)] &#x7684;&#x65B9;&#x5F0F;&#x8C03;&#x7528;jar
</-></->

案例地址:

https://gitee.com/lablelan/nodejs_demo.git
&#x6E90;&#x7801;&#x5728;nodeJava&#x76EE;&#x5F55;

案例演示:

demo0
&#x901A;&#x8FC7;&#x521B;&#x5EFA;&#x5B50;&#x8FDB;&#x7A0B;&#x65B9;&#x5F0F;&#x8C03;&#x7528;jar
demo1
&#x8C03;&#x7528;jar&#x8F93;&#x51FA;hello world
demo2
node&#x8FDB;&#x7A0B;&#x7F16;&#x7801;&#x4E3A;base64&#x7531;jvm&#x89E3;&#x7801;&#x8FD4;&#x56DE;&#x7ED3;&#x679C;
demo3
&#x8C03;&#x7528;jar&#x91CC;&#x7684;&#x6D4B;&#x8BD5;&#x63A5;&#x53E3;&#x5BFC;&#x51FA;&#x6570;&#x636E;
demo4
&#x6A21;&#x62DF;jar&#x91CC;&#x7684;&#x6D4B;&#x8BD5;&#x63A5;&#x53E3;&#x5BFC;&#x51FA;&#x6570;&#x636E;
demo5
&#x4F7F;&#x7528;jar&#x5305;&#x5BFC;&#x51FA;100&#x4E07;&#x884C;&#x6570;&#x636E;&#x5230;&#x672C;&#x5730;
demo6
&#x4F7F;&#x7528;express&#x548C;excel-export&#x5305;&#x5BFC;&#x51FA;&#x6570;&#x636E;
demo7
&#x4F7F;&#x7528;easyexcel&#x7684;jar&#x5305;&#x5BFC;&#x51FA;&#x6570;&#x636E;
demo8
&#x4F7F;&#x7528;promise&#x65B9;&#x5F0F;&#x5BFC;&#x51FA;&#x6570;&#x636E;
demo9
&#x8C03;&#x7528;sm4(&#x56FD;&#x5BC6;)&#x7684;jar&#x52A0;&#x89E3;&#x5BC6; https://www.javajike.com/book/hutool/chapter8/eb920b20c199717f34f28a89fcf6d620.html
demo10
&#x5C06;&#x4E1A;&#x52A1;&#x8DD1;&#x5728;worker&#x7EBF;&#x7A0B;&#x4E0A;

用到的jar包源码:

easyexcel: https://github.com/alibaba/easyexcel
utils: ./java/Util
myutils: ./java/myutils

node-java源码:

https://github.com/joeferner/node-java

核心源码目录:

.
&#x251C;&#x2500;&#x2500; index.js
&#x251C;&#x2500;&#x2500; lib
&#x2502;   &#x2514;&#x2500;&#x2500; nodeJavaBridge.js # index.js&#x5165;&#x53E3;&#x8FD9;&#x91CC;&#x5F00;&#x59CB;&#x521D;&#x59CB;&#x5316;java&#x5BF9;&#x8C61;
&#x2514;&#x2500;&#x2500; src
    &#x251C;&#x2500;&#x2500; java.cpp
    &#x251C;&#x2500;&#x2500; java.h # java&#x7C7B; &#x521D;&#x59CB;&#x5316;jvm&#x3001;&#x63D0;&#x4F9B;&#x4E00;&#x4E9B;&#x57FA;&#x7840;&#x7C7B;&#x578B;&#x5B9E;&#x4F8B;&#x5316;&#x63A5;&#x53E3;&#x548C;&#x8C03;&#x7528;&#x4EE3;&#x7406;
    &#x251C;&#x2500;&#x2500; javaObject.cpp
    &#x251C;&#x2500;&#x2500; javaObject.h # java&#x5BF9;&#x8C61;&#x4EE3;&#x7406; &#x6211;&#x4EEC;&#x7684;Sync&#x540E;&#x7F00;&#x7B49;&#x65B9;&#x6CD5;&#x5C31;&#x662F;&#x4ECE;&#x8FD9;&#x91CC;&#x88AB;&#x52A0;&#x8FDB;&#x5BF9;&#x8C61;&#x7684;&#x539F;&#x578B;&#x4E2D;
    &#x251C;&#x2500;&#x2500; javaScope.cpp
    &#x251C;&#x2500;&#x2500; javaScope.h # &#x7BA1;&#x7406;&#x5C40;&#x90E8;&#x5F15;&#x7528;&#x751F;&#x547D;&#x5468;&#x671F;
    &#x251C;&#x2500;&#x2500; methodCallBaton.cpp
    &#x251C;&#x2500;&#x2500; methodCallBaton.h # &#x7BA1;&#x7406;v8&#x548C;jvm&#x5BF9;&#x8C61;&#x5185;&#x5B58;&#xFF0C;&#x63D0;&#x5347;&#x5168;&#x5C40;&#x4F5C;&#x7528;&#x57DF;&#x7B49;
    &#x251C;&#x2500;&#x2500; nodeJavaBridge.cpp # &#x6865;&#x63A5;&#x66B4;&#x9732;&#x7ED9;v8
    &#x251C;&#x2500;&#x2500; node_NodeDynamicProxyClass.h # &#x52A8;&#x6001;&#x4EE3;&#x7406;&#x7C7B; &#x63D0;&#x4F9B;&#x591A;&#x7EBF;&#x7A0B;&#x529F;&#x80FD;
    &#x251C;&#x2500;&#x2500; utils.cpp
    &#x2514;&#x2500;&#x2500; utils.h # v8&#x548C;jvm&#x5BF9;&#x8C61;&#x8F6C;&#x6362;&#x5DE5;&#x5177;

带着问题去看源码:
1.如何查看一个对象的Java类型?

// Nan::SetPrototypeTemplate(funcTemplate, methodNameSync, methodCallSyncTemplate);
const ArrayList = java.import('java.util.ArrayList');
const arrayList = new ArrayList();
// 调用java的getClass方法可以获得类名称,这里的类名称被加工过,但可以大概看得出类路径
console.log(ArrayList, arrayList, arrayList.getClassSync())

2.如何知道对象/类有什么方法?

// 从源码上看可以得出java的方法会注入到原型中并带上后缀
const ArrayList = java.import('java.util.ArrayList');
const arrayList = new ArrayList();
console.log(arrayList.__proto__)

3.他是如何加载jar包的?

// 可以看出最终classpath会通过jni接口创建虚拟机时当做参数传入
Nan::SetAccessor(this->handle(), Nan::New("classpath").ToLocalChecked(), AccessorProhibitsOverwritingGetter, AccessorProhibitsOverwritingSetter);
classPath << *arrayItemStr;
vmOptions[0].optionString = strdup(classPath.str().c_str());
args.options = vmOptions;
JNI_CreateJavaVM(&jvmTemp, (void **)env, &args);

4.对象的生命周期是什么样的?

When you call a Java method through node-java, any arguments (V8/JavaScript objects) will be converted to Java objects on the v8 main thread via a call to v8ToJava (found in utils.cpp). The JavaScript object is not held on to and can be garbage collected by v8. If this is an async call, the reference count on the Java objects will be incremented. The Java method will be invoked in a node.js async thread (see uv_queue_work). When the method returns, the resulting object will be returned to the main v8 thread and converted to JavaScript objects via a call to javaToV8 and the Java object's reference count will then be decremented to allow for garbage collection. The resulting v8 object will then be returned to the callers callback function.

5.java和js对象如何转换?

// utils.cpp上有两个函数进行对象转换
jobjectArray v8ToJava(JNIEnv* env, Nan::NAN_METHOD_ARGS_TYPE args, int start, int end);
jobject v8ToJava(JNIEnv* env, v8::Local arg);

6.为什么对象的方法会自动带上Sync?

// 在javaObject.cpp可以看到javaToV8时会将java类上的方法放到v8对象上,带Sync为运行在v8主线程上,不带Sync则放进workerQueue等待其他线程处理(需要回调)
const char* methodNameSync = methodNameSyncStr.append(java->SyncSuffix()).c_str();

ps:

1.&#x9879;&#x76EE;&#x53EF;&#x4EE5;&#x4F7F;&#x7528; node-gyp rebuild &#x5BF9;c++&#x4EE3;&#x7801;&#x8FDB;&#x884C;&#x91CD;&#x65B0;&#x7F16;&#x8BD1;&#xFF0C;&#x5982;&#x9700;&#x91CD;&#x65B0;&#x7F16;&#x8BD1;&#x53EF;&#x4EE5;&#x6267;&#x884C; npm rebuild
2.&#x9047;&#x5230; java_dll_path.json &#x627E;&#x4E0D;&#x5230;&#x7684;&#x60C5;&#x51B5;&#x53EF;&#x4EE5;&#x5728; /node_modules/java &#x6267;&#x884C; node postInstall.js

Original: https://www.cnblogs.com/lablelan/p/15836882.html
Author: lable
Title: node-java的使用及源码分析

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/609876/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球