Commit 17cb37f577f6e4f653335bd7a23a33f4a701dc2b

Authored by YunaiV
1 parent c4c61459

增加 apollo 读取本地 db,需要进一步优化~

Showing 35 changed files with 96 additions and 1975 deletions
src/main/java/cn/iocoder/dashboard/framework/apollo/internals/DBConfigRepository.java
1 package cn.iocoder.dashboard.framework.apollo.internals; 1 package cn.iocoder.dashboard.framework.apollo.internals;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
  5 +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.config.SysConfigDO;
3 import com.ctrip.framework.apollo.Apollo; 6 import com.ctrip.framework.apollo.Apollo;
4 import com.ctrip.framework.apollo.build.ApolloInjector; 7 import com.ctrip.framework.apollo.build.ApolloInjector;
5 import com.ctrip.framework.apollo.core.utils.ApolloThreadFactory; 8 import com.ctrip.framework.apollo.core.utils.ApolloThreadFactory;
@@ -8,13 +11,20 @@ import com.ctrip.framework.apollo.internals.AbstractConfigRepository; @@ -8,13 +11,20 @@ import com.ctrip.framework.apollo.internals.AbstractConfigRepository;
8 import com.ctrip.framework.apollo.internals.ConfigRepository; 11 import com.ctrip.framework.apollo.internals.ConfigRepository;
9 import com.ctrip.framework.apollo.tracer.Tracer; 12 import com.ctrip.framework.apollo.tracer.Tracer;
10 import com.ctrip.framework.apollo.util.ConfigUtil; 13 import com.ctrip.framework.apollo.util.ConfigUtil;
  14 +import com.ctrip.framework.apollo.util.factory.PropertiesFactory;
11 import lombok.extern.slf4j.Slf4j; 15 import lombok.extern.slf4j.Slf4j;
12 - 16 +import org.springframework.jdbc.core.BeanPropertyRowMapper;
  17 +import org.springframework.jdbc.core.JdbcTemplate;
  18 +import org.springframework.jdbc.datasource.DriverManagerDataSource;
  19 +
  20 +import javax.sql.DataSource;
  21 +import java.sql.ResultSet;
  22 +import java.util.Comparator;
  23 +import java.util.Date;
  24 +import java.util.List;
13 import java.util.Properties; 25 import java.util.Properties;
14 import java.util.concurrent.Executors; 26 import java.util.concurrent.Executors;
15 import java.util.concurrent.ScheduledExecutorService; 27 import java.util.concurrent.ScheduledExecutorService;
16 -import java.util.concurrent.atomic.AtomicInteger;  
17 -import java.util.concurrent.atomic.AtomicReference;  
18 28
19 @Slf4j 29 @Slf4j
20 public class DBConfigRepository extends AbstractConfigRepository { 30 public class DBConfigRepository extends AbstractConfigRepository {
@@ -27,15 +37,35 @@ public class DBConfigRepository extends AbstractConfigRepository { @@ -27,15 +37,35 @@ public class DBConfigRepository extends AbstractConfigRepository {
27 } 37 }
28 38
29 private final ConfigUtil m_configUtil; 39 private final ConfigUtil m_configUtil;
30 -  
31 - private final AtomicReference<Properties> m_configCache; 40 + private PropertiesFactory propertiesFactory;
32 private final String m_namespace; 41 private final String m_namespace;
33 42
  43 + /**
  44 + * 配置缓存,使用 Properties 存储
  45 + */
  46 + private volatile Properties m_configCache;
  47 + /**
  48 + * 缓存配置的最大更新时间,用于后续的增量轮询,判断是否有更新
  49 + */
  50 + private volatile Date maxUpdateTime;
  51 +
  52 + /**
  53 + * Spring JDBC 操作模板
  54 + */
  55 + private final JdbcTemplate jdbcTemplate;
  56 +
34 public DBConfigRepository(String namespace) { 57 public DBConfigRepository(String namespace) {
35 // 初始化变量 58 // 初始化变量
36 this.m_namespace = namespace; 59 this.m_namespace = namespace;
37 - m_configCache = new AtomicReference<>();  
38 - m_configUtil = ApolloInjector.getInstance(ConfigUtil.class); 60 + this.propertiesFactory = ApolloInjector.getInstance(PropertiesFactory.class);
  61 + this.m_configUtil = ApolloInjector.getInstance(ConfigUtil.class);
  62 + // TODO 优化到配置
  63 + String url = "jdbc:mysql://127.0.1:33061/ruoyi-vue-pro?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT";
  64 + String username = "root";
  65 + String password = "123456";
  66 +// DataSource dataSource = DataSourceBuilder.create().url(url).username(username).password(password).build();
  67 + DataSource dataSource = new DriverManagerDataSource(url, username, password);
  68 + this.jdbcTemplate = new JdbcTemplate(dataSource);
39 69
40 // 初始化加载 70 // 初始化加载
41 this.trySync(); 71 this.trySync();
@@ -43,27 +73,32 @@ public class DBConfigRepository extends AbstractConfigRepository { @@ -43,27 +73,32 @@ public class DBConfigRepository extends AbstractConfigRepository {
43 this.schedulePeriodicRefresh(); 73 this.schedulePeriodicRefresh();
44 } 74 }
45 75
46 - private AtomicInteger index = new AtomicInteger();  
47 -  
48 @Override 76 @Override
49 protected void sync() { 77 protected void sync() {
50 - System.out.println("我同步啦");  
51 -  
52 - index.incrementAndGet();  
53 - Properties properties = new Properties();  
54 - properties.setProperty("demo.test", String.valueOf(index.get()));  
55 - m_configCache.set(properties);  
56 - super.fireRepositoryChange(m_namespace, properties); 78 + // 第一步,尝试获取配置
  79 + List<SysConfigDO> configs = this.loadConfigIfUpdate(this.maxUpdateTime);
  80 + if (CollUtil.isEmpty(configs)) { // 如果没有更新,则返回
  81 + return;
  82 + }
  83 + log.info("[sync][同步到新配置,配置数量为:{}]", configs.size());
  84 +
  85 + // 第二步,构建新的 Properties
  86 + Properties newProperties = this.buildProperties(configs);
  87 + this.m_configCache = newProperties;
  88 + // 第三步,获取最大的配置时间
  89 + this.maxUpdateTime = configs.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
  90 + // 第四部,触发配置刷新!重要!!!!
  91 + super.fireRepositoryChange(m_namespace, newProperties);
57 } 92 }
58 93
59 @Override 94 @Override
60 public Properties getConfig() { 95 public Properties getConfig() {
61 // 兜底,避免可能存在配置为 null 的情况 96 // 兜底,避免可能存在配置为 null 的情况
62 - if (m_configCache.get() == null) { 97 + if (m_configCache == null) {
63 this.trySync(); 98 this.trySync();
64 } 99 }
65 // 返回配置 100 // 返回配置
66 - return m_configCache.get(); 101 + return m_configCache;
67 } 102 }
68 103
69 @Override 104 @Override
@@ -76,6 +111,15 @@ public class DBConfigRepository extends AbstractConfigRepository { @@ -76,6 +111,15 @@ public class DBConfigRepository extends AbstractConfigRepository {
76 return ConfigSourceType.REMOTE; 111 return ConfigSourceType.REMOTE;
77 } 112 }
78 113
  114 + private Properties buildProperties(List<SysConfigDO> configs) {
  115 + Properties properties = propertiesFactory.getPropertiesInstance();
  116 + configs.stream().filter(config -> 0 == config.getDeleted()) // 过滤掉被删除的配置
  117 + .forEach(config -> properties.put(config.getKey(), config.getValue()));
  118 + return properties;
  119 + }
  120 +
  121 + // ========== 定时器相关操作 ==========
  122 +
79 private void schedulePeriodicRefresh() { 123 private void schedulePeriodicRefresh() {
80 log.debug("Schedule periodic refresh with interval: {} {}", 124 log.debug("Schedule periodic refresh with interval: {} {}",
81 m_configUtil.getRefreshInterval(), m_configUtil.getRefreshIntervalTimeUnit()); 125 m_configUtil.getRefreshInterval(), m_configUtil.getRefreshIntervalTimeUnit());
@@ -83,7 +127,7 @@ public class DBConfigRepository extends AbstractConfigRepository { @@ -83,7 +127,7 @@ public class DBConfigRepository extends AbstractConfigRepository {
83 Tracer.logEvent("Apollo.ConfigService", String.format("periodicRefresh: %s", m_namespace)); 127 Tracer.logEvent("Apollo.ConfigService", String.format("periodicRefresh: %s", m_namespace));
84 log.debug("refresh config for namespace: {}", m_namespace); 128 log.debug("refresh config for namespace: {}", m_namespace);
85 129
86 - // 执行同步 130 + // 执行同步. 内部已经 try catch 掉异常,无需在处理
87 trySync(); 131 trySync();
88 132
89 Tracer.logEvent("Apollo.Client.Version", Apollo.VERSION); 133 Tracer.logEvent("Apollo.Client.Version", Apollo.VERSION);
@@ -92,4 +136,35 @@ public class DBConfigRepository extends AbstractConfigRepository { @@ -92,4 +136,35 @@ public class DBConfigRepository extends AbstractConfigRepository {
92 // TimeUnit.SECONDS); 136 // TimeUnit.SECONDS);
93 } 137 }
94 138
  139 + // ========== 数据库相关操作 ==========
  140 +
  141 + /**
  142 + * 如果配置发生变化,从数据库中获取最新的全量配置。
  143 + * 如果未发生变化,则返回空
  144 + *
  145 + * @param maxUpdateTime 当前配置的最大更新时间
  146 + * @return 配置列表
  147 + */
  148 + private List<SysConfigDO> loadConfigIfUpdate(Date maxUpdateTime) {
  149 + // 第一步,判断是否要更新。
  150 + boolean isUpdate = maxUpdateTime == null; // 如果更新时间为空,说明 DB 一定有新数据
  151 + if (!isUpdate) {
  152 + isUpdate = this.existsNewConfig(maxUpdateTime); // 判断数据库中是否有更新的配置
  153 + }
  154 + if (!isUpdate) {
  155 + return null;
  156 + }
  157 + // 第二步,如果有更新,则从数据库加载所有配置
  158 + return this.getSysConfigList();
  159 + }
  160 +
  161 + private boolean existsNewConfig(Date maxUpdateTime) {
  162 + return jdbcTemplate.query("SELECT id FROM sys_config WHERE update_time > ? LIMIT 1",
  163 + ResultSet::next, maxUpdateTime);
  164 + }
  165 +
  166 + private List<SysConfigDO> getSysConfigList() {
  167 + return jdbcTemplate.query("SELECT `key`, `value`, update_time, deleted FROM sys_config", new BeanPropertyRowMapper<>(SysConfigDO.class));
  168 + }
  169 +
95 } 170 }
src/main/java/cn/iocoder/dashboard/framework/apollox/Config.java deleted
1 -package cn.iocoder.dashboard.framework.apollox;  
2 -  
3 -import java.util.Set;  
4 -  
5 -/**  
6 - * 配置接口  
7 - *  
8 - * @author Jason Song(song_s@ctrip.com)  
9 - */  
10 -public interface Config {  
11 -  
12 - /**  
13 - * Return the property value with the given key, or {@code defaultValue} if the key doesn't exist.  
14 - *  
15 - * @param key the property name  
16 - * @param defaultValue the default value when key is not found or any error occurred  
17 - * @return the property value  
18 - */  
19 - String getProperty(String key, String defaultValue);  
20 -  
21 - /**  
22 - * Return a set of the property names  
23 - *  
24 - * @return the property names  
25 - */  
26 - Set<String> getPropertyNames();  
27 -  
28 - /**  
29 - * Add change listener to this config instance.  
30 - *  
31 - * @param listener the config change listener  
32 - */  
33 - void addChangeListener(ConfigChangeListener listener);  
34 -  
35 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/ConfigChangeListener.java deleted
1 -package cn.iocoder.dashboard.framework.apollox;  
2 -  
3 -import cn.iocoder.dashboard.framework.apollox.model.ConfigChangeEvent;  
4 -  
5 -/**  
6 - * {@link Config} 变化监听器  
7 - *  
8 - * @author Jason Song(song_s@ctrip.com)  
9 - */  
10 -public interface ConfigChangeListener {  
11 -  
12 - /**  
13 - * Invoked when there is any config change for the namespace.  
14 - *  
15 - * @param changeEvent the event for this change  
16 - */  
17 - void onChange(ConfigChangeEvent changeEvent);  
18 -  
19 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/ConfigService.java deleted
1 -package cn.iocoder.dashboard.framework.apollox;  
2 -  
3 -import cn.hutool.core.lang.Singleton;  
4 -  
5 -public class ConfigService {  
6 -  
7 - public static Config getConfig(String namespace) {  
8 - return Singleton.get(DefaultConfig.class);  
9 - }  
10 -  
11 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/DefaultConfig.java deleted
1 -package cn.iocoder.dashboard.framework.apollox;  
2 -  
3 -import java.util.Collections;  
4 -import java.util.Set;  
5 -  
6 -public class DefaultConfig implements Config {  
7 -  
8 - @Override  
9 - public String getProperty(String key, String defaultValue) {  
10 - return null;  
11 - }  
12 -  
13 - @Override  
14 - public Set<String> getPropertyNames() {  
15 - return Collections.emptySet(); // TODO 等下实现  
16 - }  
17 -  
18 - @Override  
19 - public void addChangeListener(ConfigChangeListener listener) {  
20 -  
21 - }  
22 -  
23 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/enums/PropertyChangeType.java deleted
1 -package cn.iocoder.dashboard.framework.apollox.enums;  
2 -  
3 -/**  
4 - * 属性变化类型枚举  
5 - *  
6 - * @author Jason Song(song_s@ctrip.com)  
7 - */  
8 -public enum PropertyChangeType {  
9 -  
10 - ADDED, // 添加  
11 - MODIFIED, // 修改  
12 - DELETED // 删除  
13 -  
14 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/internals/AbstractConfigRepository.java deleted
1 -package cn.iocoder.dashboard.framework.apollox.internals;  
2 -  
3 -import org.slf4j.Logger;  
4 -import org.slf4j.LoggerFactory;  
5 -  
6 -import java.util.List;  
7 -import java.util.Properties;  
8 -import java.util.concurrent.CopyOnWriteArrayList;  
9 -  
10 -/**  
11 - * 配置 Repository 抽象类  
12 - *  
13 - * @author Jason Song(song_s@ctrip.com)  
14 - */  
15 -public abstract class AbstractConfigRepository implements ConfigRepository {  
16 -  
17 - private static final Logger logger = LoggerFactory.getLogger(AbstractConfigRepository.class);  
18 -  
19 - /**  
20 - * RepositoryChangeListener 数组  
21 - */  
22 - private List<RepositoryChangeListener> m_listeners = new CopyOnWriteArrayList<>();  
23 -  
24 - /**  
25 - * 尝试同步  
26 - *  
27 - * @return 是否同步成功  
28 - */  
29 - protected boolean trySync() {  
30 - try {  
31 - // 同步  
32 - sync();  
33 - // 返回同步成功  
34 - return true;  
35 - } catch (Throwable ex) {  
36 -// Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(ex));  
37 - logger.warn("Sync config failed, will retry. Repository {}", getClass(), ex);  
38 - }  
39 - // 返回同步失败  
40 - return false;  
41 - }  
42 -  
43 - /**  
44 - * 同步配置  
45 - */  
46 - protected abstract void sync();  
47 -  
48 - @Override  
49 - public void addChangeListener(RepositoryChangeListener listener) {  
50 - if (!m_listeners.contains(listener)) {  
51 - m_listeners.add(listener);  
52 - }  
53 - }  
54 -  
55 - @Override  
56 - public void removeChangeListener(RepositoryChangeListener listener) {  
57 - m_listeners.remove(listener);  
58 - }  
59 -  
60 - /**  
61 - * 触发监听器们  
62 - *  
63 - * @param namespace Namespace 名字  
64 - * @param newProperties 配置  
65 - */  
66 - protected void fireRepositoryChange(String namespace, Properties newProperties) {  
67 - // 循环 RepositoryChangeListener 数组  
68 - for (RepositoryChangeListener listener : m_listeners) {  
69 - try {  
70 - // 触发监听器  
71 - listener.onRepositoryChange(namespace, newProperties);  
72 - } catch (Throwable ex) {  
73 -// Tracer.logError(ex);  
74 - logger.error("Failed to invoke repository change listener {}", listener.getClass(), ex);  
75 - }  
76 - }  
77 - }  
78 -  
79 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/internals/ConfigRepository.java deleted
1 -package cn.iocoder.dashboard.framework.apollox.internals;  
2 -  
3 -import java.util.Properties;  
4 -  
5 -/**  
6 - * 配置 Repository 接口  
7 - *  
8 - * @author Jason Song(song_s@ctrip.com)  
9 - */  
10 -public interface ConfigRepository {  
11 -  
12 - /**  
13 - * Get the config from this repository.  
14 - * <p>  
15 - * 获得配置,以 Properties 对象返回  
16 - *  
17 - * @return config  
18 - */  
19 - Properties getConfig();  
20 -  
21 - /**  
22 - * Add change listener.  
23 - *  
24 - * @param listener the listener to observe the changes  
25 - */  
26 - void addChangeListener(RepositoryChangeListener listener);  
27 -  
28 - /**  
29 - * Remove change listener.  
30 - *  
31 - * @param listener the listener to remove  
32 - */  
33 - void removeChangeListener(RepositoryChangeListener listener);  
34 -  
35 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/internals/RemoteConfigRepository.java deleted
1 -package cn.iocoder.dashboard.framework.apollox.internals;  
2 -  
3 -import com.google.common.base.Joiner;  
4 -import org.slf4j.Logger;  
5 -import org.slf4j.LoggerFactory;  
6 -  
7 -/**  
8 - * RemoteConfig Repository  
9 - * <p>  
10 - * 远程配置 Repository ,实现从 Config Service 拉取配置,并缓存在内存中。并且,定时 + 实时刷新缓存。  
11 - *  
12 - * @author Jason Song(song_s@ctrip.com)  
13 - */  
14 -public class RemoteConfigRepository extends AbstractConfigRepository {  
15 -  
16 - private static final Logger logger = LoggerFactory.getLogger(RemoteConfigRepository.class);  
17 - private static final Joiner STRING_JOINER = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR);  
18 - private static final Joiner.MapJoiner MAP_JOINER = Joiner.on("&").withKeyValueSeparator("=");  
19 -  
20 - private static final Escaper pathEscaper = UrlEscapers.urlPathSegmentEscaper();  
21 - private static final Escaper queryParamEscaper = UrlEscapers.urlFormParameterEscaper();  
22 -  
23 - /**  
24 - * 远程配置长轮询服务  
25 - */  
26 - private RemoteConfigLongPollService remoteConfigLongPollService;  
27 - /**  
28 - * 指向 ApolloConfig 的 AtomicReference ,缓存配置  
29 - */  
30 - private volatile AtomicReference<ApolloConfig> m_configCache;  
31 - /**  
32 - * Namespace 名字  
33 - */  
34 - private final String m_namespace;  
35 - /**  
36 - * ScheduledExecutorService 对象  
37 - */  
38 - private final static ScheduledExecutorService m_executorService;  
39 - /**  
40 - * 指向 ServiceDTO( Config Service 信息) 的 AtomicReference  
41 - */  
42 - private AtomicReference<ServiceDTO> m_longPollServiceDto;  
43 - /**  
44 - * 指向 ApolloNotificationMessages 的 AtomicReference  
45 - */  
46 - private AtomicReference<ApolloNotificationMessages> m_remoteMessages;  
47 - /**  
48 - * 加载配置的 RateLimiter  
49 - */  
50 - private RateLimiter m_loadConfigRateLimiter;  
51 - /**  
52 - * 是否强制拉取缓存的标记  
53 - * <p>  
54 - * 若为 true ,则多一轮从 Config Service 拉取配置  
55 - * 为 true 的原因,RemoteConfigRepository 知道 Config Service 有配置刷新  
56 - */  
57 - private AtomicBoolean m_configNeedForceRefresh;  
58 - /**  
59 - * 失败定时重试策略,使用 {@link ExponentialSchedulePolicy}  
60 - */  
61 - private SchedulePolicy m_loadConfigFailSchedulePolicy;  
62 - private Gson gson;  
63 - private ConfigUtil m_configUtil;  
64 - private HttpUtil m_httpUtil;  
65 - private ConfigServiceLocator m_serviceLocator;  
66 -  
67 - static {  
68 - // 单线程池  
69 - m_executorService = Executors.newScheduledThreadPool(1, ApolloThreadFactory.create("RemoteConfigRepository", true));  
70 - }  
71 -  
72 - /**  
73 - * Constructor.  
74 - *  
75 - * @param namespace the namespace  
76 - */  
77 - public RemoteConfigRepository(String namespace) {  
78 - m_namespace = namespace;  
79 - m_configCache = new AtomicReference<>();  
80 - m_configUtil = ApolloInjector.getInstance(ConfigUtil.class);  
81 - m_httpUtil = ApolloInjector.getInstance(HttpUtil.class);  
82 - m_serviceLocator = ApolloInjector.getInstance(ConfigServiceLocator.class);  
83 - remoteConfigLongPollService = ApolloInjector.getInstance(RemoteConfigLongPollService.class);  
84 - m_longPollServiceDto = new AtomicReference<>();  
85 - m_remoteMessages = new AtomicReference<>();  
86 - m_loadConfigRateLimiter = RateLimiter.create(m_configUtil.getLoadConfigQPS());  
87 - m_configNeedForceRefresh = new AtomicBoolean(true);  
88 - m_loadConfigFailSchedulePolicy = new ExponentialSchedulePolicy(m_configUtil.getOnErrorRetryInterval(), m_configUtil.getOnErrorRetryInterval() * 8);  
89 - gson = new Gson();  
90 - // 尝试同步配置  
91 - super.trySync();  
92 - // 初始化定时刷新配置的任务  
93 - this.schedulePeriodicRefresh();  
94 - // 注册自己到 RemoteConfigLongPollService 中,实现配置更新的实时通知  
95 - this.scheduleLongPollingRefresh();  
96 - }  
97 -  
98 - @Override  
99 - public Properties getConfig() {  
100 - // 如果缓存为空,强制从 Config Service 拉取配置  
101 - if (m_configCache.get() == null) {  
102 - this.sync();  
103 - }  
104 - // 转换成 Properties 对象,并返回  
105 - return transformApolloConfigToProperties(m_configCache.get());  
106 - }  
107 -  
108 - @Override  
109 - public void setUpstreamRepository(ConfigRepository upstreamConfigRepository) {  
110 - // remote config doesn't need upstream  
111 - }  
112 -  
113 - private void schedulePeriodicRefresh() {  
114 - logger.debug("Schedule periodic refresh with interval: {} {}", m_configUtil.getRefreshInterval(), m_configUtil.getRefreshIntervalTimeUnit());  
115 - // 创建定时任务,定时刷新配置  
116 - m_executorService.scheduleAtFixedRate(new Runnable() {  
117 - @Override  
118 - public void run() {  
119 - // 【TODO 6001】Tracer 日志  
120 - Tracer.logEvent("Apollo.ConfigService", String.format("periodicRefresh: %s", m_namespace));  
121 - logger.debug("refresh config for namespace: {}", m_namespace);  
122 - // 尝试同步配置  
123 - trySync();  
124 - // 【TODO 6001】Tracer 日志  
125 - Tracer.logEvent("Apollo.Client.Version", Apollo.VERSION);  
126 - }  
127 - }, m_configUtil.getRefreshInterval(), m_configUtil.getRefreshInterval(), m_configUtil.getRefreshIntervalTimeUnit());  
128 - }  
129 -  
130 - @Override  
131 - protected synchronized void sync() {  
132 - // 【TODO 6001】Tracer 日志  
133 - Transaction transaction = Tracer.newTransaction("Apollo.ConfigService", "syncRemoteConfig");  
134 - try {  
135 - // 获得缓存的 ApolloConfig 对象  
136 - ApolloConfig previous = m_configCache.get();  
137 - // 从 Config Service 加载 ApolloConfig 对象  
138 - ApolloConfig current = loadApolloConfig();  
139 -  
140 - // reference equals means HTTP 304  
141 - // 若不相等,说明更新了,设置到缓存中  
142 - if (previous != current) {  
143 - logger.debug("Remote Config refreshed!");  
144 - // 设置到缓存  
145 - m_configCache.set(current);  
146 - // 发布 Repository 的配置发生变化,触发对应的监听器们  
147 - super.fireRepositoryChange(m_namespace, this.getConfig());  
148 - }  
149 - // 【TODO 6001】Tracer 日志  
150 - if (current != null) {  
151 - Tracer.logEvent(String.format("Apollo.Client.Configs.%s", current.getNamespaceName()), current.getReleaseKey());  
152 - }  
153 - // 【TODO 6001】Tracer 日志  
154 - transaction.setStatus(Transaction.SUCCESS);  
155 - } catch (Throwable ex) {  
156 - // 【TODO 6001】Tracer 日志  
157 - transaction.setStatus(ex);  
158 - throw ex;  
159 - } finally {  
160 - // 【TODO 6001】Tracer 日志  
161 - transaction.complete();  
162 - }  
163 - }  
164 -  
165 - private Properties transformApolloConfigToProperties(ApolloConfig apolloConfig) {  
166 - Properties result = new Properties();  
167 - result.putAll(apolloConfig.getConfigurations());  
168 - return result;  
169 - }  
170 -  
171 - private ApolloConfig loadApolloConfig() {  
172 - // 限流  
173 - if (!m_loadConfigRateLimiter.tryAcquire(5, TimeUnit.SECONDS)) {  
174 - // wait at most 5 seconds  
175 - try {  
176 - TimeUnit.SECONDS.sleep(5);  
177 - } catch (InterruptedException e) {  
178 - }  
179 - }  
180 - // 获得 appId cluster dataCenter 配置信息  
181 - String appId = m_configUtil.getAppId();  
182 - String cluster = m_configUtil.getCluster();  
183 - String dataCenter = m_configUtil.getDataCenter();  
184 - Tracer.logEvent("Apollo.Client.ConfigMeta", STRING_JOINER.join(appId, cluster, m_namespace));  
185 - // 计算重试次数  
186 - int maxRetries = m_configNeedForceRefresh.get() ? 2 : 1;  
187 - long onErrorSleepTime = 0; // 0 means no sleep  
188 - Throwable exception = null;  
189 - // 获得所有的 Config Service 的地址  
190 - List<ServiceDTO> configServices = getConfigServices();  
191 - String url = null;  
192 - // 循环读取配置重试次数直到成功。每一次,都会循环所有的 ServiceDTO 数组。  
193 - for (int i = 0; i < maxRetries; i++) {  
194 - // 随机所有的 Config Service 的地址  
195 - List<ServiceDTO> randomConfigServices = Lists.newLinkedList(configServices);  
196 - Collections.shuffle(randomConfigServices);  
197 - // 优先访问通知配置变更的 Config Service 的地址。并且,获取到时,需要置空,避免重复优先访问。  
198 - // Access the server which notifies the client first  
199 - if (m_longPollServiceDto.get() != null) {  
200 - randomConfigServices.add(0, m_longPollServiceDto.getAndSet(null));  
201 - }  
202 - // 循环所有的 Config Service 的地址  
203 - for (ServiceDTO configService : randomConfigServices) {  
204 - // sleep 等待,下次从 Config Service 拉取配置  
205 - if (onErrorSleepTime > 0) {  
206 - logger.warn("Load config failed, will retry in {} {}. appId: {}, cluster: {}, namespaces: {}", onErrorSleepTime, m_configUtil.getOnErrorRetryIntervalTimeUnit(), appId, cluster, m_namespace);  
207 - try {  
208 - m_configUtil.getOnErrorRetryIntervalTimeUnit().sleep(onErrorSleepTime);  
209 - } catch (InterruptedException e) {  
210 - //ignore  
211 - }  
212 - }  
213 - // 组装查询配置的地址  
214 - url = assembleQueryConfigUrl(configService.getHomepageUrl(), appId, cluster, m_namespace, dataCenter, m_remoteMessages.get(), m_configCache.get());  
215 -  
216 - logger.debug("Loading config from {}", url);  
217 - // 创建 HttpRequest 对象  
218 - HttpRequest request = new HttpRequest(url);  
219 -  
220 - // 【TODO 6001】Tracer 日志  
221 - Transaction transaction = Tracer.newTransaction("Apollo.ConfigService", "queryConfig");  
222 - transaction.addData("Url", url);  
223 - try {  
224 - // 发起请求,返回 HttpResponse 对象  
225 - HttpResponse<ApolloConfig> response = m_httpUtil.doGet(request, ApolloConfig.class);  
226 - // 设置 m_configNeedForceRefresh = false  
227 - m_configNeedForceRefresh.set(false);  
228 - // 标记成功  
229 - m_loadConfigFailSchedulePolicy.success();  
230 -  
231 - // 【TODO 6001】Tracer 日志  
232 - transaction.addData("StatusCode", response.getStatusCode());  
233 - transaction.setStatus(Transaction.SUCCESS);  
234 -  
235 - // 无新的配置,直接返回缓存的 ApolloConfig 对象  
236 - if (response.getStatusCode() == 304) {  
237 - logger.debug("Config server responds with 304 HTTP status code.");  
238 - return m_configCache.get();  
239 - }  
240 -  
241 - // 有新的配置,进行返回新的 ApolloConfig 对象  
242 - ApolloConfig result = response.getBody();  
243 - logger.debug("Loaded config for {}: {}", m_namespace, result);  
244 - return result;  
245 - } catch (ApolloConfigStatusCodeException ex) {  
246 - ApolloConfigStatusCodeException statusCodeException = ex;  
247 - // 若返回的状态码是 404 ,说明查询配置的 Config Service 不存在该 Namespace 。  
248 - // config not found  
249 - if (ex.getStatusCode() == 404) {  
250 - String message = String.format("Could not find config for namespace - appId: %s, cluster: %s, namespace: %s, " +  
251 - "please check whether the configs are released in Apollo!", appId, cluster, m_namespace);  
252 - statusCodeException = new ApolloConfigStatusCodeException(ex.getStatusCode(), message);  
253 - }  
254 - // 【TODO 6001】Tracer 日志  
255 - Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(statusCodeException));  
256 - transaction.setStatus(statusCodeException);  
257 - // 设置最终的异常  
258 - exception = statusCodeException;  
259 - } catch (Throwable ex) {  
260 - // 【TODO 6001】Tracer 日志  
261 - Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(ex));  
262 - transaction.setStatus(ex);  
263 - // 设置最终的异常  
264 - exception = ex;  
265 - } finally {  
266 - // 【TODO 6001】Tracer 日志  
267 - transaction.complete();  
268 - }  
269 - // 计算延迟时间  
270 - // if force refresh, do normal sleep, if normal config load, do exponential sleep  
271 - onErrorSleepTime = m_configNeedForceRefresh.get() ? m_configUtil.getOnErrorRetryInterval() : m_loadConfigFailSchedulePolicy.fail();  
272 - }  
273 -  
274 - }  
275 - // 若查询配置失败,抛出 ApolloConfigException 异常  
276 - String message = String.format("Load Apollo Config failed - appId: %s, cluster: %s, namespace: %s, url: %s", appId, cluster, m_namespace, url);  
277 - throw new ApolloConfigException(message, exception);  
278 - }  
279 -  
280 - // 组装查询配置的地址  
281 - String assembleQueryConfigUrl(String uri, String appId, String cluster, String namespace,  
282 - String dataCenter, ApolloNotificationMessages remoteMessages, ApolloConfig previousConfig) {  
283 - String path = "configs/%s/%s/%s"; // /configs/{appId}/{clusterName}/{namespace:.+}  
284 - List<String> pathParams = Lists.newArrayList(pathEscaper.escape(appId), pathEscaper.escape(cluster), pathEscaper.escape(namespace));  
285 - Map<String, String> queryParams = Maps.newHashMap();  
286 - // releaseKey  
287 - if (previousConfig != null) {  
288 - queryParams.put("releaseKey", queryParamEscaper.escape(previousConfig.getReleaseKey()));  
289 - }  
290 - // dataCenter  
291 - if (!Strings.isNullOrEmpty(dataCenter)) {  
292 - queryParams.put("dataCenter", queryParamEscaper.escape(dataCenter));  
293 - }  
294 - // ip  
295 - String localIp = m_configUtil.getLocalIp();  
296 - if (!Strings.isNullOrEmpty(localIp)) {  
297 - queryParams.put("ip", queryParamEscaper.escape(localIp));  
298 - }  
299 - // messages  
300 - if (remoteMessages != null) {  
301 - queryParams.put("messages", queryParamEscaper.escape(gson.toJson(remoteMessages)));  
302 - }  
303 - // 格式化 URL  
304 - String pathExpanded = String.format(path, pathParams.toArray());  
305 - // 拼接 Query String  
306 - if (!queryParams.isEmpty()) {  
307 - pathExpanded += "?" + MAP_JOINER.join(queryParams);  
308 - }  
309 - // 拼接最终的请求 URL  
310 - if (!uri.endsWith("/")) {  
311 - uri += "/";  
312 - }  
313 - return uri + pathExpanded;  
314 - }  
315 -  
316 - /**  
317 - * 注册自己到 RemoteConfigLongPollService 中,实现配置更新的实时通知  
318 - */  
319 - private void scheduleLongPollingRefresh() {  
320 - remoteConfigLongPollService.submit(m_namespace, this);  
321 - }  
322 -  
323 - /**  
324 - * 当长轮询到配置更新时,发起同步配置的任务  
325 - *  
326 - * @param longPollNotifiedServiceDto ServiceDTO 对象  
327 - * @param remoteMessages ApolloNotificationMessages 对象  
328 - */  
329 - public void onLongPollNotified(ServiceDTO longPollNotifiedServiceDto, ApolloNotificationMessages remoteMessages) {  
330 - // 提交同步任务  
331 - m_executorService.submit(new Runnable() {  
332 -  
333 - @Override  
334 - public void run() {  
335 - // 设置 m_configNeedForceRefresh 为 true  
336 - m_configNeedForceRefresh.set(true);  
337 - // 尝试同步配置  
338 - trySync();  
339 - }  
340 -  
341 - });  
342 - }  
343 -  
344 -  
345 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/internals/RepositoryChangeListener.java deleted
1 -package cn.iocoder.dashboard.framework.apollox.internals;  
2 -  
3 -import java.util.Properties;  
4 -  
5 -/**  
6 - * @author Jason Song(song_s@ctrip.com)  
7 - */  
8 -public interface RepositoryChangeListener {  
9 -  
10 - /**  
11 - * Invoked when config repository changes.  
12 - *  
13 - * @param namespace the namespace of this repository change  
14 - * @param newProperties the properties after change  
15 - */  
16 - void onRepositoryChange(String namespace, Properties newProperties);  
17 -  
18 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/model/ConfigChange.java deleted
1 -package cn.iocoder.dashboard.framework.apollox.model;  
2 -  
3 -  
4 -import cn.iocoder.dashboard.framework.apollox.enums.PropertyChangeType;  
5 -import lombok.AllArgsConstructor;  
6 -import lombok.Data;  
7 -  
8 -/**  
9 - * Holds the information for a config change.  
10 - * 配置每个属性变化的信息  
11 - *  
12 - * @author Jason Song(song_s@ctrip.com)  
13 - */  
14 -@Data  
15 -@AllArgsConstructor  
16 -public class ConfigChange {  
17 -  
18 - /**  
19 - * 属性名  
20 - */  
21 - private final String propertyName;  
22 - /**  
23 - * 老值  
24 - */  
25 - private String oldValue;  
26 - /**  
27 - * 新值  
28 - */  
29 - private String newValue;  
30 - /**  
31 - * 变化类型  
32 - */  
33 - private PropertyChangeType changeType;  
34 -  
35 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/model/ConfigChangeEvent.java deleted
1 -package cn.iocoder.dashboard.framework.apollox.model;  
2 -  
3 -import lombok.AllArgsConstructor;  
4 -  
5 -import java.util.Map;  
6 -import java.util.Set;  
7 -  
8 -/**  
9 - * A change event when a namespace's config is changed.  
10 - *  
11 - * @author Jason Song(song_s@ctrip.com)  
12 - */  
13 -@AllArgsConstructor  
14 -public class ConfigChangeEvent {  
15 -  
16 - /**  
17 - * 变化属性的集合  
18 - *  
19 - * KEY:属性名  
20 - * VALUE:配置变化  
21 - */  
22 - private final Map<String, ConfigChange> m_changes;  
23 -  
24 - /**  
25 - * Get the keys changed.  
26 - *  
27 - * @return the list of the keys  
28 - */  
29 - public Set<String> changedKeys() {  
30 - return m_changes.keySet();  
31 - }  
32 -  
33 - /**  
34 - * Get a specific change instance for the key specified.  
35 - *  
36 - * @param key the changed key  
37 - * @return the change instance  
38 - */  
39 - public ConfigChange getChange(String key) {  
40 - return m_changes.get(key);  
41 - }  
42 -  
43 - /**  
44 - * Check whether the specified key is changed  
45 - *  
46 - * @param key the key  
47 - * @return true if the key is changed, false otherwise.  
48 - */  
49 - public boolean isChanged(String key) {  
50 - return m_changes.containsKey(key);  
51 - }  
52 -  
53 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/package-info.java deleted
1 -/**  
2 - * 配置中心客户端,基于 Apollo Client 实现,所以叫 ApolloX  
3 - *  
4 - * 差别在于,我们使用 {@link cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.config.SysConfigDO} 表作为配置源。  
5 - * 当然,功能肯定也会相对少些,满足最小化诉求。  
6 - *  
7 - * 1. 项目初始化时,可以使用 SysConfigDO 表的配置  
8 - * 2. 使用 Spring @Value 可以注入属性  
9 - * 3. SysConfigDO 表的配置修改时,注入到 @Value 的属性可以刷新  
10 - *  
11 - * 另外,整个包结构会参考 Apollo 为主,方便维护与理解  
12 - *  
13 - * 注意,目前有两个特性是不支持的  
14 - * 1. 自定义配置变化的监听器  
15 - */  
16 -package cn.iocoder.dashboard.framework.apollox;  
src/main/java/cn/iocoder/dashboard/framework/apollox/spring/annotation/ApolloAnnotationProcessor.java deleted
1 -package cn.iocoder.dashboard.framework.apollox.spring.annotation;  
2 -  
3 -import cn.hutool.core.lang.Singleton;  
4 -import cn.iocoder.dashboard.framework.apollox.Config;  
5 -import cn.iocoder.dashboard.framework.apollox.ConfigChangeListener;  
6 -import cn.iocoder.dashboard.framework.apollox.DefaultConfig;  
7 -import cn.iocoder.dashboard.framework.apollox.model.ConfigChangeEvent;  
8 -import com.google.common.base.Preconditions;  
9 -import org.springframework.core.annotation.AnnotationUtils;  
10 -import org.springframework.util.ReflectionUtils;  
11 -  
12 -import java.lang.reflect.Field;  
13 -import java.lang.reflect.Method;  
14 -  
15 -/**  
16 - * Apollo Annotation Processor for Spring Application  
17 - *  
18 - * @author Jason Song(song_s@ctrip.com)  
19 - */  
20 -public class ApolloAnnotationProcessor extends ApolloProcessor {  
21 -  
22 - @Override  
23 - protected void processField(Object bean, String beanName, Field field) {  
24 - ApolloConfig annotation = AnnotationUtils.getAnnotation(field, ApolloConfig.class);  
25 - if (annotation == null) {  
26 - return;  
27 - }  
28 -  
29 - Preconditions.checkArgument(Config.class.isAssignableFrom(field.getType()), "Invalid type: %s for field: %s, should be Config", field.getType(), field);  
30 -  
31 - // 创建 Config 对象  
32 - Config config = Singleton.get(DefaultConfig.class);  
33 -  
34 - // 设置 Config 对象,到对应的 Field  
35 - ReflectionUtils.makeAccessible(field);  
36 - ReflectionUtils.setField(field, bean, config);  
37 - }  
38 -  
39 - @Override  
40 - protected void processMethod(final Object bean, String beanName, final Method method) {  
41 - ApolloConfigChangeListener annotation = AnnotationUtils.findAnnotation(method, ApolloConfigChangeListener.class);  
42 - if (annotation == null) {  
43 - return;  
44 - }  
45 - Class<?>[] parameterTypes = method.getParameterTypes();  
46 - Preconditions.checkArgument(parameterTypes.length == 1, "Invalid number of parameters: %s for method: %s, should be 1", parameterTypes.length, method);  
47 - Preconditions.checkArgument(ConfigChangeEvent.class.isAssignableFrom(parameterTypes[0]), "Invalid parameter type: %s for method: %s, should be ConfigChangeEvent", parameterTypes[0], method);  
48 -  
49 - // 创建 ConfigChangeListener 监听器。该监听器会调用被注解的方法。  
50 - ReflectionUtils.makeAccessible(method);  
51 - ConfigChangeListener configChangeListener = changeEvent -> {  
52 - // 反射调用  
53 - ReflectionUtils.invokeMethod(method, bean, changeEvent);  
54 - };  
55 -  
56 - // 向指定 Namespace 的 Config 对象们,注册该监听器  
57 - Config config = Singleton.get(DefaultConfig.class);  
58 - config.addChangeListener(configChangeListener);  
59 - }  
60 -  
61 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/spring/annotation/ApolloConfig.java deleted
1 -package cn.iocoder.dashboard.framework.apollox.spring.annotation;  
2 -  
3 -import java.lang.annotation.*;  
4 -  
5 -/**  
6 - * Use this annotation to inject Apollo Config Instance.  
7 - *  
8 - * <p>Usage example:</p>  
9 - * <pre class="code">  
10 - * //Inject the config for "someNamespace"  
11 - * &#064;ApolloConfig("someNamespace")  
12 - * private Config config;  
13 - * </pre>  
14 - *  
15 - * @author Jason Song(song_s@ctrip.com)  
16 - */  
17 -@Retention(RetentionPolicy.RUNTIME)  
18 -@Target(ElementType.FIELD)  
19 -@Documented  
20 -public @interface ApolloConfig {  
21 -  
22 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/spring/annotation/ApolloConfigChangeListener.java deleted
1 -package cn.iocoder.dashboard.framework.apollox.spring.annotation;  
2 -  
3 -import java.lang.annotation.*;  
4 -  
5 -/**  
6 - * Use this annotation to register Apollo ConfigChangeListener.  
7 - *  
8 - * @author Jason Song(song_s@ctrip.com)  
9 - */  
10 -@Retention(RetentionPolicy.RUNTIME)  
11 -@Target(ElementType.METHOD)  
12 -@Documented  
13 -public @interface ApolloConfigChangeListener {  
14 -  
15 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/spring/annotation/ApolloProcessor.java deleted
1 -package cn.iocoder.dashboard.framework.apollox.spring.annotation;  
2 -  
3 -import org.springframework.beans.BeansException;  
4 -import org.springframework.beans.factory.config.BeanPostProcessor;  
5 -import org.springframework.core.Ordered;  
6 -import org.springframework.core.PriorityOrdered;  
7 -import org.springframework.util.ReflectionUtils;  
8 -  
9 -import java.lang.reflect.Field;  
10 -import java.lang.reflect.Method;  
11 -import java.util.LinkedList;  
12 -import java.util.List;  
13 -  
14 -/**  
15 - * Apollo 处理器抽象类,封装了在 Spring Bean 初始化之前,处理属性和方法。  
16 - *  
17 - * Create by zhangzheng on 2018/2/6  
18 - */  
19 -public abstract class ApolloProcessor implements BeanPostProcessor, PriorityOrdered {  
20 -  
21 - @Override  
22 - public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {  
23 - Class<?> clazz = bean.getClass();  
24 - // 处理所有 Field  
25 - for (Field field : findAllField(clazz)) {  
26 - processField(bean, beanName, field);  
27 - }  
28 - // 处理所有的 Method  
29 - for (Method method : findAllMethod(clazz)) {  
30 - processMethod(bean, beanName, method);  
31 - }  
32 - return bean;  
33 - }  
34 -  
35 - @Override  
36 - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {  
37 - return bean;  
38 - }  
39 -  
40 - /**  
41 - * subclass should implement this method to process field  
42 - */  
43 - protected abstract void processField(Object bean, String beanName, Field field);  
44 -  
45 - /**  
46 - * subclass should implement this method to process method  
47 - */  
48 - protected abstract void processMethod(Object bean, String beanName, Method method);  
49 -  
50 - @Override  
51 - public int getOrder() {  
52 - // make it as late as possible  
53 - return Ordered.LOWEST_PRECEDENCE; // 最高优先级  
54 - }  
55 -  
56 - private List<Field> findAllField(Class<?> clazz) {  
57 - final List<Field> res = new LinkedList<>();  
58 - ReflectionUtils.doWithFields(clazz, res::add);  
59 - return res;  
60 - }  
61 -  
62 - private List<Method> findAllMethod(Class<?> clazz) {  
63 - final List<Method> res = new LinkedList<>();  
64 - ReflectionUtils.doWithMethods(clazz, res::add);  
65 - return res;  
66 - }  
67 -  
68 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/spring/annotation/SpringValueProcessor.java deleted
1 -package cn.iocoder.dashboard.framework.apollox.spring.annotation;  
2 -  
3 -import cn.hutool.core.lang.Singleton;  
4 -import cn.iocoder.dashboard.framework.apollox.spring.property.*;  
5 -import com.google.common.collect.LinkedListMultimap;  
6 -import com.google.common.collect.Multimap;  
7 -import lombok.extern.slf4j.Slf4j;  
8 -import org.springframework.beans.BeanUtils;  
9 -import org.springframework.beans.BeansException;  
10 -import org.springframework.beans.factory.annotation.Value;  
11 -import org.springframework.beans.factory.config.BeanFactoryPostProcessor;  
12 -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;  
13 -import org.springframework.context.annotation.Bean;  
14 -  
15 -import java.beans.PropertyDescriptor;  
16 -import java.lang.reflect.Field;  
17 -import java.lang.reflect.Method;  
18 -import java.util.Collection;  
19 -import java.util.Set;  
20 -  
21 -/**  
22 - * Spring value processor of field or method which has @Value and xml config placeholders.  
23 - *  
24 - * Spring Value 处理器,处理:  
25 - *  
26 - * 1. 带有 `@Value` 注解的 Field 和 Method  
27 - * 2. XML 配置的 Bean 的 PlaceHolder 们  
28 - *  
29 - * 每个 Field、Method、XML PlaceHolder 被处理成一个 SpringValue 对象,添加到 SpringValueRegistry 中。  
30 - *  
31 - * 目的还是,为了 PlaceHolder 的自动更新机制。  
32 - *  
33 - * @author github.com/zhegexiaohuozi seimimaster@gmail.com  
34 - * @since 2017/12/20.  
35 - */  
36 -@Slf4j  
37 -public class SpringValueProcessor extends ApolloProcessor implements BeanFactoryPostProcessor {  
38 -  
39 - /**  
40 - * SpringValueDefinition 集合  
41 - *  
42 - * KEY:beanName  
43 - * VALUE:SpringValueDefinition 集合  
44 - */  
45 - private static Multimap<String, SpringValueDefinition> beanName2SpringValueDefinitions = LinkedListMultimap.create();  
46 -  
47 - private final PlaceholderHelper placeholderHelper = Singleton.get(PlaceholderHelper.class);  
48 - private final SpringValueRegistry springValueRegistry = Singleton.get(SpringValueRegistry.class);  
49 -  
50 - @Override  
51 - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {  
52 - beanName2SpringValueDefinitions = SpringValueDefinitionProcessor.getBeanName2SpringValueDefinitions();  
53 - }  
54 -  
55 - @Override  
56 - public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {  
57 - // 处理 Field 和 Method  
58 - super.postProcessBeforeInitialization(bean, beanName);  
59 - // 处理 XML 配置的 Bean 的 PlaceHolder 们  
60 - processBeanPropertyValues(bean, beanName);  
61 - return bean;  
62 - }  
63 -  
64 - @Override  
65 - protected void processField(Object bean, String beanName, Field field) {  
66 - // register @Value on field  
67 - Value value = field.getAnnotation(Value.class);  
68 - if (value == null) {  
69 - return;  
70 - }  
71 - // 提取 `keys` 属性们。  
72 - Set<String> keys = placeholderHelper.extractPlaceholderKeys(value.value());  
73 - if (keys.isEmpty()) {  
74 - return;  
75 - }  
76 - // 循环 `keys` ,创建对应的 SpringValue 对象,并添加到 `springValueRegistry` 中。  
77 - for (String key : keys) {  
78 - SpringValue springValue = new SpringValue(key, value.value(), bean, beanName, field, false);  
79 - springValueRegistry.register(key, springValue);  
80 - log.debug("Monitoring {}", springValue);  
81 - }  
82 - }  
83 -  
84 - @Override  
85 - protected void processMethod(Object bean, String beanName, Method method) {  
86 - // register @Value on method  
87 - Value value = method.getAnnotation(Value.class);  
88 - if (value == null) {  
89 - return;  
90 - }  
91 - // 忽略 @Bean 注解的方法  
92 - // skip Configuration bean methods  
93 - if (method.getAnnotation(Bean.class) != null) {  
94 - return;  
95 - }  
96 - // 忽略非 setting 方法  
97 - if (method.getParameterTypes().length != 1) {  
98 - log.error("Ignore @Value setter {}.{}, expecting 1 parameter, actual {} parameters", bean.getClass().getName(), method.getName(), method.getParameterTypes().length);  
99 - return;  
100 - }  
101 - // 提取 `keys` 属性们。  
102 - Set<String> keys = placeholderHelper.extractPlaceholderKeys(value.value());  
103 - if (keys.isEmpty()) {  
104 - return;  
105 - }  
106 - // 循环 `keys` ,创建对应的 SpringValue 对象,并添加到 `springValueRegistry` 中。  
107 - for (String key : keys) {  
108 - SpringValue springValue = new SpringValue(key, value.value(), bean, beanName, method, false);  
109 - springValueRegistry.register(key, springValue);  
110 - log.info("Monitoring {}", springValue);  
111 - }  
112 - }  
113 -  
114 - private void processBeanPropertyValues(Object bean, String beanName) {  
115 - // 获得 SpringValueDefinition 数组  
116 - Collection<SpringValueDefinition> propertySpringValues = beanName2SpringValueDefinitions.get(beanName);  
117 - if (propertySpringValues == null || propertySpringValues.isEmpty()) {  
118 - return;  
119 - }  
120 - // 循环 SpringValueDefinition 数组,创建对应的 SpringValue 对象,并添加到 `springValueRegistry` 中。  
121 - for (SpringValueDefinition definition : propertySpringValues) {  
122 - try {  
123 - PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(bean.getClass(), definition.getPropertyName());  
124 - Method method = pd.getWriteMethod();  
125 - if (method == null) {  
126 - continue;  
127 - }  
128 - SpringValue springValue = new SpringValue(definition.getKey(), definition.getPlaceholder(), bean, beanName, method, false);  
129 - springValueRegistry.register(definition.getKey(), springValue);  
130 - log.debug("Monitoring {}", springValue);  
131 - } catch (Throwable ex) {  
132 - log.error("Failed to enable auto update feature for {}.{}", bean.getClass(), definition.getPropertyName());  
133 - }  
134 - }  
135 -  
136 - // clear  
137 - // 移除 Bean 对应的 SpringValueDefinition 数组  
138 - beanName2SpringValueDefinitions.removeAll(beanName);  
139 - }  
140 -  
141 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/spring/boot/ApolloApplicationContextInitializer.java deleted
1 -package cn.iocoder.dashboard.framework.apollox.spring.boot;  
2 -  
3 -  
4 -import cn.iocoder.dashboard.framework.apollox.Config;  
5 -import cn.iocoder.dashboard.framework.apollox.ConfigService;  
6 -import cn.iocoder.dashboard.framework.apollox.spring.config.ConfigPropertySourceFactory;  
7 -import cn.iocoder.dashboard.framework.apollox.spring.config.PropertySourcesConstants;  
8 -import cn.iocoder.dashboard.framework.apollox.spring.util.SpringInjector;  
9 -import lombok.extern.slf4j.Slf4j;  
10 -import org.springframework.context.ApplicationContextInitializer;  
11 -import org.springframework.context.ConfigurableApplicationContext;  
12 -import org.springframework.core.env.CompositePropertySource;  
13 -import org.springframework.core.env.ConfigurableEnvironment;  
14 -  
15 -import java.util.Collections;  
16 -import java.util.List;  
17 -  
18 -import static cn.iocoder.dashboard.framework.apollox.spring.config.PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME;  
19 -  
20 -@Slf4j  
21 -public class ApolloApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {  
22 -  
23 - private final ConfigPropertySourceFactory configPropertySourceFactory = SpringInjector.getInstance(ConfigPropertySourceFactory.class);  
24 -  
25 - @Override  
26 - public void initialize(ConfigurableApplicationContext context) {  
27 - ConfigurableEnvironment environment = context.getEnvironment();  
28 - // 忽略,若已经有 APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME 的 PropertySource  
29 - if (environment.getPropertySources().contains(APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME)) {  
30 - // already initialized  
31 - return;  
32 - }  
33 -  
34 - // 忽略,若已经有 APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME 的 PropertySource  
35 - if (environment.getPropertySources().contains(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME)) {  
36 - // already initialized  
37 - return;  
38 - }  
39 -  
40 - // 获得 "apollo.bootstrap.namespaces" 配置项  
41 - List<String> namespaceList = Collections.singletonList("default");  
42 -  
43 - // 按照优先级,顺序遍历 Namespace  
44 - CompositePropertySource composite = new CompositePropertySource(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME);  
45 - for (String namespace : namespaceList) {  
46 - // 创建 Apollo Config 对象  
47 - Config config = ConfigService.getConfig(namespace);  
48 - // 创建 Namespace 对应的 ConfigPropertySource 对象  
49 - // 添加到 `composite` 中。  
50 - composite.addPropertySource(configPropertySourceFactory.getConfigPropertySource(namespace, config));  
51 - }  
52 -  
53 - // 添加到 `environment` 中,且优先级最高  
54 - environment.getPropertySources().addFirst(composite);  
55 - }  
56 -  
57 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/spring/boot/ApolloAutoConfiguration.java deleted
1 -package cn.iocoder.dashboard.framework.apollox.spring.boot;  
2 -  
3 -import cn.iocoder.dashboard.framework.apollox.spring.config.ConfigPropertySourcesProcessor;  
4 -import cn.iocoder.dashboard.framework.apollox.spring.property.PropertySourcesProcessor;  
5 -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;  
6 -import org.springframework.context.annotation.Bean;  
7 -import org.springframework.context.annotation.Configuration;  
8 -  
9 -@Configuration  
10 -@ConditionalOnMissingBean(PropertySourcesProcessor.class) // 缺失 PropertySourcesProcessor 时  
11 -public class ApolloAutoConfiguration {  
12 -  
13 - @Bean  
14 - public ConfigPropertySourcesProcessor configPropertySourcesProcessor() {  
15 - return new ConfigPropertySourcesProcessor(); // 注入 ConfigPropertySourcesProcessor bean 对象  
16 - }  
17 -  
18 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/spring/config/ConfigPropertySource.java deleted
1 -package cn.iocoder.dashboard.framework.apollox.spring.config;  
2 -  
3 -import cn.iocoder.dashboard.framework.apollox.Config;  
4 -import cn.iocoder.dashboard.framework.apollox.ConfigChangeListener;  
5 -import org.springframework.core.env.EnumerablePropertySource;  
6 -  
7 -import java.util.Set;  
8 -  
9 -/**  
10 - * Property source wrapper for Config  
11 - *  
12 - * 基于 {@link Config} 的 PropertySource 实现类  
13 - *  
14 - * @author Jason Song(song_s@ctrip.com)  
15 - */  
16 -public class ConfigPropertySource extends EnumerablePropertySource<Config> {  
17 -  
18 - private static final String[] EMPTY_ARRAY = new String[0];  
19 -  
20 - ConfigPropertySource(String name, Config source) { // 此处的 Apollo Config 作为 `source`  
21 - super(name, source);  
22 - }  
23 -  
24 - @Override  
25 - public String[] getPropertyNames() {  
26 - // 从 Config 中,获得属性名集合  
27 - Set<String> propertyNames = this.source.getPropertyNames();  
28 - // 转换成 String 数组,返回  
29 - if (propertyNames.isEmpty()) {  
30 - return EMPTY_ARRAY;  
31 - }  
32 - return propertyNames.toArray(new String[0]);  
33 - }  
34 -  
35 - @Override  
36 - public Object getProperty(String name) {  
37 - return this.source.getProperty(name, null);  
38 - }  
39 -  
40 - /**  
41 - * 添加 ConfigChangeListener 到 Config 中  
42 - *  
43 - * @param listener 监听器  
44 - */  
45 - public void addChangeListener(ConfigChangeListener listener) {  
46 - this.source.addChangeListener(listener);  
47 - }  
48 -  
49 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/spring/config/ConfigPropertySourceFactory.java deleted
1 -package cn.iocoder.dashboard.framework.apollox.spring.config;  
2 -  
3 -import cn.iocoder.dashboard.framework.apollox.Config;  
4 -import com.google.common.collect.Lists;  
5 -  
6 -import java.util.List;  
7 -  
8 -/**  
9 - * {@link ConfigPropertySource} 工厂  
10 - */  
11 -public class ConfigPropertySourceFactory {  
12 -  
13 - /**  
14 - * ConfigPropertySource 数组  
15 - */  
16 - private final List<ConfigPropertySource> configPropertySources = Lists.newLinkedList();  
17 -  
18 - // 创建 ConfigPropertySource 对象  
19 - public ConfigPropertySource getConfigPropertySource(String name, Config source) {  
20 - // 创建 ConfigPropertySource 对象  
21 - ConfigPropertySource configPropertySource = new ConfigPropertySource(name, source);  
22 - // 添加到数组中  
23 - configPropertySources.add(configPropertySource);  
24 - return configPropertySource;  
25 - }  
26 -  
27 - public List<ConfigPropertySource> getAllConfigPropertySources() {  
28 - return Lists.newLinkedList(configPropertySources);  
29 - }  
30 -  
31 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/spring/config/ConfigPropertySourcesProcessor.java deleted
1 -package cn.iocoder.dashboard.framework.apollox.spring.config;  
2 -  
3 -import cn.iocoder.dashboard.framework.apollox.spring.annotation.ApolloAnnotationProcessor;  
4 -import cn.iocoder.dashboard.framework.apollox.spring.annotation.SpringValueProcessor;  
5 -import cn.iocoder.dashboard.framework.apollox.spring.property.PropertySourcesProcessor;  
6 -import cn.iocoder.dashboard.framework.apollox.spring.property.SpringValueDefinitionProcessor;  
7 -import cn.iocoder.dashboard.framework.apollox.spring.util.BeanRegistrationUtil;  
8 -import org.springframework.beans.BeansException;  
9 -import org.springframework.beans.factory.support.BeanDefinitionRegistry;  
10 -import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;  
11 -import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;  
12 -  
13 -/**  
14 - * Apollo Property Sources processor for Spring XML Based Application  
15 - *  
16 - * @author Jason Song(song_s@ctrip.com)  
17 - */  
18 -public class ConfigPropertySourcesProcessor extends PropertySourcesProcessor implements BeanDefinitionRegistryPostProcessor {  
19 -  
20 - @Override  
21 - public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {  
22 - // 注册 PropertySourcesPlaceholderConfigurer 到 BeanDefinitionRegistry 中,替换 PlaceHolder 为对应的属性值,参考文章 https://leokongwq.github.io/2016/12/28/spring-PropertyPlaceholderConfigurer.html  
23 - BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, PropertySourcesPlaceholderConfigurer.class.getName(), PropertySourcesPlaceholderConfigurer.class);  
24 - // 注册 ApolloAnnotationProcessor 到 BeanDefinitionRegistry 中,因为 XML 配置的 Bean 对象,也可能存在 @ApolloConfig 和 @ApolloConfigChangeListener 注解。  
25 - BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, ApolloAnnotationProcessor.class.getName(), ApolloAnnotationProcessor.class);  
26 - // 注册 SpringValueProcessor 到 BeanDefinitionRegistry 中,用于 PlaceHolder 自动更新机制  
27 - BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, SpringValueProcessor.class.getName(), SpringValueProcessor.class);  
28 - // 注册 ApolloJsonValueProcessor 到 BeanDefinitionRegistry 中,因为 XML 配置的 Bean 对象,也可能存在 @ApolloJsonValue 注解。  
29 -// BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, ApolloJsonValueProcessor.class.getName(), ApolloJsonValueProcessor.class); TODO 芋艿:暂时不需要迁移  
30 -  
31 - // 处理 XML 配置的 Spring PlaceHolder  
32 - processSpringValueDefinition(registry);  
33 - }  
34 -  
35 - /**  
36 - * For Spring 3.x versions, the BeanDefinitionRegistryPostProcessor would not be  
37 - * instantiated if it is added in postProcessBeanDefinitionRegistry phase, so we have to manually  
38 - * call the postProcessBeanDefinitionRegistry method of SpringValueDefinitionProcessor here...  
39 - */  
40 - private void processSpringValueDefinition(BeanDefinitionRegistry registry) {  
41 - // 创建 SpringValueDefinitionProcessor 对象  
42 - SpringValueDefinitionProcessor springValueDefinitionProcessor = new SpringValueDefinitionProcessor();  
43 - // 处理 XML 配置的 Spring PlaceHolder  
44 - springValueDefinitionProcessor.postProcessBeanDefinitionRegistry(registry);  
45 - }  
46 -  
47 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/spring/config/PropertySourcesConstants.java deleted
1 -package cn.iocoder.dashboard.framework.apollox.spring.config;  
2 -  
3 -public interface PropertySourcesConstants {  
4 -  
5 - String APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME = "ApolloBootstrapPropertySources";  
6 -  
7 - String APOLLO_PROPERTY_SOURCE_NAME = "ApolloPropertySources";  
8 -  
9 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/spring/property/AutoUpdateConfigChangeListener.java deleted
1 -package cn.iocoder.dashboard.framework.apollox.spring.property;  
2 -  
3 -import cn.hutool.core.lang.Singleton;  
4 -import cn.iocoder.dashboard.framework.apollox.ConfigChangeListener;  
5 -import cn.iocoder.dashboard.framework.apollox.enums.PropertyChangeType;  
6 -import cn.iocoder.dashboard.framework.apollox.model.ConfigChange;  
7 -import cn.iocoder.dashboard.framework.apollox.model.ConfigChangeEvent;  
8 -import com.alibaba.fastjson.JSON;  
9 -import org.slf4j.Logger;  
10 -import org.slf4j.LoggerFactory;  
11 -import org.springframework.beans.TypeConverter;  
12 -import org.springframework.beans.factory.config.ConfigurableBeanFactory;  
13 -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;  
14 -import org.springframework.core.env.Environment;  
15 -import org.springframework.util.CollectionUtils;  
16 -  
17 -import java.lang.reflect.Field;  
18 -import java.lang.reflect.Type;  
19 -import java.util.Collection;  
20 -import java.util.Objects;  
21 -import java.util.Set;  
22 -  
23 -/**  
24 - * 自动更新配置监听器  
25 - *  
26 - * Create by zhangzheng on 2018/3/6  
27 - */  
28 -public class AutoUpdateConfigChangeListener implements ConfigChangeListener {  
29 -  
30 - private static final Logger logger = LoggerFactory.getLogger(AutoUpdateConfigChangeListener.class);  
31 -  
32 - /**  
33 - * {@link TypeConverter#convertIfNecessary(Object, Class, Field)} 是否带上 Field 参数,因为 Spring 3.2.0+ 才有该方法  
34 - */  
35 - private final boolean typeConverterHasConvertIfNecessaryWithFieldParameter;  
36 - private final Environment environment;  
37 - private final ConfigurableBeanFactory beanFactory;  
38 - /**  
39 - * TypeConverter 对象,参见 https://blog.csdn.net/rulerp2014/article/details/51100857  
40 - */  
41 - private final TypeConverter typeConverter;  
42 - private final PlaceholderHelper placeholderHelper;  
43 - private final SpringValueRegistry springValueRegistry;  
44 -  
45 - public AutoUpdateConfigChangeListener(Environment environment, ConfigurableListableBeanFactory beanFactory) {  
46 - this.typeConverterHasConvertIfNecessaryWithFieldParameter = testTypeConverterHasConvertIfNecessaryWithFieldParameter();  
47 - this.beanFactory = beanFactory;  
48 - this.typeConverter = this.beanFactory.getTypeConverter();  
49 - this.environment = environment;  
50 - this.placeholderHelper = Singleton.get(PlaceholderHelper.class);  
51 - this.springValueRegistry = Singleton.get(SpringValueRegistry.class);  
52 - }  
53 -  
54 - @Override  
55 - public void onChange(ConfigChangeEvent changeEvent) {  
56 - // 获得更新的 KEY 集合  
57 - Set<String> keys = changeEvent.changedKeys();  
58 - if (CollectionUtils.isEmpty(keys)) {  
59 - return;  
60 - }  
61 - // 循环 KEY 集合,更新 StringValue  
62 - for (String key : keys) {  
63 - // 忽略,若不在 SpringValueRegistry 中  
64 - // 1. check whether the changed key is relevant  
65 - Collection<SpringValue> targetValues = springValueRegistry.get(key);  
66 - if (targetValues == null || targetValues.isEmpty()) {  
67 - continue;  
68 - }  
69 - // 校验是否需要更新  
70 - // 2. check whether the value is really changed or not (since spring property sources have hierarchies)  
71 - if (!shouldTriggerAutoUpdate(changeEvent, key)) {  
72 - continue;  
73 - }  
74 - // 循环,更新 SpringValue  
75 - // 3. update the value  
76 - for (SpringValue val : targetValues) {  
77 - updateSpringValue(val);  
78 - }  
79 - }  
80 - }  
81 -  
82 - /**  
83 - * Check whether we should trigger the auto update or not.  
84 - * <br />  
85 - * For added or modified keys, we should trigger auto update if the current value in Spring equals to the new value.  
86 - * <br />  
87 - * For deleted keys, we will trigger auto update anyway.  
88 - */  
89 - private boolean shouldTriggerAutoUpdate(ConfigChangeEvent changeEvent, String changedKey) {  
90 - ConfigChange configChange = changeEvent.getChange(changedKey);  
91 - // 若变更类型为删除,需要触发更新  
92 - if (configChange.getChangeType() == PropertyChangeType.DELETED) {  
93 - return true;  
94 - }  
95 - // 若变更类型为新增或修改,判断 environment 的值是否和最新值相等。  
96 - // 【高能】!!!  
97 - return Objects.equals(environment.getProperty(changedKey), configChange.getNewValue());  
98 - }  
99 -  
100 - private void updateSpringValue(SpringValue springValue) {  
101 - try {  
102 - // 解析值  
103 - Object value = resolvePropertyValue(springValue);  
104 - // 更新 StringValue  
105 - springValue.update(value);  
106 - logger.info("Auto update apollo changed value successfully, new value: {}, {}", value, springValue);  
107 - } catch (Throwable ex) {  
108 - logger.error("Auto update apollo changed value failed, {}", springValue.toString(), ex);  
109 - }  
110 - }  
111 -  
112 - /**  
113 - * Logic transplanted from DefaultListableBeanFactory  
114 - *  
115 - * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency(org.springframework.beans.factory.config.DependencyDescriptor, String, Set, TypeConverter)  
116 - */  
117 - private Object resolvePropertyValue(SpringValue springValue) {  
118 - // value will never be null, as @Value and @ApolloJsonValue will not allow that  
119 - Object value = placeholderHelper.resolvePropertyValue(beanFactory, springValue.getBeanName(), springValue.getPlaceholder());  
120 - // 如果值数据结构是 JSON 类型,则使用 Gson 解析成对应值的类型  
121 - if (springValue.isJson()) {  
122 - value = parseJsonValue((String) value, springValue.getGenericType());  
123 - } else {  
124 - // 如果类型为 Field  
125 - if (springValue.isField()) {  
126 - // org.springframework.beans.TypeConverter#convertIfNecessary(java.lang.Object, java.lang.Class, java.lang.reflect.Field) is available from Spring 3.2.0+  
127 - if (typeConverterHasConvertIfNecessaryWithFieldParameter) {  
128 - value = this.typeConverter.convertIfNecessary(value, springValue.getTargetType(), springValue.getField());  
129 - } else {  
130 - value = this.typeConverter.convertIfNecessary(value, springValue.getTargetType());  
131 - }  
132 - // 如果类型为 Method  
133 - } else {  
134 - value = this.typeConverter.convertIfNecessary(value, springValue.getTargetType(), springValue.getMethodParameter());  
135 - }  
136 - }  
137 -  
138 - return value;  
139 - }  
140 -  
141 - private Object parseJsonValue(String json, Type targetType) {  
142 - try {  
143 - return JSON.parseObject(json, targetType);  
144 - } catch (Throwable ex) {  
145 - logger.error("Parsing json '{}' to type {} failed!", json, targetType, ex);  
146 - throw ex;  
147 - }  
148 - }  
149 -  
150 - private boolean testTypeConverterHasConvertIfNecessaryWithFieldParameter() {  
151 - try {  
152 - TypeConverter.class.getMethod("convertIfNecessary", Object.class, Class.class, Field.class);  
153 - } catch (Throwable ex) {  
154 - return false;  
155 - }  
156 - return true;  
157 - }  
158 -  
159 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/spring/property/PlaceholderHelper.java deleted
1 -package cn.iocoder.dashboard.framework.apollox.spring.property;  
2 -  
3 -import com.google.common.base.Strings;  
4 -import com.google.common.collect.Sets;  
5 -import org.springframework.beans.factory.config.BeanDefinition;  
6 -import org.springframework.beans.factory.config.BeanExpressionContext;  
7 -import org.springframework.beans.factory.config.ConfigurableBeanFactory;  
8 -import org.springframework.beans.factory.config.Scope;  
9 -import org.springframework.util.StringUtils;  
10 -  
11 -import java.util.Set;  
12 -import java.util.Stack;  
13 -  
14 -/**  
15 - * Placeholder 工具类  
16 - *  
17 - * Placeholder helper functions.  
18 - */  
19 -public class PlaceholderHelper {  
20 -  
21 - private static final String PLACEHOLDER_PREFIX = "${";  
22 - private static final String PLACEHOLDER_SUFFIX = "}";  
23 - private static final String VALUE_SEPARATOR = ":";  
24 - private static final String SIMPLE_PLACEHOLDER_PREFIX = "{";  
25 - private static final String EXPRESSION_PREFIX = "#{";  
26 - private static final String EXPRESSION_SUFFIX = "}";  
27 -  
28 - /**  
29 - * Resolve placeholder property values, e.g.  
30 - *  
31 - * "${somePropertyValue}" -> "the actual property value"  
32 - */  
33 - public Object resolvePropertyValue(ConfigurableBeanFactory beanFactory, String beanName, String placeholder) {  
34 - // resolve string value  
35 - String strVal = beanFactory.resolveEmbeddedValue(placeholder);  
36 - // 获得 BeanDefinition 对象  
37 - BeanDefinition bd = (beanFactory.containsBean(beanName) ? beanFactory.getMergedBeanDefinition(beanName) : null);  
38 - // resolve expressions like "#{systemProperties.myProp}"  
39 - return evaluateBeanDefinitionString(beanFactory, strVal, bd);  
40 - }  
41 -  
42 - private Object evaluateBeanDefinitionString(ConfigurableBeanFactory beanFactory, String value, BeanDefinition beanDefinition) {  
43 - if (beanFactory.getBeanExpressionResolver() == null) {  
44 - return value;  
45 - }  
46 - Scope scope = (beanDefinition != null ? beanFactory.getRegisteredScope(beanDefinition.getScope()) : null);  
47 - return beanFactory.getBeanExpressionResolver().evaluate(value, new BeanExpressionContext(beanFactory, scope));  
48 - }  
49 -  
50 - /**  
51 - * Extract keys from placeholder, e.g.  
52 - * <ul>  
53 - * <li>${some.key} => "some.key"</li>  
54 - * <li>${some.key:${some.other.key:100}} => "some.key", "some.other.key"</li>  
55 - * <li>${${some.key}} => "some.key"</li>  
56 - * <li>${${some.key:other.key}} => "some.key"</li>  
57 - * <li>${${some.key}:${another.key}} => "some.key", "another.key"</li>  
58 - * <li>#{new java.text.SimpleDateFormat('${some.key}').parse('${another.key}')} => "some.key", "another.key"</li>  
59 - * </ul>  
60 - */  
61 - public Set<String> extractPlaceholderKeys(String propertyString) {  
62 - Set<String> placeholderKeys = Sets.newHashSet();  
63 -  
64 - if (!isNormalizedPlaceholder(propertyString) && !isExpressionWithPlaceholder(propertyString)) {  
65 - return placeholderKeys;  
66 - }  
67 -  
68 - Stack<String> stack = new Stack<>();  
69 - stack.push(propertyString);  
70 -  
71 - while (!stack.isEmpty()) {  
72 - String strVal = stack.pop();  
73 - int startIndex = strVal.indexOf(PLACEHOLDER_PREFIX);  
74 - if (startIndex == -1) {  
75 - placeholderKeys.add(strVal);  
76 - continue;  
77 - }  
78 - int endIndex = findPlaceholderEndIndex(strVal, startIndex);  
79 - if (endIndex == -1) {  
80 - // invalid placeholder?  
81 - continue;  
82 - }  
83 -  
84 - String placeholderCandidate = strVal.substring(startIndex + PLACEHOLDER_PREFIX.length(), endIndex);  
85 -  
86 - // ${some.key:other.key}  
87 - if (placeholderCandidate.startsWith(PLACEHOLDER_PREFIX)) {  
88 - stack.push(placeholderCandidate);  
89 - } else {  
90 - // some.key:${some.other.key:100}  
91 - int separatorIndex = placeholderCandidate.indexOf(VALUE_SEPARATOR);  
92 -  
93 - if (separatorIndex == -1) {  
94 - stack.push(placeholderCandidate);  
95 - } else {  
96 - stack.push(placeholderCandidate.substring(0, separatorIndex));  
97 - String defaultValuePart =  
98 - normalizeToPlaceholder(placeholderCandidate.substring(separatorIndex + VALUE_SEPARATOR.length()));  
99 - if (!Strings.isNullOrEmpty(defaultValuePart)) {  
100 - stack.push(defaultValuePart);  
101 - }  
102 - }  
103 - }  
104 -  
105 - // has remaining part, e.g. ${a}.${b}  
106 - if (endIndex + PLACEHOLDER_SUFFIX.length() < strVal.length() - 1) {  
107 - String remainingPart = normalizeToPlaceholder(strVal.substring(endIndex + PLACEHOLDER_SUFFIX.length()));  
108 - if (!Strings.isNullOrEmpty(remainingPart)) {  
109 - stack.push(remainingPart);  
110 - }  
111 - }  
112 - }  
113 -  
114 - return placeholderKeys;  
115 - }  
116 -  
117 - private boolean isNormalizedPlaceholder(String propertyString) {  
118 - return propertyString.startsWith(PLACEHOLDER_PREFIX) && propertyString.endsWith(PLACEHOLDER_SUFFIX);  
119 - }  
120 -  
121 - private boolean isExpressionWithPlaceholder(String propertyString) {  
122 - return propertyString.startsWith(EXPRESSION_PREFIX) && propertyString.endsWith(EXPRESSION_SUFFIX)  
123 - && propertyString.contains(PLACEHOLDER_PREFIX);  
124 - }  
125 -  
126 - private String normalizeToPlaceholder(String strVal) {  
127 - int startIndex = strVal.indexOf(PLACEHOLDER_PREFIX);  
128 - if (startIndex == -1) {  
129 - return null;  
130 - }  
131 - int endIndex = strVal.lastIndexOf(PLACEHOLDER_SUFFIX);  
132 - if (endIndex == -1) {  
133 - return null;  
134 - }  
135 -  
136 - return strVal.substring(startIndex, endIndex + PLACEHOLDER_SUFFIX.length());  
137 - }  
138 -  
139 - private int findPlaceholderEndIndex(CharSequence buf, int startIndex) {  
140 - int index = startIndex + PLACEHOLDER_PREFIX.length();  
141 - int withinNestedPlaceholder = 0;  
142 - while (index < buf.length()) {  
143 - if (StringUtils.substringMatch(buf, index, PLACEHOLDER_SUFFIX)) {  
144 - if (withinNestedPlaceholder > 0) {  
145 - withinNestedPlaceholder--;  
146 - index = index + PLACEHOLDER_SUFFIX.length();  
147 - } else {  
148 - return index;  
149 - }  
150 - } else if (StringUtils.substringMatch(buf, index, SIMPLE_PLACEHOLDER_PREFIX)) {  
151 - withinNestedPlaceholder++;  
152 - index = index + SIMPLE_PLACEHOLDER_PREFIX.length();  
153 - } else {  
154 - index++;  
155 - }  
156 - }  
157 - return -1;  
158 - }  
159 -  
160 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/spring/property/PropertySourcesProcessor.java deleted
1 -package cn.iocoder.dashboard.framework.apollox.spring.property;  
2 -  
3 -import cn.iocoder.dashboard.framework.apollox.spring.config.ConfigPropertySource;  
4 -import cn.iocoder.dashboard.framework.apollox.spring.config.ConfigPropertySourceFactory;  
5 -import cn.iocoder.dashboard.framework.apollox.spring.util.SpringInjector;  
6 -import org.springframework.beans.BeansException;  
7 -import org.springframework.beans.factory.config.BeanFactoryPostProcessor;  
8 -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;  
9 -import org.springframework.context.EnvironmentAware;  
10 -import org.springframework.core.Ordered;  
11 -import org.springframework.core.PriorityOrdered;  
12 -import org.springframework.core.env.ConfigurableEnvironment;  
13 -import org.springframework.core.env.Environment;  
14 -  
15 -import java.util.List;  
16 -import java.util.concurrent.atomic.AtomicBoolean;  
17 -  
18 -/**  
19 - * Apollo Property Sources processor for Spring Annotation Based Application. <br /> <br />  
20 - * <p>  
21 - * The reason why PropertySourcesProcessor implements {@link BeanFactoryPostProcessor} instead of  
22 - * {@link org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor} is that lower versions of  
23 - * Spring (e.g. 3.1.1) doesn't support registering BeanDefinitionRegistryPostProcessor in ImportBeanDefinitionRegistrar  
24 - * - {@link com.ctrip.framework.apollo.spring.annotation.ApolloConfigRegistrar}  
25 - *  
26 - * @author Jason Song(song_s@ctrip.com)  
27 - */  
28 -public class PropertySourcesProcessor implements BeanFactoryPostProcessor, EnvironmentAware, PriorityOrdered {  
29 -  
30 - /**  
31 - * 是否初始化的标识  
32 - */  
33 - private static final AtomicBoolean INITIALIZED = new AtomicBoolean(false);  
34 -  
35 - private final ConfigPropertySourceFactory configPropertySourceFactory = SpringInjector.getInstance(ConfigPropertySourceFactory.class);  
36 - /**  
37 - * Spring ConfigurableEnvironment 对象  
38 - */  
39 - private ConfigurableEnvironment environment;  
40 -  
41 -  
42 - @Override  
43 - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {  
44 - if (INITIALIZED.compareAndSet(false, true)) {  
45 - // 初始化 AutoUpdateConfigChangeListener 对象,实现属性的自动更新  
46 - initializeAutoUpdatePropertiesFeature(beanFactory);  
47 - }  
48 - }  
49 -  
50 - private void initializeAutoUpdatePropertiesFeature(ConfigurableListableBeanFactory beanFactory) {  
51 - // 创建 AutoUpdateConfigChangeListener 对象  
52 - AutoUpdateConfigChangeListener autoUpdateConfigChangeListener = new AutoUpdateConfigChangeListener(environment, beanFactory);  
53 - // 循环,向 ConfigPropertySource 注册配置变更器  
54 - List<ConfigPropertySource> configPropertySources = configPropertySourceFactory.getAllConfigPropertySources();  
55 - for (ConfigPropertySource configPropertySource : configPropertySources) {  
56 - configPropertySource.addChangeListener(autoUpdateConfigChangeListener);  
57 - }  
58 - }  
59 -  
60 - @Override  
61 - public void setEnvironment(Environment environment) {  
62 - //it is safe enough to cast as all known environment is derived from ConfigurableEnvironment  
63 - this.environment = (ConfigurableEnvironment) environment;  
64 - }  
65 -  
66 - @Override  
67 - public int getOrder() {  
68 - // make it as early as possible  
69 - return Ordered.HIGHEST_PRECEDENCE; // 最高优先级  
70 - }  
71 -  
72 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/spring/property/SpringValue.java deleted
1 -package cn.iocoder.dashboard.framework.apollox.spring.property;  
2 -  
3 -import org.springframework.core.MethodParameter;  
4 -  
5 -import java.lang.reflect.Field;  
6 -import java.lang.reflect.InvocationTargetException;  
7 -import java.lang.reflect.Method;  
8 -import java.lang.reflect.Type;  
9 -  
10 -/**  
11 - * Spring @Value method info  
12 - *  
13 - * @author github.com/zhegexiaohuozi seimimaster@gmail.com  
14 - * @since 2018/2/6.  
15 - */  
16 -public class SpringValue {  
17 -  
18 - /**  
19 - * Bean 对象  
20 - */  
21 - private Object bean;  
22 - /**  
23 - * Bean 名字  
24 - */  
25 - private String beanName;  
26 - /**  
27 - * Spring 方法参数封装  
28 - */  
29 - private MethodParameter methodParameter;  
30 - /**  
31 - * Field  
32 - */  
33 - private Field field;  
34 - /**  
35 - * KEY  
36 - *  
37 - * 即在 Config 中的属性 KEY 。  
38 - */  
39 - private String key;  
40 - /**  
41 - * 占位符  
42 - */  
43 - private String placeholder;  
44 - /**  
45 - * 值类型  
46 - */  
47 - private Class<?> targetType;  
48 - /**  
49 - * 是否 JSON  
50 - */  
51 - private boolean isJson;  
52 - /**  
53 - * 泛型。当是 JSON 类型时,使用  
54 - */  
55 - private Type genericType;  
56 -  
57 - // Field  
58 - public SpringValue(String key, String placeholder, Object bean, String beanName, Field field, boolean isJson) {  
59 - this.bean = bean;  
60 - this.beanName = beanName;  
61 - // Field  
62 - this.field = field;  
63 - this.key = key;  
64 - this.placeholder = placeholder;  
65 - // Field 差异  
66 - this.targetType = field.getType();  
67 - this.isJson = isJson;  
68 - if (isJson) {  
69 - this.genericType = field.getGenericType();  
70 - }  
71 - }  
72 -  
73 - // Method  
74 - public SpringValue(String key, String placeholder, Object bean, String beanName, Method method, boolean isJson) {  
75 - this.bean = bean;  
76 - this.beanName = beanName;  
77 - // Method  
78 - this.methodParameter = new MethodParameter(method, 0);  
79 - this.key = key;  
80 - this.placeholder = placeholder;  
81 - // Method 差异  
82 - Class<?>[] paramTps = method.getParameterTypes();  
83 - this.targetType = paramTps[0];  
84 - this.isJson = isJson;  
85 - if (isJson) {  
86 - this.genericType = method.getGenericParameterTypes()[0];  
87 - }  
88 - }  
89 -  
90 - public void update(Object newVal) throws IllegalAccessException, InvocationTargetException {  
91 - // Field  
92 - if (isField()) {  
93 - injectField(newVal);  
94 - // Method  
95 - } else {  
96 - injectMethod(newVal);  
97 - }  
98 - }  
99 -  
100 - private void injectField(Object newVal) throws IllegalAccessException {  
101 - boolean accessible = field.isAccessible();  
102 - field.setAccessible(true);  
103 - field.set(bean, newVal);  
104 - field.setAccessible(accessible);  
105 - }  
106 -  
107 - private void injectMethod(Object newVal) throws InvocationTargetException, IllegalAccessException {  
108 - methodParameter.getMethod().invoke(bean, newVal);  
109 - }  
110 -  
111 - public String getBeanName() {  
112 - return beanName;  
113 - }  
114 -  
115 - public Class<?> getTargetType() {  
116 - return targetType;  
117 - }  
118 -  
119 - public String getPlaceholder() {  
120 - return this.placeholder;  
121 - }  
122 -  
123 - public MethodParameter getMethodParameter() {  
124 - return methodParameter;  
125 - }  
126 -  
127 - public boolean isField() {  
128 - return this.field != null;  
129 - }  
130 -  
131 - public Field getField() {  
132 - return field;  
133 - }  
134 -  
135 - public Type getGenericType() {  
136 - return genericType;  
137 - }  
138 -  
139 - public boolean isJson() {  
140 - return isJson;  
141 - }  
142 -  
143 - @Override  
144 - public String toString() {  
145 - if (isField()) {  
146 - return String.format("key: %s, beanName: %s, field: %s.%s", key, beanName, bean.getClass().getName(), field.getName());  
147 - }  
148 - return String.format("key: %s, beanName: %s, method: %s.%s", key, beanName, bean.getClass().getName(),  
149 - methodParameter.getMethod().getName());  
150 - }  
151 -  
152 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/spring/property/SpringValueDefinition.java deleted
1 -package cn.iocoder.dashboard.framework.apollox.spring.property;  
2 -  
3 -import lombok.AllArgsConstructor;  
4 -import lombok.Getter;  
5 -  
6 -/**  
7 - * Spring Value 定义  
8 - */  
9 -@Getter  
10 -@AllArgsConstructor  
11 -public class SpringValueDefinition {  
12 -  
13 - /**  
14 - * KEY  
15 - *  
16 - * 即在 Config 中的属性 KEY 。  
17 - */  
18 - private final String key;  
19 - /**  
20 - * 占位符  
21 - */  
22 - private final String placeholder;  
23 - /**  
24 - * 属性名  
25 - */  
26 - private final String propertyName;  
27 -  
28 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/spring/property/SpringValueDefinitionProcessor.java deleted
1 -package cn.iocoder.dashboard.framework.apollox.spring.property;  
2 -  
3 -import cn.hutool.core.lang.Singleton;  
4 -import com.google.common.collect.LinkedListMultimap;  
5 -import com.google.common.collect.Multimap;  
6 -import org.springframework.beans.BeansException;  
7 -import org.springframework.beans.MutablePropertyValues;  
8 -import org.springframework.beans.PropertyValue;  
9 -import org.springframework.beans.factory.config.BeanDefinition;  
10 -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;  
11 -import org.springframework.beans.factory.config.TypedStringValue;  
12 -import org.springframework.beans.factory.support.BeanDefinitionRegistry;  
13 -import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;  
14 -  
15 -import java.util.List;  
16 -import java.util.Set;  
17 -import java.util.concurrent.atomic.AtomicBoolean;  
18 -  
19 -/**  
20 - * To process xml config placeholders, e.g.  
21 - *  
22 - * <pre>  
23 - * &lt;bean class=&quot;com.ctrip.framework.apollo.demo.spring.xmlConfigDemo.bean.XmlBean&quot;&gt;  
24 - * &lt;property name=&quot;timeout&quot; value=&quot;${timeout:200}&quot;/&gt;  
25 - * &lt;property name=&quot;batch&quot; value=&quot;${batch:100}&quot;/&gt;  
26 - * &lt;/bean&gt;  
27 - * </pre>  
28 - */  
29 -public class SpringValueDefinitionProcessor implements BeanDefinitionRegistryPostProcessor {  
30 -  
31 - /**  
32 - * SpringValueDefinition 集合  
33 - * <p>  
34 - * KEY:beanName  
35 - * VALUE:SpringValueDefinition 集合  
36 - */  
37 - private static final Multimap<String, SpringValueDefinition> beanName2SpringValueDefinitions = LinkedListMultimap.create();  
38 - /**  
39 - * 是否初始化的标识  
40 - */  
41 - private static final AtomicBoolean initialized = new AtomicBoolean(false);  
42 -  
43 - private final PlaceholderHelper placeholderHelper = Singleton.get(PlaceholderHelper.class);  
44 -  
45 - @Override  
46 - public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {  
47 - processPropertyValues(registry);  
48 - }  
49 -  
50 - @Override  
51 - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {  
52 - }  
53 -  
54 - public static Multimap<String, SpringValueDefinition> getBeanName2SpringValueDefinitions() {  
55 - return beanName2SpringValueDefinitions;  
56 - }  
57 -  
58 - private void processPropertyValues(BeanDefinitionRegistry beanRegistry) {  
59 - // 若已经初始化,直接返回  
60 - if (!initialized.compareAndSet(false, true)) {  
61 - // already initialized  
62 - return;  
63 - }  
64 - // 循环 BeanDefinition 集合  
65 - String[] beanNames = beanRegistry.getBeanDefinitionNames();  
66 - for (String beanName : beanNames) {  
67 - BeanDefinition beanDefinition = beanRegistry.getBeanDefinition(beanName);  
68 - // 循环 BeanDefinition 的 PropertyValue 数组  
69 - MutablePropertyValues mutablePropertyValues = beanDefinition.getPropertyValues();  
70 - List<PropertyValue> propertyValues = mutablePropertyValues.getPropertyValueList();  
71 - for (PropertyValue propertyValue : propertyValues) {  
72 - // 获得 `value` 属性。  
73 - Object value = propertyValue.getValue();  
74 - // 忽略非 Spring PlaceHolder 的 `value` 属性。  
75 - if (!(value instanceof TypedStringValue)) {  
76 - continue;  
77 - }  
78 - // 获得 `placeholder` 属性。  
79 - String placeholder = ((TypedStringValue) value).getValue();  
80 - // 提取 `keys` 属性们。  
81 - Set<String> keys = placeholderHelper.extractPlaceholderKeys(placeholder);  
82 - if (keys.isEmpty()) {  
83 - continue;  
84 - }  
85 - // 循环 `keys` ,创建对应的 SpringValueDefinition 对象,并添加到 `beanName2SpringValueDefinitions` 中。  
86 - for (String key : keys) {  
87 - beanName2SpringValueDefinitions.put(beanName,  
88 - new SpringValueDefinition(key, placeholder, propertyValue.getName()));  
89 - }  
90 - }  
91 - }  
92 - }  
93 -  
94 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/spring/property/SpringValueRegistry.java deleted
1 -package cn.iocoder.dashboard.framework.apollox.spring.property;  
2 -  
3 -import com.google.common.collect.LinkedListMultimap;  
4 -import com.google.common.collect.Multimap;  
5 -  
6 -import java.util.Collection;  
7 -  
8 -/**  
9 - * {@link SpringValue} 注册表  
10 - */  
11 -public class SpringValueRegistry {  
12 -  
13 - /**  
14 - * SpringValue 集合  
15 - *  
16 - * KEY:属性 KEY ,即 Config 配置 KEY  
17 - * VALUE:SpringValue 数组  
18 - */  
19 - private final Multimap<String, SpringValue> registry = LinkedListMultimap.create();  
20 -  
21 - // 注册  
22 - public void register(String key, SpringValue springValue) {  
23 - registry.put(key, springValue);  
24 - }  
25 -  
26 - // 获得  
27 - public Collection<SpringValue> get(String key) {  
28 - return registry.get(key);  
29 - }  
30 -  
31 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/spring/util/BeanRegistrationUtil.java deleted
1 -package cn.iocoder.dashboard.framework.apollox.spring.util;  
2 -  
3 -import org.springframework.beans.factory.config.BeanDefinition;  
4 -import org.springframework.beans.factory.support.BeanDefinitionBuilder;  
5 -import org.springframework.beans.factory.support.BeanDefinitionRegistry;  
6 -  
7 -import java.util.Objects;  
8 -  
9 -/**  
10 - * Bean Registration 工具类  
11 - *  
12 - * @author Jason Song(song_s@ctrip.com)  
13 - */  
14 -public class BeanRegistrationUtil {  
15 -  
16 - // 注册 `beanClass` 到 BeanDefinitionRegistry 中,当且仅当 `beanName` 和 `beanClass` 都不存在对应的 BeanDefinition 时  
17 - public static boolean registerBeanDefinitionIfNotExists(BeanDefinitionRegistry registry, String beanName, Class<?> beanClass) {  
18 - // 不存在 `beanName` 对应的 BeanDefinition  
19 - if (registry.containsBeanDefinition(beanName)) {  
20 - return false;  
21 - }  
22 -  
23 - // 不存在 `beanClass` 对应的 BeanDefinition  
24 - String[] candidates = registry.getBeanDefinitionNames();  
25 - for (String candidate : candidates) {  
26 - BeanDefinition beanDefinition = registry.getBeanDefinition(candidate);  
27 - if (Objects.equals(beanDefinition.getBeanClassName(), beanClass.getName())) {  
28 - return false;  
29 - }  
30 - }  
31 -  
32 - // 注册 `beanClass` 到 BeanDefinitionRegistry 中  
33 - BeanDefinition annotationProcessor = BeanDefinitionBuilder.genericBeanDefinition(beanClass).getBeanDefinition();  
34 - registry.registerBeanDefinition(beanName, annotationProcessor);  
35 - return true;  
36 - }  
37 -  
38 -}  
src/main/java/cn/iocoder/dashboard/framework/apollox/spring/util/SpringInjector.java deleted
1 -package cn.iocoder.dashboard.framework.apollox.spring.util;  
2 -  
3 -import cn.hutool.core.lang.Singleton;  
4 -  
5 -/**  
6 - * Spring 注入器  
7 - */  
8 -public class SpringInjector {  
9 -  
10 - public static <T> T getInstance(Class<T> clazz) {  
11 - return Singleton.get(clazz);  
12 - }  
13 -  
14 -}  
src/main/resources/META-INF/spring.factories deleted
1 -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\  
2 - cn.iocoder.dashboard.framework.apollox.spring.boot.ApolloAutoConfiguration  
3 -org.springframework.context.ApplicationContextInitializer=\  
4 - cn.iocoder.dashboard.framework.apollox.spring.boot.ApolloApplicationContextInitializer  
src/main/resources/application.yaml
@@ -50,10 +50,9 @@ yudao: @@ -50,10 +50,9 @@ yudao:
50 # Apollo 配置中心 50 # Apollo 配置中心
51 apollo: 51 apollo:
52 bootstrap: 52 bootstrap:
53 - enabled: true 53 + enabled: true # 设置 Apollo 在启动阶段生效
54 eagerLoad: 54 eagerLoad:
55 - enabled: true  
56 - autoUpdateInjectedSpringProperties: true 55 + enabled: true # 设置 Apollo 在日志初始化前生效,可以实现日志的动态级别配置
57 56
58 # MyBatis Plus 的配置项 57 # MyBatis Plus 的配置项
59 mybatis-plus: 58 mybatis-plus: