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 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 6 import com.ctrip.framework.apollo.Apollo;
4 7 import com.ctrip.framework.apollo.build.ApolloInjector;
5 8 import com.ctrip.framework.apollo.core.utils.ApolloThreadFactory;
... ... @@ -8,13 +11,20 @@ import com.ctrip.framework.apollo.internals.AbstractConfigRepository;
8 11 import com.ctrip.framework.apollo.internals.ConfigRepository;
9 12 import com.ctrip.framework.apollo.tracer.Tracer;
10 13 import com.ctrip.framework.apollo.util.ConfigUtil;
  14 +import com.ctrip.framework.apollo.util.factory.PropertiesFactory;
11 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 25 import java.util.Properties;
14 26 import java.util.concurrent.Executors;
15 27 import java.util.concurrent.ScheduledExecutorService;
16   -import java.util.concurrent.atomic.AtomicInteger;
17   -import java.util.concurrent.atomic.AtomicReference;
18 28  
19 29 @Slf4j
20 30 public class DBConfigRepository extends AbstractConfigRepository {
... ... @@ -27,15 +37,35 @@ public class DBConfigRepository extends AbstractConfigRepository {
27 37 }
28 38  
29 39 private final ConfigUtil m_configUtil;
30   -
31   - private final AtomicReference<Properties> m_configCache;
  40 + private PropertiesFactory propertiesFactory;
32 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 57 public DBConfigRepository(String namespace) {
35 58 // 初始化变量
36 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 71 this.trySync();
... ... @@ -43,27 +73,32 @@ public class DBConfigRepository extends AbstractConfigRepository {
43 73 this.schedulePeriodicRefresh();
44 74 }
45 75  
46   - private AtomicInteger index = new AtomicInteger();
47   -
48 76 @Override
49 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 94 @Override
60 95 public Properties getConfig() {
61 96 // 兜底,避免可能存在配置为 null 的情况
62   - if (m_configCache.get() == null) {
  97 + if (m_configCache == null) {
63 98 this.trySync();
64 99 }
65 100 // 返回配置
66   - return m_configCache.get();
  101 + return m_configCache;
67 102 }
68 103  
69 104 @Override
... ... @@ -76,6 +111,15 @@ public class DBConfigRepository extends AbstractConfigRepository {
76 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 123 private void schedulePeriodicRefresh() {
80 124 log.debug("Schedule periodic refresh with interval: {} {}",
81 125 m_configUtil.getRefreshInterval(), m_configUtil.getRefreshIntervalTimeUnit());
... ... @@ -83,7 +127,7 @@ public class DBConfigRepository extends AbstractConfigRepository {
83 127 Tracer.logEvent("Apollo.ConfigService", String.format("periodicRefresh: %s", m_namespace));
84 128 log.debug("refresh config for namespace: {}", m_namespace);
85 129  
86   - // 执行同步
  130 + // 执行同步. 内部已经 try catch 掉异常,无需在处理
87 131 trySync();
88 132  
89 133 Tracer.logEvent("Apollo.Client.Version", Apollo.VERSION);
... ... @@ -92,4 +136,35 @@ public class DBConfigRepository extends AbstractConfigRepository {
92 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 50 # Apollo 配置中心
51 51 apollo:
52 52 bootstrap:
53   - enabled: true
  53 + enabled: true # 设置 Apollo 在启动阶段生效
54 54 eagerLoad:
55   - enabled: true
56   - autoUpdateInjectedSpringProperties: true
  55 + enabled: true # 设置 Apollo 在日志初始化前生效,可以实现日志的动态级别配置
57 56  
58 57 # MyBatis Plus 的配置项
59 58 mybatis-plus:
... ...