diff --git a/QUARKUS_SUPPORT.md b/QUARKUS_SUPPORT.md
new file mode 100644
index 0000000000..c20fb2c28b
--- /dev/null
+++ b/QUARKUS_SUPPORT.md
@@ -0,0 +1,112 @@
+# WxJava Quarkus/GraalVM Native Image Support
+
+## 概述
+
+从 4.7.8.B 版本开始,WxJava 提供了对 Quarkus 和 GraalVM Native Image 的支持。这允许您将使用 WxJava 的应用程序编译为原生可执行文件,从而获得更快的启动速度和更低的内存占用。
+
+## 问题背景
+
+在之前的版本中,使用 Quarkus 构建 Native Image 时会遇到以下错误:
+
+```
+Error: Unsupported features in 3 methods
+Detailed message:
+Error: Detected an instance of Random/SplittableRandom class in the image heap.
+Instances created during image generation have cached seed values and don't behave as expected.
+The culprit object has been instantiated by the 'org.apache.http.impl.auth.NTLMEngineImpl' class initializer
+```
+
+## 解决方案
+
+为了解决这个问题,WxJava 进行了以下改进:
+
+### 1. Random 实例的延迟初始化
+
+所有 `java.util.Random` 实例都已改为延迟初始化,避免在类加载时创建:
+
+- `RandomUtils` - 使用双重检查锁定模式延迟初始化
+- `SignUtils` - 使用双重检查锁定模式延迟初始化
+- `WxCryptUtil` - 使用双重检查锁定模式延迟初始化
+
+### 2. Native Image 配置
+
+在 `weixin-java-common` 模块中添加了 GraalVM Native Image 配置文件:
+
+- `META-INF/native-image/com.github.binarywang/weixin-java-common/native-image.properties`
+ - 配置 Apache HttpClient 相关类在运行时初始化,避免在构建时创建 SecureRandom 实例
+
+- `META-INF/native-image/com.github.binarywang/weixin-java-common/reflect-config.json`
+ - 配置反射访问的类和方法
+
+## 使用方式
+
+### Quarkus 项目配置
+
+在您的 Quarkus 项目中使用 WxJava,只需正常引入依赖即可:
+
+```xml
+
+ com.github.binarywang
+ weixin-java-miniapp
+ 4.7.8.B
+
+```
+
+### 构建 Native Image
+
+使用 Quarkus 构建原生可执行文件:
+
+```bash
+./mvnw package -Pnative
+```
+
+或使用容器构建:
+
+```bash
+./mvnw package -Pnative -Dquarkus.native.container-build=true
+```
+
+### GraalVM Native Image
+
+如果直接使用 GraalVM Native Image 工具:
+
+```bash
+native-image --no-fallback \
+ -H:+ReportExceptionStackTraces \
+ -jar your-application.jar
+```
+
+WxJava 的配置文件会自动被 Native Image 工具识别和应用。
+
+## 测试验证
+
+建议在构建 Native Image 后进行以下测试:
+
+1. 验证应用程序可以正常启动
+2. 验证微信 API 调用功能正常
+3. 验证随机字符串生成功能正常
+4. 验证加密/解密功能正常
+
+## 已知限制
+
+- 本配置主要针对 Quarkus 3.x 和 GraalVM 22.x+ 版本进行测试
+- 如果使用其他 Native Image 构建工具(如 Spring Native),可能需要额外配置
+- 部分反射使用可能需要根据实际使用的 WxJava 功能进行调整
+
+## 问题反馈
+
+如果在使用 Quarkus/GraalVM Native Image 时遇到问题,请通过以下方式反馈:
+
+1. 在 [GitHub Issues](https://github.com/binarywang/WxJava/issues) 提交问题
+2. 提供详细的错误信息和 Native Image 构建日志
+3. 说明使用的 Quarkus 版本和 GraalVM 版本
+
+## 参考资料
+
+- [Quarkus 官方文档](https://quarkus.io/)
+- [GraalVM Native Image 文档](https://www.graalvm.org/latest/reference-manual/native-image/)
+- [Quarkus Tips for Writing Native Applications](https://quarkus.io/guides/writing-native-applications-tips)
+
+## 贡献
+
+欢迎提交 PR 完善 Quarkus/GraalVM 支持!如果您发现了新的兼容性问题或有改进建议,请参考 [代码贡献指南](CONTRIBUTING.md)。
diff --git a/README.md b/README.md
index 12b516c1b7..0b86319b66 100644
--- a/README.md
+++ b/README.md
@@ -65,13 +65,14 @@
1. [`WxJava` 荣获 `GitCode` 2024年度十大开源社区奖项](https://mp.weixin.qq.com/s/wM_UlMsDm3IZ1CPPDvcvQw)。
2. 项目合作洽谈请联系微信`binary0000`(在微信里自行搜索并添加好友,请注明来意,如有关于SDK问题需讨论请参考下文入群讨论,不要加此微信)。
3. **2024-12-30 发布 [【4.7.0正式版】](https://mp.weixin.qq.com/s/_7k-XLYBqeJJhvHWCsdT0A)**!
-4. 贡献源码可以参考视频:[【贡献源码全过程(上集)】](https://mp.weixin.qq.com/s/3xUZSATWwHR_gZZm207h7Q)、[【贡献源码全过程(下集)】](https://mp.weixin.qq.com/s/nyzJwVVoYSJ4hSbwyvTx9A) ,友情提供:[程序员小山与Bug](https://space.bilibili.com/473631007)
-5. 新手重要提示:本项目仅是一个SDK开发工具包,未提供Web实现,建议使用 `maven` 或 `gradle` 引用本项目即可使用本SDK提供的各种功能,详情可参考 **[【Demo项目】](demo.md)** 或本项目中的部分单元测试代码;
-6. 微信开发新手请务必阅读【开发文档】([Gitee Wiki](https://gitee.com/binary/weixin-java-tools/wikis/Home) 或者 [Github Wiki](https://github.com/binarywang/WxJava/wiki))的常见问题部分,可以少走很多弯路,节省不少时间。
-7. 技术交流群:想获得QQ群/微信群/钉钉企业群等信息的同学,请使用微信扫描上面的微信公众号二维码关注 `WxJava` 后点击相关菜单即可获取加入方式,同时也可以在微信中搜索 `weixin-java-tools` 或 `WxJava` 后选择正确的公众号进行关注,该公众号会及时通知SDK相关更新信息,并不定期分享微信Java开发相关技术知识;
-8. 钉钉技术交流群:`32206329`(技术交流2群), `30294972`(技术交流1群,目前已满),`35724728`(通知群,实时通知Github项目变更记录)。
-9. 微信开发新手或者Java开发新手在群内提问或新开Issue提问前,请先阅读[【提问的智慧】](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/master/README-zh_CN.md),并确保已查阅过 [【开发文档Wiki】](https://github.com/binarywang/WxJava/wiki) ,避免浪费大家的宝贵时间;
-10. 寻求帮助时需贴代码或大长串异常信息的,请利用 http://paste.ubuntu.com
+4. **从 4.7.8.B 版本开始支持 Quarkus/GraalVM Native Image,详见 [【Quarkus 支持文档】](QUARKUS_SUPPORT.md)**。
+5. 贡献源码可以参考视频:[【贡献源码全过程(上集)】](https://mp.weixin.qq.com/s/3xUZSATWwHR_gZZm207h7Q)、[【贡献源码全过程(下集)】](https://mp.weixin.qq.com/s/nyzJwVVoYSJ4hSbwyvTx9A) ,友情提供:[程序员小山与Bug](https://space.bilibili.com/473631007)
+6. 新手重要提示:本项目仅是一个SDK开发工具包,未提供Web实现,建议使用 `maven` 或 `gradle` 引用本项目即可使用本SDK提供的各种功能,详情可参考 **[【Demo项目】](demo.md)** 或本项目中的部分单元测试代码;
+7. 微信开发新手请务必阅读【开发文档】([Gitee Wiki](https://gitee.com/binary/weixin-java-tools/wikis/Home) 或者 [Github Wiki](https://github.com/binarywang/WxJava/wiki))的常见问题部分,可以少走很多弯路,节省不少时间。
+8. 技术交流群:想获得QQ群/微信群/钉钉企业群等信息的同学,请使用微信扫描上面的微信公众号二维码关注 `WxJava` 后点击相关菜单即可获取加入方式,同时也可以在微信中搜索 `weixin-java-tools` 或 `WxJava` 后选择正确的公众号进行关注,该公众号会及时通知SDK相关更新信息,并不定期分享微信Java开发相关技术知识;
+9. 钉钉技术交流群:`32206329`(技术交流2群), `30294972`(技术交流1群,目前已满),`35724728`(通知群,实时通知Github项目变更记录)。
+10. 微信开发新手或者Java开发新手在群内提问或新开Issue提问前,请先阅读[【提问的智慧】](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/master/README-zh_CN.md),并确保已查阅过 [【开发文档Wiki】](https://github.com/binarywang/WxJava/wiki) ,避免浪费大家的宝贵时间;
+11. 寻求帮助时需贴代码或大长串异常信息的,请利用 http://paste.ubuntu.com
--------------------------------
### 其他说明
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/RandomUtils.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/RandomUtils.java
index bbb11992bc..a9017c0d16 100644
--- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/RandomUtils.java
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/RandomUtils.java
@@ -4,12 +4,24 @@ public class RandomUtils {
private static final String RANDOM_STR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
- private static final java.util.Random RANDOM = new java.util.Random();
+ private static volatile java.util.Random random;
+
+ private static java.util.Random getRandom() {
+ if (random == null) {
+ synchronized (RandomUtils.class) {
+ if (random == null) {
+ random = new java.util.Random();
+ }
+ }
+ }
+ return random;
+ }
public static String getRandomStr() {
StringBuilder sb = new StringBuilder();
+ java.util.Random r = getRandom();
for (int i = 0; i < 16; i++) {
- sb.append(RANDOM_STR.charAt(RANDOM.nextInt(RANDOM_STR.length())));
+ sb.append(RANDOM_STR.charAt(r.nextInt(RANDOM_STR.length())));
}
return sb.toString();
}
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java
index 0a40d0e93c..4039580cf1 100755
--- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java
@@ -37,6 +37,19 @@ public class WxCryptUtil {
private static final Base64 BASE64 = new Base64();
private static final Charset CHARSET = StandardCharsets.UTF_8;
+ private static volatile Random random;
+
+ private static Random getRandom() {
+ if (random == null) {
+ synchronized (WxCryptUtil.class) {
+ if (random == null) {
+ random = new Random();
+ }
+ }
+ }
+ return random;
+ }
+
private static final ThreadLocal BUILDER_LOCAL = ThreadLocal.withInitial(() -> {
try {
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
@@ -109,10 +122,10 @@ private static int bytesNetworkOrder2Number(byte[] bytesInNetworkOrder) {
*/
private static String genRandomStr() {
String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
- Random random = new Random();
+ Random r = getRandom();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 16; i++) {
- int number = random.nextInt(base.length());
+ int number = r.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
diff --git a/weixin-java-common/src/main/resources/META-INF/native-image/com.github.binarywang/weixin-java-common/native-image.properties b/weixin-java-common/src/main/resources/META-INF/native-image/com.github.binarywang/weixin-java-common/native-image.properties
new file mode 100644
index 0000000000..e1e601713f
--- /dev/null
+++ b/weixin-java-common/src/main/resources/META-INF/native-image/com.github.binarywang/weixin-java-common/native-image.properties
@@ -0,0 +1,4 @@
+Args = --initialize-at-run-time=org.apache.http.impl.auth.NTLMEngineImpl \
+ --initialize-at-run-time=org.apache.http.impl.auth.NTLMEngine \
+ --initialize-at-run-time=org.apache.http.impl.auth.KerberosScheme \
+ --initialize-at-run-time=org.apache.http.impl.auth.SPNegoScheme
diff --git a/weixin-java-common/src/main/resources/META-INF/native-image/com.github.binarywang/weixin-java-common/reflect-config.json b/weixin-java-common/src/main/resources/META-INF/native-image/com.github.binarywang/weixin-java-common/reflect-config.json
new file mode 100644
index 0000000000..3bf76c8dab
--- /dev/null
+++ b/weixin-java-common/src/main/resources/META-INF/native-image/com.github.binarywang/weixin-java-common/reflect-config.json
@@ -0,0 +1,14 @@
+[
+ {
+ "name": "me.chanjar.weixin.common.util.RandomUtils",
+ "methods": [
+ {"name": "getRandomStr", "parameterTypes": []}
+ ]
+ },
+ {
+ "name": "me.chanjar.weixin.common.util.crypto.WxCryptUtil",
+ "allDeclaredConstructors": true,
+ "allDeclaredMethods": true,
+ "allDeclaredFields": true
+ }
+]
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/SignUtils.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/SignUtils.java
index 7065d06383..37a1ef2efd 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/SignUtils.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/util/SignUtils.java
@@ -33,6 +33,19 @@ public static String genRandomStr() {
return genRandomStr(32);
}
+ private static volatile Random random;
+
+ private static Random getRandom() {
+ if (random == null) {
+ synchronized (SignUtils.class) {
+ if (random == null) {
+ random = new Random();
+ }
+ }
+ }
+ return random;
+ }
+
/**
* 生成随机字符串
*
@@ -41,10 +54,10 @@ public static String genRandomStr() {
*/
public static String genRandomStr(int length) {
String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
- Random random = new Random();
+ Random r = getRandom();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < length; i++) {
- int number = random.nextInt(base.length());
+ int number = r.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();