diff --git a/pom.xml b/pom.xml
index cc04724b0..8fb46e19c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -167,6 +167,13 @@
${redisson.version}
+
+
+ com.ctrip.framework.apollo
+ apollo-client
+ 1.7.0
+
+
org.apache.skywalking
diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollo/internals/DBConfigRepository.java b/src/main/java/cn/iocoder/dashboard/framework/apollo/internals/DBConfigRepository.java
new file mode 100644
index 000000000..38743080a
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/framework/apollo/internals/DBConfigRepository.java
@@ -0,0 +1,95 @@
+package cn.iocoder.dashboard.framework.apollo.internals;
+
+import com.ctrip.framework.apollo.Apollo;
+import com.ctrip.framework.apollo.build.ApolloInjector;
+import com.ctrip.framework.apollo.core.utils.ApolloThreadFactory;
+import com.ctrip.framework.apollo.enums.ConfigSourceType;
+import com.ctrip.framework.apollo.internals.AbstractConfigRepository;
+import com.ctrip.framework.apollo.internals.ConfigRepository;
+import com.ctrip.framework.apollo.tracer.Tracer;
+import com.ctrip.framework.apollo.util.ConfigUtil;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Properties;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+@Slf4j
+public class DBConfigRepository extends AbstractConfigRepository {
+
+ private final static ScheduledExecutorService m_executorService;
+
+ static {
+ m_executorService = Executors.newScheduledThreadPool(1,
+ ApolloThreadFactory.create(DBConfigRepository.class.getSimpleName(), true));
+ }
+
+ private final ConfigUtil m_configUtil;
+
+ private final AtomicReference m_configCache;
+ private final String m_namespace;
+
+ public DBConfigRepository(String namespace) {
+ // 初始化变量
+ this.m_namespace = namespace;
+ m_configCache = new AtomicReference<>();
+ m_configUtil = ApolloInjector.getInstance(ConfigUtil.class);
+
+ // 初始化加载
+ this.trySync();
+ // 初始化定时任务
+ this.schedulePeriodicRefresh();
+ }
+
+ private AtomicInteger index = new AtomicInteger();
+
+ @Override
+ protected void sync() {
+ System.out.println("我同步啦");
+
+ index.incrementAndGet();
+ Properties properties = new Properties();
+ properties.setProperty("demo.test", String.valueOf(index.get()));
+ m_configCache.set(properties);
+ super.fireRepositoryChange(m_namespace, properties);
+ }
+
+ @Override
+ public Properties getConfig() {
+ // 兜底,避免可能存在配置为 null 的情况
+ if (m_configCache.get() == null) {
+ this.trySync();
+ }
+ // 返回配置
+ return m_configCache.get();
+ }
+
+ @Override
+ public void setUpstreamRepository(ConfigRepository upstreamConfigRepository) {
+ // 啥事不做
+ }
+
+ @Override
+ public ConfigSourceType getSourceType() {
+ return ConfigSourceType.REMOTE;
+ }
+
+ private void schedulePeriodicRefresh() {
+ log.debug("Schedule periodic refresh with interval: {} {}",
+ m_configUtil.getRefreshInterval(), m_configUtil.getRefreshIntervalTimeUnit());
+ m_executorService.scheduleAtFixedRate(() -> {
+ Tracer.logEvent("Apollo.ConfigService", String.format("periodicRefresh: %s", m_namespace));
+ log.debug("refresh config for namespace: {}", m_namespace);
+
+ // 执行同步
+ trySync();
+
+ Tracer.logEvent("Apollo.Client.Version", Apollo.VERSION);
+ }, m_configUtil.getRefreshInterval(), m_configUtil.getRefreshInterval(),
+ m_configUtil.getRefreshIntervalTimeUnit());
+// TimeUnit.SECONDS);
+ }
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollo/internals/DefaultXInjector.java b/src/main/java/cn/iocoder/dashboard/framework/apollo/internals/DefaultXInjector.java
new file mode 100644
index 000000000..587a850f3
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/framework/apollo/internals/DefaultXInjector.java
@@ -0,0 +1,75 @@
+package cn.iocoder.dashboard.framework.apollo.internals;
+
+import cn.iocoder.dashboard.framework.apollo.spi.DBConfigFactory;
+import com.ctrip.framework.apollo.exceptions.ApolloConfigException;
+import com.ctrip.framework.apollo.internals.*;
+import com.ctrip.framework.apollo.spi.*;
+import com.ctrip.framework.apollo.tracer.Tracer;
+import com.ctrip.framework.apollo.util.ConfigUtil;
+import com.ctrip.framework.apollo.util.factory.DefaultPropertiesFactory;
+import com.ctrip.framework.apollo.util.factory.PropertiesFactory;
+import com.ctrip.framework.apollo.util.http.HttpUtil;
+import com.ctrip.framework.apollo.util.yaml.YamlParser;
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Singleton;
+
+/**
+ * Guice injector
+ *
+ * 基于 Guice 注入器实现类
+ *
+ * @author Jason Song(song_s@ctrip.com)
+ */
+public class DefaultXInjector implements Injector {
+
+ private final com.google.inject.Injector m_injector;
+
+ public DefaultXInjector() {
+ try {
+ m_injector = Guice.createInjector(new ApolloModule());
+ } catch (Throwable ex) {
+ ApolloConfigException exception = new ApolloConfigException("Unable to initialize Guice Injector!", ex);
+ Tracer.logError(exception);
+ throw exception;
+ }
+ }
+
+ @Override
+ public T getInstance(Class clazz) {
+ try {
+ return m_injector.getInstance(clazz);
+ } catch (Throwable ex) {
+ Tracer.logError(ex);
+ throw new ApolloConfigException(String.format("Unable to load instance for %s!", clazz.getName()), ex);
+ }
+ }
+
+ @Override
+ public T getInstance(Class clazz, String name) {
+ // Guice does not support get instance by type and name
+ return null;
+ }
+
+ private static class ApolloModule extends AbstractModule {
+
+ @Override
+ protected void configure() {
+ bind(ConfigManager.class).to(DefaultConfigManager.class).in(Singleton.class);
+ bind(ConfigFactoryManager.class).to(DefaultConfigFactoryManager.class).in(Singleton.class);
+ bind(ConfigRegistry.class).to(DefaultConfigRegistry.class).in(Singleton.class);
+
+ // 自定义 ConfigFactory 实现,使用 DB 作为数据源
+ bind(ConfigFactory.class).to(DBConfigFactory.class).in(Singleton.class);
+
+ bind(ConfigUtil.class).in(Singleton.class);
+ bind(HttpUtil.class).in(Singleton.class);
+ bind(ConfigServiceLocator.class).in(Singleton.class);
+ bind(RemoteConfigLongPollService.class).in(Singleton.class);
+ bind(YamlParser.class).in(Singleton.class);
+ bind(PropertiesFactory.class).to(DefaultPropertiesFactory.class).in(Singleton.class);
+ }
+
+ }
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollo/package-info.java b/src/main/java/cn/iocoder/dashboard/framework/apollo/package-info.java
new file mode 100644
index 000000000..f8a617005
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/framework/apollo/package-info.java
@@ -0,0 +1 @@
+package cn.iocoder.dashboard.framework.apollo;
diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollo/spi/DBConfigFactory.java b/src/main/java/cn/iocoder/dashboard/framework/apollo/spi/DBConfigFactory.java
new file mode 100644
index 000000000..8bc760a4b
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/framework/apollo/spi/DBConfigFactory.java
@@ -0,0 +1,27 @@
+package cn.iocoder.dashboard.framework.apollo.spi;
+
+import cn.iocoder.dashboard.framework.apollo.internals.DBConfigRepository;
+import com.ctrip.framework.apollo.Config;
+import com.ctrip.framework.apollo.ConfigFile;
+import com.ctrip.framework.apollo.core.enums.ConfigFileFormat;
+import com.ctrip.framework.apollo.internals.ConfigRepository;
+import com.ctrip.framework.apollo.internals.DefaultConfig;
+import com.ctrip.framework.apollo.spi.ConfigFactory;
+
+public class DBConfigFactory implements ConfigFactory {
+
+ @Override
+ public Config create(String namespace) {
+ return new DefaultConfig(namespace, this.createDBConfigRepository(namespace));
+ }
+
+ @Override
+ public ConfigFile createConfigFile(String namespace, ConfigFileFormat configFileFormat) {
+ throw new UnsupportedOperationException("暂不支持 Apollo 配置文件");
+ }
+
+ private ConfigRepository createDBConfigRepository(String namespace) {
+ return new DBConfigRepository(namespace); // TODO 芋艿:看看怎么优化
+ }
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/config/SysConfigController.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/config/SysConfigController.java
index 7fdc7554f..45b05569c 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/controller/config/SysConfigController.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/controller/config/SysConfigController.java
@@ -28,7 +28,7 @@ import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.CO
@RequestMapping("/system/config")
public class SysConfigController {
- @Value("${demo.test:false}")
+ @Value("${demo.test}")
private String demo;
@GetMapping("/demo")
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/permission/SysRoleMapper.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/permission/SysRoleMapper.java
index 615fc44ea..eb6345a8b 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/permission/SysRoleMapper.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/permission/SysRoleMapper.java
@@ -8,8 +8,8 @@ import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRo
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.apache.ibatis.annotations.Mapper;
+import org.springframework.lang.Nullable;
-import javax.annotation.Nullable;
import java.util.Collection;
import java.util.List;
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/dept/SysPostService.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/dept/SysPostService.java
index 0a7168c6a..e95c7acac 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/service/dept/SysPostService.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/dept/SysPostService.java
@@ -6,8 +6,8 @@ import cn.iocoder.dashboard.modules.system.controller.dept.vo.post.SysPostExport
import cn.iocoder.dashboard.modules.system.controller.dept.vo.post.SysPostPageReqVO;
import cn.iocoder.dashboard.modules.system.controller.dept.vo.post.SysPostUpdateReqVO;
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.dept.SysPostDO;
+import org.springframework.lang.Nullable;
-import javax.annotation.Nullable;
import java.util.Collection;
import java.util.List;
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/SysPermissionService.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/SysPermissionService.java
index cab499201..60936a414 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/SysPermissionService.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/SysPermissionService.java
@@ -1,8 +1,8 @@
package cn.iocoder.dashboard.modules.system.service.permission;
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO;
+import org.springframework.lang.Nullable;
-import javax.annotation.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.Set;
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/SysRoleService.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/SysRoleService.java
index 41a9d39ef..161a0d810 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/SysRoleService.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/SysRoleService.java
@@ -6,8 +6,8 @@ import cn.iocoder.dashboard.modules.system.controller.permission.vo.role.SysRole
import cn.iocoder.dashboard.modules.system.controller.permission.vo.role.SysRolePageReqVO;
import cn.iocoder.dashboard.modules.system.controller.permission.vo.role.SysRoleUpdateReqVO;
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleDO;
+import org.springframework.lang.Nullable;
-import javax.annotation.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.Set;
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysRoleServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysRoleServiceImpl.java
index 1aa05485a..d98611cbf 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysRoleServiceImpl.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysRoleServiceImpl.java
@@ -18,11 +18,11 @@ import cn.iocoder.dashboard.modules.system.service.permission.SysRoleService;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.google.common.collect.ImmutableMap;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
-import javax.annotation.Nullable;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;
diff --git a/src/main/resources/META-INF/services/com.ctrip.framework.apollo.internals.Injector b/src/main/resources/META-INF/services/com.ctrip.framework.apollo.internals.Injector
new file mode 100644
index 000000000..a52f206f2
--- /dev/null
+++ b/src/main/resources/META-INF/services/com.ctrip.framework.apollo.internals.Injector
@@ -0,0 +1 @@
+cn.iocoder.dashboard.framework.apollo.internals.DefaultXInjector
diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml
index c38ecabbc..14248ca39 100644
--- a/src/main/resources/application.yaml
+++ b/src/main/resources/application.yaml
@@ -47,6 +47,14 @@ yudao:
file:
base-path: http://127.0.0.1:1024/api/file/get/
+# Apollo 配置中心
+apollo:
+ bootstrap:
+ enabled: true
+ eagerLoad:
+ enabled: true
+ autoUpdateInjectedSpringProperties: true
+
# MyBatis Plus 的配置项
mybatis-plus:
configuration: