Commit 17cb37f577f6e4f653335bd7a23a33f4a701dc2b
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
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
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 | - * @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
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 | - * <bean class="com.ctrip.framework.apollo.demo.spring.xmlConfigDemo.bean.XmlBean"> | |
| 24 | - * <property name="timeout" value="${timeout:200}"/> | |
| 25 | - * <property name="batch" value="${batch:100}"/> | |
| 26 | - * </bean> | |
| 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
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: | ... | ... |