上篇文章簡單提了下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原理:
通過c++橋接 [js <-> c++(v8、jvm) <-> java(jar)] 的方式呼叫jar
案例地址:
https://gitee.com/lablelan/nodejs_demo.git
原始碼在nodeJava目錄
案例演示:
demo0
通過創建子行程方式呼叫jar
demo1
呼叫jar輸出hello world
demo2
node行程編碼為base64由jvm解碼回傳結果
demo3
呼叫jar里的測驗介面匯出資料
demo4
模擬jar里的測驗介面匯出資料
demo5
使用jar包匯出100萬行資料到本地
demo6
使用express和excel-export包匯出資料
demo7
使用easyexcel的jar包匯出資料
demo8
使用promise方式匯出資料
demo9
呼叫sm4(國密)的jar加解密 https://www.javajike.com/book/hutool/chapter8/eb920b20c199717f34f28a89fcf6d620.html
demo10
將業務跑在worker執行緒上
用到的jar包原始碼:
easyexcel: https://github.com/alibaba/easyexcel
utils: ./java/Util
myutils: ./java/myutils
node-java原始碼:
https://github.com/joeferner/node-java
核心原始碼目錄:
.
├── index.js
├── lib
│ └── nodeJavaBridge.js # index.js入口這里開始初始化java物件
└── src
├── java.cpp
├── java.h # java類 初始化jvm、提供一些基礎型別實體化介面和呼叫代理
├── javaObject.cpp
├── javaObject.h # java物件代理 我們的Sync后綴等方法就是從這里被加進物件的原型中
├── javaScope.cpp
├── javaScope.h # 管理區域參考生命周期
├── methodCallBaton.cpp
├── methodCallBaton.h # 管理v8和jvm物件記憶體,提升全域作用域等
├── nodeJavaBridge.cpp # 橋接暴露給v8
├── node_NodeDynamicProxyClass.h # 動態代理類 提供多執行緒功能
├── utils.cpp
└── utils.h # v8和jvm物件轉換工具
帶著問題去看原始碼:
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<v8::String>("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<v8::Value> arg);
6.為什么物件的方法會自動帶上Sync?
// 在javaObject.cpp可以看到javaToV8時會將java類上的方法放到v8物件上,帶Sync為運行在v8主執行緒上,不帶Sync則放進workerQueue等待其他執行緒處理(需要回呼)
const char* methodNameSync = methodNameSyncStr.append(java->SyncSuffix()).c_str();
ps:
1.專案可以使用 node-gyp rebuild 對c++代碼進行重新編譯,如需重新編譯可以執行 npm rebuild
2.遇到 java_dll_path.json 找不到的情況可以在 /node_modules/java 執行 node postInstall.js
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/421293.html
標籤:Java
上一篇:Go基礎知識梳理(二)
