diff --git a/sofa-boot-project/sofa-boot/pom.xml b/sofa-boot-project/sofa-boot/pom.xml
index 46ea0a1d2..b4385cc3e 100644
--- a/sofa-boot-project/sofa-boot/pom.xml
+++ b/sofa-boot-project/sofa-boot/pom.xml
@@ -46,6 +46,12 @@
test
+
+ org.mockito
+ mockito-inline
+ test
+
+
diff --git a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/Initializer/AutoModuleExportApplicationContextInitializer.java b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/Initializer/AutoModuleExportApplicationContextInitializer.java
new file mode 100644
index 000000000..3460426c9
--- /dev/null
+++ b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/Initializer/AutoModuleExportApplicationContextInitializer.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.boot.Initializer;
+
+import com.alipay.sofa.boot.util.ModuleUtil;
+import org.springframework.context.ApplicationContextInitializer;
+import org.springframework.context.ConfigurableApplicationContext;
+
+/**
+ * @author huazhongming
+ * @since 4.4.0
+ */
+public class AutoModuleExportApplicationContextInitializer
+ implements
+ ApplicationContextInitializer {
+
+ private static final String AUTO_MODULE_JDK_ENABLE_KEY = "sofa.boot.auto.module.export.jdk.enable";
+ private static final String AUTO_MODULE_ALL_ENABLE_KEY = "sofa.boot.auto.module.export.all.enable";
+
+ @Override
+ public void initialize(ConfigurableApplicationContext applicationContext) {
+ if (isEnable(applicationContext, AUTO_MODULE_ALL_ENABLE_KEY, "false")) {
+ ModuleUtil.exportAllModulePackageToAll();
+ } else if (isEnable(applicationContext, AUTO_MODULE_JDK_ENABLE_KEY, "true")) {
+ ModuleUtil.exportAllJDKModulePackageToAll();
+ }
+ }
+
+ protected boolean isEnable(ConfigurableApplicationContext applicationContext, String key,
+ String defaultValue) {
+ String switchStr = applicationContext.getEnvironment().getProperty(key, defaultValue);
+ return Boolean.parseBoolean(switchStr);
+ }
+}
diff --git a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java
new file mode 100644
index 000000000..5e2c20146
--- /dev/null
+++ b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java
@@ -0,0 +1,253 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.boot.util;
+
+import com.alipay.sofa.boot.log.SofaBootLoggerFactory;
+import org.slf4j.Logger;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.reflect.Field;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * @author huazhongming
+ * @since 4.4.0
+ */
+public class ModuleUtil {
+
+ private static final Logger LOGGER = SofaBootLoggerFactory
+ .getLogger(ModuleUtil.class);
+
+ private static final MethodHandle implAddOpensToAllUnnamed;
+
+ private static final MethodHandle implAddOpens;
+
+ private static final MethodHandle implAddExportsToAllUnnamed;
+
+ private static final MethodHandle implAddExports;
+
+ private static final Map nameToModules;
+
+ private static final AtomicBoolean isExported = new AtomicBoolean(false);
+
+ static {
+ implAddOpensToAllUnnamed = createModuleMethodHandle("implAddOpensToAllUnnamed",
+ String.class);
+ implAddOpens = createModuleMethodHandle("implAddOpens", String.class);
+ implAddExportsToAllUnnamed = createModuleMethodHandle("implAddExportsToAllUnnamed",
+ String.class);
+ implAddExports = createModuleMethodHandle("implAddExports", String.class);
+ nameToModules = getNameToModule();
+ }
+
+ /**
+ * Export all JDK module packages to all.
+ */
+ public static void exportAllJDKModulePackageToAll() {
+ try {
+ if (isExported.compareAndSet(false,true) && nameToModules != null) {
+ nameToModules.forEach((name, module) -> module.getPackages().forEach(pkgName -> {
+ if (isJDKModulePackage(pkgName)) {
+ addOpensToAll(module, pkgName);
+ addExportsToAll(module, pkgName);
+ }
+ }));
+ }
+ } catch (Throwable t) {
+ LOGGER.error("Failed to export all JDK module package to all", t);
+ }
+ }
+
+ private static boolean isJDKModulePackage(String modulePackageName) {
+ return modulePackageName.startsWith("java.") || modulePackageName.startsWith("jdk.");
+ }
+
+ /**
+ * Export all module packages to all.
+ */
+ public static void exportAllModulePackageToAll() {
+ try {
+ if (isExported.compareAndSet(false,true) && nameToModules != null) {
+ nameToModules.forEach((name, module) -> module.getPackages().forEach(pkgName -> {
+ addOpensToAll(module, pkgName);
+ addExportsToAll(module, pkgName);
+ }));
+ }
+ } catch (Throwable t) {
+ LOGGER.error("Failed to export all module package to all", t);
+ }
+ }
+
+ /**
+ * Updates this module to open a package to all unnamed modules.
+ *
+ * @param moduleName
+ * @param packageName
+ */
+ public static boolean addOpensToAllUnnamed(String moduleName, String packageName) {
+ return invokeModuleMethod(implAddOpensToAllUnnamed, moduleName, packageName);
+ }
+
+ /**
+ * Updates this module to open a package to all unnamed modules.
+ *
+ * @param module
+ * @param packageName
+ */
+ public static boolean addOpensToAllUnnamed(Module module, String packageName) {
+ return invokeModuleMethod(implAddOpensToAllUnnamed, module, packageName);
+ }
+
+ /**
+ * Updates this module to export a package to all unnamed modules.
+ *
+ * @param moduleName
+ * @param packageName
+ */
+ public static boolean addExportsToAllUnnamed(String moduleName, String packageName) {
+ return invokeModuleMethod(implAddExportsToAllUnnamed, moduleName, packageName);
+ }
+
+ /**
+ * Updates this module to export a package to all unnamed modules.
+ *
+ * @param module
+ * @param packageName
+ */
+ public static boolean addExportsToAllUnnamed(Module module, String packageName) {
+ return invokeModuleMethod(implAddExportsToAllUnnamed, module, packageName);
+ }
+
+ /**
+ * Updates this module to open a package to another module.
+ *
+ * @param moduleName
+ * @param packageName
+ */
+ public static boolean addOpensToAll(String moduleName, String packageName) {
+
+ return invokeModuleMethod(implAddOpens, moduleName, packageName);
+ }
+
+ /**
+ * Updates this module to open a package to another module.
+ *
+ * @param module
+ * @param packageName
+ */
+ public static boolean addOpensToAll(Module module, String packageName) {
+
+ return invokeModuleMethod(implAddOpens, module, packageName);
+ }
+
+ /**
+ * Updates this module to export a package unconditionally.
+ * @param moduleName
+ * @param packageName
+ */
+ public static boolean addExportsToAll(String moduleName, String packageName) {
+ return invokeModuleMethod(implAddExports, moduleName, packageName);
+ }
+
+ /**
+ * Updates this module to export a package unconditionally.
+ * @param module
+ * @param packageName
+ */
+ public static boolean addExportsToAll(Module module, String packageName) {
+ return invokeModuleMethod(implAddExports, module, packageName);
+ }
+
+ /**
+ * invoke ModuleLayer method
+ *
+ * @param method
+ * @param moduleName
+ * @param packageName
+ * @return
+ */
+ public static boolean invokeModuleMethod(MethodHandle method, String moduleName,
+ String packageName) {
+ Optional findModule = ModuleLayer.boot().findModule(moduleName);
+ if (findModule.isPresent()) {
+ try {
+ return invokeModuleMethod(method, findModule.get(), packageName);
+ } catch (Throwable t) {
+ LOGGER.error("Failed to invoke ModuleLayer method: {}", method, t);
+ }
+ }
+ return false;
+ }
+
+ public static boolean invokeModuleMethod(MethodHandle method, Module module, String packageName) {
+ try {
+ method.invoke(module, packageName);
+ return true;
+ } catch (Throwable t) {
+ LOGGER.error("Failed to invoke Module method: {}", method, t);
+ }
+ return false;
+ }
+
+ /**
+ * create MethodHandle from Module
+ *
+ * @param methodName
+ * @param parameterTypes
+ * @return MethodHandle
+ */
+ private static MethodHandle createModuleMethodHandle(String methodName,
+ Class>... parameterTypes) {
+ try {
+ return UnsafeUtil.implLookup().unreflect(
+ Module.class.getDeclaredMethod(methodName, parameterTypes));
+ } catch (Throwable t) {
+ LOGGER.error("Failed to create Module method handle: {}", methodName, t);
+ }
+ return null;
+ }
+
+ /**
+ * Get ModuleLayer.bootLayer field value
+ *
+ * @param fieldName
+ * @return field value
+ */
+ private static Object getModuleLayerFieldsValue(String fieldName) {
+ ModuleLayer moduleLayer = ModuleLayer.boot();
+ try {
+ Class moduleLayerClass = ModuleLayer.class;
+ Field field = moduleLayerClass.getDeclaredField(fieldName);
+ return UnsafeUtil.implLookup().unreflectVarHandle(field).get(moduleLayer);
+ } catch (Throwable t) {
+ LOGGER.error("Failed to get ModuleLayer field value: {}", fieldName, t);
+ }
+ return null;
+ }
+
+ /**
+ * Get all modules from System.bootLayer
+ *
+ * @return modules
+ */
+ @SuppressWarnings("unchecked")
+ public static Map getNameToModule() {
+ return (Map) getModuleLayerFieldsValue("nameToModule");
+ }
+}
diff --git a/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/UnsafeUtil.java b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/UnsafeUtil.java
new file mode 100644
index 000000000..0bbbca199
--- /dev/null
+++ b/sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/UnsafeUtil.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.boot.util;
+
+import sun.misc.Unsafe;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Field;
+
+/**
+ * @author huazhongming
+ * @since 4.4.0
+ */
+public class UnsafeUtil {
+ private static Unsafe UNSAFE;
+ private static MethodHandles.Lookup IMPL_LOOKUP;
+
+ public static Unsafe unsafe() {
+ if (UNSAFE == null) {
+ Unsafe unsafe = null;
+ try {
+ Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
+ theUnsafeField.setAccessible(true);
+ unsafe = (Unsafe) theUnsafeField.get(null);
+ } catch (Throwable ignored) {
+ }
+ UNSAFE = unsafe;
+ }
+
+ return UNSAFE;
+ }
+
+ public static MethodHandles.Lookup implLookup() {
+ if (IMPL_LOOKUP == null) {
+ Class lookupClass = MethodHandles.Lookup.class;
+
+ try {
+ Field implLookupField = lookupClass.getDeclaredField("IMPL_LOOKUP");
+ long offset = unsafe().staticFieldOffset(implLookupField);
+ IMPL_LOOKUP = (MethodHandles.Lookup) unsafe().getObject(
+ unsafe().staticFieldBase(implLookupField), offset);
+ } catch (Throwable ignored) {
+ }
+ }
+ return IMPL_LOOKUP;
+ }
+}
diff --git a/sofa-boot-project/sofa-boot/src/main/resources/META-INF/spring.factories b/sofa-boot-project/sofa-boot/src/main/resources/META-INF/spring.factories
index 1aea1f8c4..9ed35930a 100644
--- a/sofa-boot-project/sofa-boot/src/main/resources/META-INF/spring.factories
+++ b/sofa-boot-project/sofa-boot/src/main/resources/META-INF/spring.factories
@@ -15,4 +15,5 @@ org.springframework.boot.SpringApplicationRunListener=\
# Initializers
org.springframework.context.ApplicationContextInitializer=\
- com.alipay.sofa.boot.compatibility.CompatibilityVerifierApplicationContextInitializer
+ com.alipay.sofa.boot.compatibility.CompatibilityVerifierApplicationContextInitializer,\
+ com.alipay.sofa.boot.Initializer.AutoModuleExportApplicationContextInitializer
diff --git a/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/initializer/AutoModuleExportApplicationContextInitializerTests.java b/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/initializer/AutoModuleExportApplicationContextInitializerTests.java
new file mode 100644
index 000000000..e9d30fa48
--- /dev/null
+++ b/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/initializer/AutoModuleExportApplicationContextInitializerTests.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.boot.initializer;
+
+import com.alipay.sofa.boot.Initializer.AutoModuleExportApplicationContextInitializer;
+import com.alipay.sofa.boot.util.ModuleUtil;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.times;
+
+/**
+ * @author huazhongming
+ * @since 4.4.0
+ */
+public class AutoModuleExportApplicationContextInitializerTests {
+
+ private ApplicationContextRunner contextRunner;
+
+ @BeforeEach
+ void setUp() {
+ contextRunner = new ApplicationContextRunner()
+ .withInitializer(new AutoModuleExportApplicationContextInitializer());
+ }
+
+ @Test
+ void jdkDefaultTrue(){
+
+ try (MockedStatic mockedStatic = mockStatic(ModuleUtil.class)) {
+ contextRunner.withPropertyValues().run(applicationContext -> {});
+ mockedStatic.verify(ModuleUtil::exportAllJDKModulePackageToAll, times(1));
+ }
+ }
+
+ @Test
+ void allDefaultFalse(){
+ try (MockedStatic mockedStatic = mockStatic(ModuleUtil.class)) {
+ contextRunner.withPropertyValues().run(applicationContext -> {});
+ mockedStatic.verify(ModuleUtil::exportAllModulePackageToAll, times(0));
+ }
+ }
+
+ @Test
+ void jdkDisable(){
+
+ try (MockedStatic mockedStatic = mockStatic(ModuleUtil.class)) {
+ contextRunner.withPropertyValues("sofa.boot.auto.module.export.jdk.enable=false").run(applicationContext -> {});
+ mockedStatic.verify(ModuleUtil::exportAllJDKModulePackageToAll, times(0));
+ }
+ }
+
+ @Test
+ void allEnable(){
+ try (MockedStatic mockedStatic = mockStatic(ModuleUtil.class)) {
+ contextRunner.withPropertyValues("sofa.boot.auto.module.export.all.enable=true").run(applicationContext -> {});
+ mockedStatic.verify(ModuleUtil::exportAllModulePackageToAll, times(1));
+ }
+ }
+}
diff --git a/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/util/ModuleUtilTests.java b/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/util/ModuleUtilTests.java
new file mode 100644
index 000000000..d574a05ff
--- /dev/null
+++ b/sofa-boot-project/sofa-boot/src/test/java/com/alipay/sofa/boot/util/ModuleUtilTests.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alipay.sofa.boot.util;
+
+import org.junit.jupiter.api.Test;
+
+import java.lang.reflect.InaccessibleObjectException;
+import java.lang.reflect.Method;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * @author huazhongming
+ * @since 4.4.0
+ */
+public class ModuleUtilTests {
+
+ @Test
+ public void testExportAllJDKModulePackageToAll() throws NoSuchMethodException {
+
+ Exception exception = assertThrows(InaccessibleObjectException.class,() -> {
+ Method newByteChannel0Method = Files.class.getDeclaredMethod("provider", Path.class);
+ newByteChannel0Method.setAccessible(true);
+ });
+
+ assertTrue(exception.getMessage().contains("module java.base does not \"opens java.nio.file\" to unnamed module"));
+
+ ModuleUtil.exportAllJDKModulePackageToAll();
+
+ Method newByteChannel0Method = Files.class.getDeclaredMethod("provider", Path.class);
+ newByteChannel0Method.setAccessible(true);
+ }
+}