diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollo/internals/DBConfigRepository.java b/src/main/java/cn/iocoder/dashboard/framework/apollo/internals/DBConfigRepository.java index 38743080a..eeb583398 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/apollo/internals/DBConfigRepository.java +++ b/src/main/java/cn/iocoder/dashboard/framework/apollo/internals/DBConfigRepository.java @@ -1,5 +1,8 @@ package cn.iocoder.dashboard.framework.apollo.internals; +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.config.SysConfigDO; import com.ctrip.framework.apollo.Apollo; import com.ctrip.framework.apollo.build.ApolloInjector; import com.ctrip.framework.apollo.core.utils.ApolloThreadFactory; @@ -8,13 +11,20 @@ import com.ctrip.framework.apollo.internals.AbstractConfigRepository; import com.ctrip.framework.apollo.internals.ConfigRepository; import com.ctrip.framework.apollo.tracer.Tracer; import com.ctrip.framework.apollo.util.ConfigUtil; +import com.ctrip.framework.apollo.util.factory.PropertiesFactory; import lombok.extern.slf4j.Slf4j; +import org.springframework.jdbc.core.BeanPropertyRowMapper; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import javax.sql.DataSource; +import java.sql.ResultSet; +import java.util.Comparator; +import java.util.Date; +import java.util.List; import java.util.Properties; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; @Slf4j public class DBConfigRepository extends AbstractConfigRepository { @@ -27,15 +37,35 @@ public class DBConfigRepository extends AbstractConfigRepository { } private final ConfigUtil m_configUtil; - - private final AtomicReference m_configCache; + private PropertiesFactory propertiesFactory; private final String m_namespace; + /** + * 配置缓存,使用 Properties 存储 + */ + private volatile Properties m_configCache; + /** + * 缓存配置的最大更新时间,用于后续的增量轮询,判断是否有更新 + */ + private volatile Date maxUpdateTime; + + /** + * Spring JDBC 操作模板 + */ + private final JdbcTemplate jdbcTemplate; + public DBConfigRepository(String namespace) { // 初始化变量 this.m_namespace = namespace; - m_configCache = new AtomicReference<>(); - m_configUtil = ApolloInjector.getInstance(ConfigUtil.class); + this.propertiesFactory = ApolloInjector.getInstance(PropertiesFactory.class); + this.m_configUtil = ApolloInjector.getInstance(ConfigUtil.class); + // TODO 优化到配置 + String url = "jdbc:mysql://127.0.1:33061/ruoyi-vue-pro?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT"; + String username = "root"; + String password = "123456"; +// DataSource dataSource = DataSourceBuilder.create().url(url).username(username).password(password).build(); + DataSource dataSource = new DriverManagerDataSource(url, username, password); + this.jdbcTemplate = new JdbcTemplate(dataSource); // 初始化加载 this.trySync(); @@ -43,27 +73,32 @@ public class DBConfigRepository extends AbstractConfigRepository { this.schedulePeriodicRefresh(); } - private AtomicInteger index = new AtomicInteger(); - @Override protected void sync() { - System.out.println("我同步啦"); + // 第一步,尝试获取配置 + List configs = this.loadConfigIfUpdate(this.maxUpdateTime); + if (CollUtil.isEmpty(configs)) { // 如果没有更新,则返回 + return; + } + log.info("[sync][同步到新配置,配置数量为:{}]", configs.size()); - index.incrementAndGet(); - Properties properties = new Properties(); - properties.setProperty("demo.test", String.valueOf(index.get())); - m_configCache.set(properties); - super.fireRepositoryChange(m_namespace, properties); + // 第二步,构建新的 Properties + Properties newProperties = this.buildProperties(configs); + this.m_configCache = newProperties; + // 第三步,获取最大的配置时间 + this.maxUpdateTime = configs.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime(); + // 第四部,触发配置刷新!重要!!!! + super.fireRepositoryChange(m_namespace, newProperties); } @Override public Properties getConfig() { // 兜底,避免可能存在配置为 null 的情况 - if (m_configCache.get() == null) { + if (m_configCache == null) { this.trySync(); } // 返回配置 - return m_configCache.get(); + return m_configCache; } @Override @@ -76,6 +111,15 @@ public class DBConfigRepository extends AbstractConfigRepository { return ConfigSourceType.REMOTE; } + private Properties buildProperties(List configs) { + Properties properties = propertiesFactory.getPropertiesInstance(); + configs.stream().filter(config -> 0 == config.getDeleted()) // 过滤掉被删除的配置 + .forEach(config -> properties.put(config.getKey(), config.getValue())); + return properties; + } + + // ========== 定时器相关操作 ========== + private void schedulePeriodicRefresh() { log.debug("Schedule periodic refresh with interval: {} {}", m_configUtil.getRefreshInterval(), m_configUtil.getRefreshIntervalTimeUnit()); @@ -83,7 +127,7 @@ public class DBConfigRepository extends AbstractConfigRepository { Tracer.logEvent("Apollo.ConfigService", String.format("periodicRefresh: %s", m_namespace)); log.debug("refresh config for namespace: {}", m_namespace); - // 执行同步 + // 执行同步. 内部已经 try catch 掉异常,无需在处理 trySync(); Tracer.logEvent("Apollo.Client.Version", Apollo.VERSION); @@ -92,4 +136,35 @@ public class DBConfigRepository extends AbstractConfigRepository { // TimeUnit.SECONDS); } + // ========== 数据库相关操作 ========== + + /** + * 如果配置发生变化,从数据库中获取最新的全量配置。 + * 如果未发生变化,则返回空 + * + * @param maxUpdateTime 当前配置的最大更新时间 + * @return 配置列表 + */ + private List loadConfigIfUpdate(Date maxUpdateTime) { + // 第一步,判断是否要更新。 + boolean isUpdate = maxUpdateTime == null; // 如果更新时间为空,说明 DB 一定有新数据 + if (!isUpdate) { + isUpdate = this.existsNewConfig(maxUpdateTime); // 判断数据库中是否有更新的配置 + } + if (!isUpdate) { + return null; + } + // 第二步,如果有更新,则从数据库加载所有配置 + return this.getSysConfigList(); + } + + private boolean existsNewConfig(Date maxUpdateTime) { + return jdbcTemplate.query("SELECT id FROM sys_config WHERE update_time > ? LIMIT 1", + ResultSet::next, maxUpdateTime); + } + + private List getSysConfigList() { + return jdbcTemplate.query("SELECT `key`, `value`, update_time, deleted FROM sys_config", new BeanPropertyRowMapper<>(SysConfigDO.class)); + } + } diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/Config.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/Config.java deleted file mode 100644 index 188d34978..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/Config.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox; - -import java.util.Set; - -/** - * 配置接口 - * - * @author Jason Song(song_s@ctrip.com) - */ -public interface Config { - - /** - * Return the property value with the given key, or {@code defaultValue} if the key doesn't exist. - * - * @param key the property name - * @param defaultValue the default value when key is not found or any error occurred - * @return the property value - */ - String getProperty(String key, String defaultValue); - - /** - * Return a set of the property names - * - * @return the property names - */ - Set getPropertyNames(); - - /** - * Add change listener to this config instance. - * - * @param listener the config change listener - */ - void addChangeListener(ConfigChangeListener listener); - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/ConfigChangeListener.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/ConfigChangeListener.java deleted file mode 100644 index 7b782f3ef..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/ConfigChangeListener.java +++ /dev/null @@ -1,19 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox; - -import cn.iocoder.dashboard.framework.apollox.model.ConfigChangeEvent; - -/** - * {@link Config} 变化监听器 - * - * @author Jason Song(song_s@ctrip.com) - */ -public interface ConfigChangeListener { - - /** - * Invoked when there is any config change for the namespace. - * - * @param changeEvent the event for this change - */ - void onChange(ConfigChangeEvent changeEvent); - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/ConfigService.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/ConfigService.java deleted file mode 100644 index 780b96101..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/ConfigService.java +++ /dev/null @@ -1,11 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox; - -import cn.hutool.core.lang.Singleton; - -public class ConfigService { - - public static Config getConfig(String namespace) { - return Singleton.get(DefaultConfig.class); - } - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/DefaultConfig.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/DefaultConfig.java deleted file mode 100644 index 16fef1a15..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/DefaultConfig.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox; - -import java.util.Collections; -import java.util.Set; - -public class DefaultConfig implements Config { - - @Override - public String getProperty(String key, String defaultValue) { - return null; - } - - @Override - public Set getPropertyNames() { - return Collections.emptySet(); // TODO 等下实现 - } - - @Override - public void addChangeListener(ConfigChangeListener listener) { - - } - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/enums/PropertyChangeType.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/enums/PropertyChangeType.java deleted file mode 100644 index 10681b894..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/enums/PropertyChangeType.java +++ /dev/null @@ -1,14 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox.enums; - -/** - * 属性变化类型枚举 - * - * @author Jason Song(song_s@ctrip.com) - */ -public enum PropertyChangeType { - - ADDED, // 添加 - MODIFIED, // 修改 - DELETED // 删除 - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/internals/AbstractConfigRepository.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/internals/AbstractConfigRepository.java deleted file mode 100644 index decfc7968..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/internals/AbstractConfigRepository.java +++ /dev/null @@ -1,79 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox.internals; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; -import java.util.Properties; -import java.util.concurrent.CopyOnWriteArrayList; - -/** - * 配置 Repository 抽象类 - * - * @author Jason Song(song_s@ctrip.com) - */ -public abstract class AbstractConfigRepository implements ConfigRepository { - - private static final Logger logger = LoggerFactory.getLogger(AbstractConfigRepository.class); - - /** - * RepositoryChangeListener 数组 - */ - private List m_listeners = new CopyOnWriteArrayList<>(); - - /** - * 尝试同步 - * - * @return 是否同步成功 - */ - protected boolean trySync() { - try { - // 同步 - sync(); - // 返回同步成功 - return true; - } catch (Throwable ex) { -// Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(ex)); - logger.warn("Sync config failed, will retry. Repository {}", getClass(), ex); - } - // 返回同步失败 - return false; - } - - /** - * 同步配置 - */ - protected abstract void sync(); - - @Override - public void addChangeListener(RepositoryChangeListener listener) { - if (!m_listeners.contains(listener)) { - m_listeners.add(listener); - } - } - - @Override - public void removeChangeListener(RepositoryChangeListener listener) { - m_listeners.remove(listener); - } - - /** - * 触发监听器们 - * - * @param namespace Namespace 名字 - * @param newProperties 配置 - */ - protected void fireRepositoryChange(String namespace, Properties newProperties) { - // 循环 RepositoryChangeListener 数组 - for (RepositoryChangeListener listener : m_listeners) { - try { - // 触发监听器 - listener.onRepositoryChange(namespace, newProperties); - } catch (Throwable ex) { -// Tracer.logError(ex); - logger.error("Failed to invoke repository change listener {}", listener.getClass(), ex); - } - } - } - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/internals/ConfigRepository.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/internals/ConfigRepository.java deleted file mode 100644 index 71e6357e9..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/internals/ConfigRepository.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox.internals; - -import java.util.Properties; - -/** - * 配置 Repository 接口 - * - * @author Jason Song(song_s@ctrip.com) - */ -public interface ConfigRepository { - - /** - * Get the config from this repository. - *

- * 获得配置,以 Properties 对象返回 - * - * @return config - */ - Properties getConfig(); - - /** - * Add change listener. - * - * @param listener the listener to observe the changes - */ - void addChangeListener(RepositoryChangeListener listener); - - /** - * Remove change listener. - * - * @param listener the listener to remove - */ - void removeChangeListener(RepositoryChangeListener listener); - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/internals/RemoteConfigRepository.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/internals/RemoteConfigRepository.java deleted file mode 100644 index 0a95b96ce..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/internals/RemoteConfigRepository.java +++ /dev/null @@ -1,345 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox.internals; - -import com.google.common.base.Joiner; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * RemoteConfig Repository - *

- * 远程配置 Repository ,实现从 Config Service 拉取配置,并缓存在内存中。并且,定时 + 实时刷新缓存。 - * - * @author Jason Song(song_s@ctrip.com) - */ -public class RemoteConfigRepository extends AbstractConfigRepository { - - private static final Logger logger = LoggerFactory.getLogger(RemoteConfigRepository.class); - private static final Joiner STRING_JOINER = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR); - private static final Joiner.MapJoiner MAP_JOINER = Joiner.on("&").withKeyValueSeparator("="); - - private static final Escaper pathEscaper = UrlEscapers.urlPathSegmentEscaper(); - private static final Escaper queryParamEscaper = UrlEscapers.urlFormParameterEscaper(); - - /** - * 远程配置长轮询服务 - */ - private RemoteConfigLongPollService remoteConfigLongPollService; - /** - * 指向 ApolloConfig 的 AtomicReference ,缓存配置 - */ - private volatile AtomicReference m_configCache; - /** - * Namespace 名字 - */ - private final String m_namespace; - /** - * ScheduledExecutorService 对象 - */ - private final static ScheduledExecutorService m_executorService; - /** - * 指向 ServiceDTO( Config Service 信息) 的 AtomicReference - */ - private AtomicReference m_longPollServiceDto; - /** - * 指向 ApolloNotificationMessages 的 AtomicReference - */ - private AtomicReference m_remoteMessages; - /** - * 加载配置的 RateLimiter - */ - private RateLimiter m_loadConfigRateLimiter; - /** - * 是否强制拉取缓存的标记 - *

- * 若为 true ,则多一轮从 Config Service 拉取配置 - * 为 true 的原因,RemoteConfigRepository 知道 Config Service 有配置刷新 - */ - private AtomicBoolean m_configNeedForceRefresh; - /** - * 失败定时重试策略,使用 {@link ExponentialSchedulePolicy} - */ - private SchedulePolicy m_loadConfigFailSchedulePolicy; - private Gson gson; - private ConfigUtil m_configUtil; - private HttpUtil m_httpUtil; - private ConfigServiceLocator m_serviceLocator; - - static { - // 单线程池 - m_executorService = Executors.newScheduledThreadPool(1, ApolloThreadFactory.create("RemoteConfigRepository", true)); - } - - /** - * Constructor. - * - * @param namespace the namespace - */ - public RemoteConfigRepository(String namespace) { - m_namespace = namespace; - m_configCache = new AtomicReference<>(); - m_configUtil = ApolloInjector.getInstance(ConfigUtil.class); - m_httpUtil = ApolloInjector.getInstance(HttpUtil.class); - m_serviceLocator = ApolloInjector.getInstance(ConfigServiceLocator.class); - remoteConfigLongPollService = ApolloInjector.getInstance(RemoteConfigLongPollService.class); - m_longPollServiceDto = new AtomicReference<>(); - m_remoteMessages = new AtomicReference<>(); - m_loadConfigRateLimiter = RateLimiter.create(m_configUtil.getLoadConfigQPS()); - m_configNeedForceRefresh = new AtomicBoolean(true); - m_loadConfigFailSchedulePolicy = new ExponentialSchedulePolicy(m_configUtil.getOnErrorRetryInterval(), m_configUtil.getOnErrorRetryInterval() * 8); - gson = new Gson(); - // 尝试同步配置 - super.trySync(); - // 初始化定时刷新配置的任务 - this.schedulePeriodicRefresh(); - // 注册自己到 RemoteConfigLongPollService 中,实现配置更新的实时通知 - this.scheduleLongPollingRefresh(); - } - - @Override - public Properties getConfig() { - // 如果缓存为空,强制从 Config Service 拉取配置 - if (m_configCache.get() == null) { - this.sync(); - } - // 转换成 Properties 对象,并返回 - return transformApolloConfigToProperties(m_configCache.get()); - } - - @Override - public void setUpstreamRepository(ConfigRepository upstreamConfigRepository) { - // remote config doesn't need upstream - } - - private void schedulePeriodicRefresh() { - logger.debug("Schedule periodic refresh with interval: {} {}", m_configUtil.getRefreshInterval(), m_configUtil.getRefreshIntervalTimeUnit()); - // 创建定时任务,定时刷新配置 - m_executorService.scheduleAtFixedRate(new Runnable() { - @Override - public void run() { - // 【TODO 6001】Tracer 日志 - Tracer.logEvent("Apollo.ConfigService", String.format("periodicRefresh: %s", m_namespace)); - logger.debug("refresh config for namespace: {}", m_namespace); - // 尝试同步配置 - trySync(); - // 【TODO 6001】Tracer 日志 - Tracer.logEvent("Apollo.Client.Version", Apollo.VERSION); - } - }, m_configUtil.getRefreshInterval(), m_configUtil.getRefreshInterval(), m_configUtil.getRefreshIntervalTimeUnit()); - } - - @Override - protected synchronized void sync() { - // 【TODO 6001】Tracer 日志 - Transaction transaction = Tracer.newTransaction("Apollo.ConfigService", "syncRemoteConfig"); - try { - // 获得缓存的 ApolloConfig 对象 - ApolloConfig previous = m_configCache.get(); - // 从 Config Service 加载 ApolloConfig 对象 - ApolloConfig current = loadApolloConfig(); - - // reference equals means HTTP 304 - // 若不相等,说明更新了,设置到缓存中 - if (previous != current) { - logger.debug("Remote Config refreshed!"); - // 设置到缓存 - m_configCache.set(current); - // 发布 Repository 的配置发生变化,触发对应的监听器们 - super.fireRepositoryChange(m_namespace, this.getConfig()); - } - // 【TODO 6001】Tracer 日志 - if (current != null) { - Tracer.logEvent(String.format("Apollo.Client.Configs.%s", current.getNamespaceName()), current.getReleaseKey()); - } - // 【TODO 6001】Tracer 日志 - transaction.setStatus(Transaction.SUCCESS); - } catch (Throwable ex) { - // 【TODO 6001】Tracer 日志 - transaction.setStatus(ex); - throw ex; - } finally { - // 【TODO 6001】Tracer 日志 - transaction.complete(); - } - } - - private Properties transformApolloConfigToProperties(ApolloConfig apolloConfig) { - Properties result = new Properties(); - result.putAll(apolloConfig.getConfigurations()); - return result; - } - - private ApolloConfig loadApolloConfig() { - // 限流 - if (!m_loadConfigRateLimiter.tryAcquire(5, TimeUnit.SECONDS)) { - // wait at most 5 seconds - try { - TimeUnit.SECONDS.sleep(5); - } catch (InterruptedException e) { - } - } - // 获得 appId cluster dataCenter 配置信息 - String appId = m_configUtil.getAppId(); - String cluster = m_configUtil.getCluster(); - String dataCenter = m_configUtil.getDataCenter(); - Tracer.logEvent("Apollo.Client.ConfigMeta", STRING_JOINER.join(appId, cluster, m_namespace)); - // 计算重试次数 - int maxRetries = m_configNeedForceRefresh.get() ? 2 : 1; - long onErrorSleepTime = 0; // 0 means no sleep - Throwable exception = null; - // 获得所有的 Config Service 的地址 - List configServices = getConfigServices(); - String url = null; - // 循环读取配置重试次数直到成功。每一次,都会循环所有的 ServiceDTO 数组。 - for (int i = 0; i < maxRetries; i++) { - // 随机所有的 Config Service 的地址 - List randomConfigServices = Lists.newLinkedList(configServices); - Collections.shuffle(randomConfigServices); - // 优先访问通知配置变更的 Config Service 的地址。并且,获取到时,需要置空,避免重复优先访问。 - // Access the server which notifies the client first - if (m_longPollServiceDto.get() != null) { - randomConfigServices.add(0, m_longPollServiceDto.getAndSet(null)); - } - // 循环所有的 Config Service 的地址 - for (ServiceDTO configService : randomConfigServices) { - // sleep 等待,下次从 Config Service 拉取配置 - if (onErrorSleepTime > 0) { - logger.warn("Load config failed, will retry in {} {}. appId: {}, cluster: {}, namespaces: {}", onErrorSleepTime, m_configUtil.getOnErrorRetryIntervalTimeUnit(), appId, cluster, m_namespace); - try { - m_configUtil.getOnErrorRetryIntervalTimeUnit().sleep(onErrorSleepTime); - } catch (InterruptedException e) { - //ignore - } - } - // 组装查询配置的地址 - url = assembleQueryConfigUrl(configService.getHomepageUrl(), appId, cluster, m_namespace, dataCenter, m_remoteMessages.get(), m_configCache.get()); - - logger.debug("Loading config from {}", url); - // 创建 HttpRequest 对象 - HttpRequest request = new HttpRequest(url); - - // 【TODO 6001】Tracer 日志 - Transaction transaction = Tracer.newTransaction("Apollo.ConfigService", "queryConfig"); - transaction.addData("Url", url); - try { - // 发起请求,返回 HttpResponse 对象 - HttpResponse response = m_httpUtil.doGet(request, ApolloConfig.class); - // 设置 m_configNeedForceRefresh = false - m_configNeedForceRefresh.set(false); - // 标记成功 - m_loadConfigFailSchedulePolicy.success(); - - // 【TODO 6001】Tracer 日志 - transaction.addData("StatusCode", response.getStatusCode()); - transaction.setStatus(Transaction.SUCCESS); - - // 无新的配置,直接返回缓存的 ApolloConfig 对象 - if (response.getStatusCode() == 304) { - logger.debug("Config server responds with 304 HTTP status code."); - return m_configCache.get(); - } - - // 有新的配置,进行返回新的 ApolloConfig 对象 - ApolloConfig result = response.getBody(); - logger.debug("Loaded config for {}: {}", m_namespace, result); - return result; - } catch (ApolloConfigStatusCodeException ex) { - ApolloConfigStatusCodeException statusCodeException = ex; - // 若返回的状态码是 404 ,说明查询配置的 Config Service 不存在该 Namespace 。 - // config not found - if (ex.getStatusCode() == 404) { - String message = String.format("Could not find config for namespace - appId: %s, cluster: %s, namespace: %s, " + - "please check whether the configs are released in Apollo!", appId, cluster, m_namespace); - statusCodeException = new ApolloConfigStatusCodeException(ex.getStatusCode(), message); - } - // 【TODO 6001】Tracer 日志 - Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(statusCodeException)); - transaction.setStatus(statusCodeException); - // 设置最终的异常 - exception = statusCodeException; - } catch (Throwable ex) { - // 【TODO 6001】Tracer 日志 - Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(ex)); - transaction.setStatus(ex); - // 设置最终的异常 - exception = ex; - } finally { - // 【TODO 6001】Tracer 日志 - transaction.complete(); - } - // 计算延迟时间 - // if force refresh, do normal sleep, if normal config load, do exponential sleep - onErrorSleepTime = m_configNeedForceRefresh.get() ? m_configUtil.getOnErrorRetryInterval() : m_loadConfigFailSchedulePolicy.fail(); - } - - } - // 若查询配置失败,抛出 ApolloConfigException 异常 - String message = String.format("Load Apollo Config failed - appId: %s, cluster: %s, namespace: %s, url: %s", appId, cluster, m_namespace, url); - throw new ApolloConfigException(message, exception); - } - - // 组装查询配置的地址 - String assembleQueryConfigUrl(String uri, String appId, String cluster, String namespace, - String dataCenter, ApolloNotificationMessages remoteMessages, ApolloConfig previousConfig) { - String path = "configs/%s/%s/%s"; // /configs/{appId}/{clusterName}/{namespace:.+} - List pathParams = Lists.newArrayList(pathEscaper.escape(appId), pathEscaper.escape(cluster), pathEscaper.escape(namespace)); - Map queryParams = Maps.newHashMap(); - // releaseKey - if (previousConfig != null) { - queryParams.put("releaseKey", queryParamEscaper.escape(previousConfig.getReleaseKey())); - } - // dataCenter - if (!Strings.isNullOrEmpty(dataCenter)) { - queryParams.put("dataCenter", queryParamEscaper.escape(dataCenter)); - } - // ip - String localIp = m_configUtil.getLocalIp(); - if (!Strings.isNullOrEmpty(localIp)) { - queryParams.put("ip", queryParamEscaper.escape(localIp)); - } - // messages - if (remoteMessages != null) { - queryParams.put("messages", queryParamEscaper.escape(gson.toJson(remoteMessages))); - } - // 格式化 URL - String pathExpanded = String.format(path, pathParams.toArray()); - // 拼接 Query String - if (!queryParams.isEmpty()) { - pathExpanded += "?" + MAP_JOINER.join(queryParams); - } - // 拼接最终的请求 URL - if (!uri.endsWith("/")) { - uri += "/"; - } - return uri + pathExpanded; - } - - /** - * 注册自己到 RemoteConfigLongPollService 中,实现配置更新的实时通知 - */ - private void scheduleLongPollingRefresh() { - remoteConfigLongPollService.submit(m_namespace, this); - } - - /** - * 当长轮询到配置更新时,发起同步配置的任务 - * - * @param longPollNotifiedServiceDto ServiceDTO 对象 - * @param remoteMessages ApolloNotificationMessages 对象 - */ - public void onLongPollNotified(ServiceDTO longPollNotifiedServiceDto, ApolloNotificationMessages remoteMessages) { - // 提交同步任务 - m_executorService.submit(new Runnable() { - - @Override - public void run() { - // 设置 m_configNeedForceRefresh 为 true - m_configNeedForceRefresh.set(true); - // 尝试同步配置 - trySync(); - } - - }); - } - - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/internals/RepositoryChangeListener.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/internals/RepositoryChangeListener.java deleted file mode 100644 index 338fae778..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/internals/RepositoryChangeListener.java +++ /dev/null @@ -1,18 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox.internals; - -import java.util.Properties; - -/** - * @author Jason Song(song_s@ctrip.com) - */ -public interface RepositoryChangeListener { - - /** - * Invoked when config repository changes. - * - * @param namespace the namespace of this repository change - * @param newProperties the properties after change - */ - void onRepositoryChange(String namespace, Properties newProperties); - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/model/ConfigChange.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/model/ConfigChange.java deleted file mode 100644 index ac52f10be..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/model/ConfigChange.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox.model; - - -import cn.iocoder.dashboard.framework.apollox.enums.PropertyChangeType; -import lombok.AllArgsConstructor; -import lombok.Data; - -/** - * Holds the information for a config change. - * 配置每个属性变化的信息 - * - * @author Jason Song(song_s@ctrip.com) - */ -@Data -@AllArgsConstructor -public class ConfigChange { - - /** - * 属性名 - */ - private final String propertyName; - /** - * 老值 - */ - private String oldValue; - /** - * 新值 - */ - private String newValue; - /** - * 变化类型 - */ - private PropertyChangeType changeType; - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/model/ConfigChangeEvent.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/model/ConfigChangeEvent.java deleted file mode 100644 index 50aaab566..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/model/ConfigChangeEvent.java +++ /dev/null @@ -1,53 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox.model; - -import lombok.AllArgsConstructor; - -import java.util.Map; -import java.util.Set; - -/** - * A change event when a namespace's config is changed. - * - * @author Jason Song(song_s@ctrip.com) - */ -@AllArgsConstructor -public class ConfigChangeEvent { - - /** - * 变化属性的集合 - * - * KEY:属性名 - * VALUE:配置变化 - */ - private final Map m_changes; - - /** - * Get the keys changed. - * - * @return the list of the keys - */ - public Set changedKeys() { - return m_changes.keySet(); - } - - /** - * Get a specific change instance for the key specified. - * - * @param key the changed key - * @return the change instance - */ - public ConfigChange getChange(String key) { - return m_changes.get(key); - } - - /** - * Check whether the specified key is changed - * - * @param key the key - * @return true if the key is changed, false otherwise. - */ - public boolean isChanged(String key) { - return m_changes.containsKey(key); - } - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/package-info.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/package-info.java deleted file mode 100644 index 7e4350f55..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/package-info.java +++ /dev/null @@ -1,16 +0,0 @@ -/** - * 配置中心客户端,基于 Apollo Client 实现,所以叫 ApolloX - * - * 差别在于,我们使用 {@link cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.config.SysConfigDO} 表作为配置源。 - * 当然,功能肯定也会相对少些,满足最小化诉求。 - * - * 1. 项目初始化时,可以使用 SysConfigDO 表的配置 - * 2. 使用 Spring @Value 可以注入属性 - * 3. SysConfigDO 表的配置修改时,注入到 @Value 的属性可以刷新 - * - * 另外,整个包结构会参考 Apollo 为主,方便维护与理解 - * - * 注意,目前有两个特性是不支持的 - * 1. 自定义配置变化的监听器 - */ -package cn.iocoder.dashboard.framework.apollox; diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/annotation/ApolloAnnotationProcessor.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/annotation/ApolloAnnotationProcessor.java deleted file mode 100644 index b19305dfa..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/annotation/ApolloAnnotationProcessor.java +++ /dev/null @@ -1,61 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox.spring.annotation; - -import cn.hutool.core.lang.Singleton; -import cn.iocoder.dashboard.framework.apollox.Config; -import cn.iocoder.dashboard.framework.apollox.ConfigChangeListener; -import cn.iocoder.dashboard.framework.apollox.DefaultConfig; -import cn.iocoder.dashboard.framework.apollox.model.ConfigChangeEvent; -import com.google.common.base.Preconditions; -import org.springframework.core.annotation.AnnotationUtils; -import org.springframework.util.ReflectionUtils; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; - -/** - * Apollo Annotation Processor for Spring Application - * - * @author Jason Song(song_s@ctrip.com) - */ -public class ApolloAnnotationProcessor extends ApolloProcessor { - - @Override - protected void processField(Object bean, String beanName, Field field) { - ApolloConfig annotation = AnnotationUtils.getAnnotation(field, ApolloConfig.class); - if (annotation == null) { - return; - } - - Preconditions.checkArgument(Config.class.isAssignableFrom(field.getType()), "Invalid type: %s for field: %s, should be Config", field.getType(), field); - - // 创建 Config 对象 - Config config = Singleton.get(DefaultConfig.class); - - // 设置 Config 对象,到对应的 Field - ReflectionUtils.makeAccessible(field); - ReflectionUtils.setField(field, bean, config); - } - - @Override - protected void processMethod(final Object bean, String beanName, final Method method) { - ApolloConfigChangeListener annotation = AnnotationUtils.findAnnotation(method, ApolloConfigChangeListener.class); - if (annotation == null) { - return; - } - Class[] parameterTypes = method.getParameterTypes(); - Preconditions.checkArgument(parameterTypes.length == 1, "Invalid number of parameters: %s for method: %s, should be 1", parameterTypes.length, method); - Preconditions.checkArgument(ConfigChangeEvent.class.isAssignableFrom(parameterTypes[0]), "Invalid parameter type: %s for method: %s, should be ConfigChangeEvent", parameterTypes[0], method); - - // 创建 ConfigChangeListener 监听器。该监听器会调用被注解的方法。 - ReflectionUtils.makeAccessible(method); - ConfigChangeListener configChangeListener = changeEvent -> { - // 反射调用 - ReflectionUtils.invokeMethod(method, bean, changeEvent); - }; - - // 向指定 Namespace 的 Config 对象们,注册该监听器 - Config config = Singleton.get(DefaultConfig.class); - config.addChangeListener(configChangeListener); - } - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/annotation/ApolloConfig.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/annotation/ApolloConfig.java deleted file mode 100644 index 476b5a236..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/annotation/ApolloConfig.java +++ /dev/null @@ -1,22 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox.spring.annotation; - -import java.lang.annotation.*; - -/** - * Use this annotation to inject Apollo Config Instance. - * - *

Usage example:

- *
- * //Inject the config for "someNamespace"
- * @ApolloConfig("someNamespace")
- * private Config config;
- * 
- * - * @author Jason Song(song_s@ctrip.com) - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.FIELD) -@Documented -public @interface ApolloConfig { - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/annotation/ApolloConfigChangeListener.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/annotation/ApolloConfigChangeListener.java deleted file mode 100644 index 7e642b3c5..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/annotation/ApolloConfigChangeListener.java +++ /dev/null @@ -1,15 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox.spring.annotation; - -import java.lang.annotation.*; - -/** - * Use this annotation to register Apollo ConfigChangeListener. - * - * @author Jason Song(song_s@ctrip.com) - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -@Documented -public @interface ApolloConfigChangeListener { - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/annotation/ApolloProcessor.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/annotation/ApolloProcessor.java deleted file mode 100644 index 29d222c3c..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/annotation/ApolloProcessor.java +++ /dev/null @@ -1,68 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox.spring.annotation; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.core.Ordered; -import org.springframework.core.PriorityOrdered; -import org.springframework.util.ReflectionUtils; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.LinkedList; -import java.util.List; - -/** - * Apollo 处理器抽象类,封装了在 Spring Bean 初始化之前,处理属性和方法。 - * - * Create by zhangzheng on 2018/2/6 - */ -public abstract class ApolloProcessor implements BeanPostProcessor, PriorityOrdered { - - @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - Class clazz = bean.getClass(); - // 处理所有 Field - for (Field field : findAllField(clazz)) { - processField(bean, beanName, field); - } - // 处理所有的 Method - for (Method method : findAllMethod(clazz)) { - processMethod(bean, beanName, method); - } - return bean; - } - - @Override - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - return bean; - } - - /** - * subclass should implement this method to process field - */ - protected abstract void processField(Object bean, String beanName, Field field); - - /** - * subclass should implement this method to process method - */ - protected abstract void processMethod(Object bean, String beanName, Method method); - - @Override - public int getOrder() { - // make it as late as possible - return Ordered.LOWEST_PRECEDENCE; // 最高优先级 - } - - private List findAllField(Class clazz) { - final List res = new LinkedList<>(); - ReflectionUtils.doWithFields(clazz, res::add); - return res; - } - - private List findAllMethod(Class clazz) { - final List res = new LinkedList<>(); - ReflectionUtils.doWithMethods(clazz, res::add); - return res; - } - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/annotation/SpringValueProcessor.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/annotation/SpringValueProcessor.java deleted file mode 100644 index f5cc34060..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/annotation/SpringValueProcessor.java +++ /dev/null @@ -1,141 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox.spring.annotation; - -import cn.hutool.core.lang.Singleton; -import cn.iocoder.dashboard.framework.apollox.spring.property.*; -import com.google.common.collect.LinkedListMultimap; -import com.google.common.collect.Multimap; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.BeanUtils; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.context.annotation.Bean; - -import java.beans.PropertyDescriptor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.Collection; -import java.util.Set; - -/** - * Spring value processor of field or method which has @Value and xml config placeholders. - * - * Spring Value 处理器,处理: - * - * 1. 带有 `@Value` 注解的 Field 和 Method - * 2. XML 配置的 Bean 的 PlaceHolder 们 - * - * 每个 Field、Method、XML PlaceHolder 被处理成一个 SpringValue 对象,添加到 SpringValueRegistry 中。 - * - * 目的还是,为了 PlaceHolder 的自动更新机制。 - * - * @author github.com/zhegexiaohuozi seimimaster@gmail.com - * @since 2017/12/20. - */ -@Slf4j -public class SpringValueProcessor extends ApolloProcessor implements BeanFactoryPostProcessor { - - /** - * SpringValueDefinition 集合 - * - * KEY:beanName - * VALUE:SpringValueDefinition 集合 - */ - private static Multimap beanName2SpringValueDefinitions = LinkedListMultimap.create(); - - private final PlaceholderHelper placeholderHelper = Singleton.get(PlaceholderHelper.class); - private final SpringValueRegistry springValueRegistry = Singleton.get(SpringValueRegistry.class); - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - beanName2SpringValueDefinitions = SpringValueDefinitionProcessor.getBeanName2SpringValueDefinitions(); - } - - @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - // 处理 Field 和 Method - super.postProcessBeforeInitialization(bean, beanName); - // 处理 XML 配置的 Bean 的 PlaceHolder 们 - processBeanPropertyValues(bean, beanName); - return bean; - } - - @Override - protected void processField(Object bean, String beanName, Field field) { - // register @Value on field - Value value = field.getAnnotation(Value.class); - if (value == null) { - return; - } - // 提取 `keys` 属性们。 - Set keys = placeholderHelper.extractPlaceholderKeys(value.value()); - if (keys.isEmpty()) { - return; - } - // 循环 `keys` ,创建对应的 SpringValue 对象,并添加到 `springValueRegistry` 中。 - for (String key : keys) { - SpringValue springValue = new SpringValue(key, value.value(), bean, beanName, field, false); - springValueRegistry.register(key, springValue); - log.debug("Monitoring {}", springValue); - } - } - - @Override - protected void processMethod(Object bean, String beanName, Method method) { - // register @Value on method - Value value = method.getAnnotation(Value.class); - if (value == null) { - return; - } - // 忽略 @Bean 注解的方法 - // skip Configuration bean methods - if (method.getAnnotation(Bean.class) != null) { - return; - } - // 忽略非 setting 方法 - if (method.getParameterTypes().length != 1) { - log.error("Ignore @Value setter {}.{}, expecting 1 parameter, actual {} parameters", bean.getClass().getName(), method.getName(), method.getParameterTypes().length); - return; - } - // 提取 `keys` 属性们。 - Set keys = placeholderHelper.extractPlaceholderKeys(value.value()); - if (keys.isEmpty()) { - return; - } - // 循环 `keys` ,创建对应的 SpringValue 对象,并添加到 `springValueRegistry` 中。 - for (String key : keys) { - SpringValue springValue = new SpringValue(key, value.value(), bean, beanName, method, false); - springValueRegistry.register(key, springValue); - log.info("Monitoring {}", springValue); - } - } - - private void processBeanPropertyValues(Object bean, String beanName) { - // 获得 SpringValueDefinition 数组 - Collection propertySpringValues = beanName2SpringValueDefinitions.get(beanName); - if (propertySpringValues == null || propertySpringValues.isEmpty()) { - return; - } - // 循环 SpringValueDefinition 数组,创建对应的 SpringValue 对象,并添加到 `springValueRegistry` 中。 - for (SpringValueDefinition definition : propertySpringValues) { - try { - PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(bean.getClass(), definition.getPropertyName()); - Method method = pd.getWriteMethod(); - if (method == null) { - continue; - } - SpringValue springValue = new SpringValue(definition.getKey(), definition.getPlaceholder(), bean, beanName, method, false); - springValueRegistry.register(definition.getKey(), springValue); - log.debug("Monitoring {}", springValue); - } catch (Throwable ex) { - log.error("Failed to enable auto update feature for {}.{}", bean.getClass(), definition.getPropertyName()); - } - } - - // clear - // 移除 Bean 对应的 SpringValueDefinition 数组 - beanName2SpringValueDefinitions.removeAll(beanName); - } - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/boot/ApolloApplicationContextInitializer.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/boot/ApolloApplicationContextInitializer.java deleted file mode 100644 index f51c68735..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/boot/ApolloApplicationContextInitializer.java +++ /dev/null @@ -1,57 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox.spring.boot; - - -import cn.iocoder.dashboard.framework.apollox.Config; -import cn.iocoder.dashboard.framework.apollox.ConfigService; -import cn.iocoder.dashboard.framework.apollox.spring.config.ConfigPropertySourceFactory; -import cn.iocoder.dashboard.framework.apollox.spring.config.PropertySourcesConstants; -import cn.iocoder.dashboard.framework.apollox.spring.util.SpringInjector; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.core.env.CompositePropertySource; -import org.springframework.core.env.ConfigurableEnvironment; - -import java.util.Collections; -import java.util.List; - -import static cn.iocoder.dashboard.framework.apollox.spring.config.PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME; - -@Slf4j -public class ApolloApplicationContextInitializer implements ApplicationContextInitializer { - - private final ConfigPropertySourceFactory configPropertySourceFactory = SpringInjector.getInstance(ConfigPropertySourceFactory.class); - - @Override - public void initialize(ConfigurableApplicationContext context) { - ConfigurableEnvironment environment = context.getEnvironment(); - // 忽略,若已经有 APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME 的 PropertySource - if (environment.getPropertySources().contains(APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME)) { - // already initialized - return; - } - - // 忽略,若已经有 APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME 的 PropertySource - if (environment.getPropertySources().contains(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME)) { - // already initialized - return; - } - - // 获得 "apollo.bootstrap.namespaces" 配置项 - List namespaceList = Collections.singletonList("default"); - - // 按照优先级,顺序遍历 Namespace - CompositePropertySource composite = new CompositePropertySource(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME); - for (String namespace : namespaceList) { - // 创建 Apollo Config 对象 - Config config = ConfigService.getConfig(namespace); - // 创建 Namespace 对应的 ConfigPropertySource 对象 - // 添加到 `composite` 中。 - composite.addPropertySource(configPropertySourceFactory.getConfigPropertySource(namespace, config)); - } - - // 添加到 `environment` 中,且优先级最高 - environment.getPropertySources().addFirst(composite); - } - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/boot/ApolloAutoConfiguration.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/boot/ApolloAutoConfiguration.java deleted file mode 100644 index fb13b1d73..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/boot/ApolloAutoConfiguration.java +++ /dev/null @@ -1,18 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox.spring.boot; - -import cn.iocoder.dashboard.framework.apollox.spring.config.ConfigPropertySourcesProcessor; -import cn.iocoder.dashboard.framework.apollox.spring.property.PropertySourcesProcessor; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -@ConditionalOnMissingBean(PropertySourcesProcessor.class) // 缺失 PropertySourcesProcessor 时 -public class ApolloAutoConfiguration { - - @Bean - public ConfigPropertySourcesProcessor configPropertySourcesProcessor() { - return new ConfigPropertySourcesProcessor(); // 注入 ConfigPropertySourcesProcessor bean 对象 - } - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/config/ConfigPropertySource.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/config/ConfigPropertySource.java deleted file mode 100644 index c4c6d9d7b..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/config/ConfigPropertySource.java +++ /dev/null @@ -1,49 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox.spring.config; - -import cn.iocoder.dashboard.framework.apollox.Config; -import cn.iocoder.dashboard.framework.apollox.ConfigChangeListener; -import org.springframework.core.env.EnumerablePropertySource; - -import java.util.Set; - -/** - * Property source wrapper for Config - * - * 基于 {@link Config} 的 PropertySource 实现类 - * - * @author Jason Song(song_s@ctrip.com) - */ -public class ConfigPropertySource extends EnumerablePropertySource { - - private static final String[] EMPTY_ARRAY = new String[0]; - - ConfigPropertySource(String name, Config source) { // 此处的 Apollo Config 作为 `source` - super(name, source); - } - - @Override - public String[] getPropertyNames() { - // 从 Config 中,获得属性名集合 - Set propertyNames = this.source.getPropertyNames(); - // 转换成 String 数组,返回 - if (propertyNames.isEmpty()) { - return EMPTY_ARRAY; - } - return propertyNames.toArray(new String[0]); - } - - @Override - public Object getProperty(String name) { - return this.source.getProperty(name, null); - } - - /** - * 添加 ConfigChangeListener 到 Config 中 - * - * @param listener 监听器 - */ - public void addChangeListener(ConfigChangeListener listener) { - this.source.addChangeListener(listener); - } - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/config/ConfigPropertySourceFactory.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/config/ConfigPropertySourceFactory.java deleted file mode 100644 index 418380b8a..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/config/ConfigPropertySourceFactory.java +++ /dev/null @@ -1,31 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox.spring.config; - -import cn.iocoder.dashboard.framework.apollox.Config; -import com.google.common.collect.Lists; - -import java.util.List; - -/** - * {@link ConfigPropertySource} 工厂 - */ -public class ConfigPropertySourceFactory { - - /** - * ConfigPropertySource 数组 - */ - private final List configPropertySources = Lists.newLinkedList(); - - // 创建 ConfigPropertySource 对象 - public ConfigPropertySource getConfigPropertySource(String name, Config source) { - // 创建 ConfigPropertySource 对象 - ConfigPropertySource configPropertySource = new ConfigPropertySource(name, source); - // 添加到数组中 - configPropertySources.add(configPropertySource); - return configPropertySource; - } - - public List getAllConfigPropertySources() { - return Lists.newLinkedList(configPropertySources); - } - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/config/ConfigPropertySourcesProcessor.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/config/ConfigPropertySourcesProcessor.java deleted file mode 100644 index 218b681e4..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/config/ConfigPropertySourcesProcessor.java +++ /dev/null @@ -1,47 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox.spring.config; - -import cn.iocoder.dashboard.framework.apollox.spring.annotation.ApolloAnnotationProcessor; -import cn.iocoder.dashboard.framework.apollox.spring.annotation.SpringValueProcessor; -import cn.iocoder.dashboard.framework.apollox.spring.property.PropertySourcesProcessor; -import cn.iocoder.dashboard.framework.apollox.spring.property.SpringValueDefinitionProcessor; -import cn.iocoder.dashboard.framework.apollox.spring.util.BeanRegistrationUtil; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; -import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; - -/** - * Apollo Property Sources processor for Spring XML Based Application - * - * @author Jason Song(song_s@ctrip.com) - */ -public class ConfigPropertySourcesProcessor extends PropertySourcesProcessor implements BeanDefinitionRegistryPostProcessor { - - @Override - public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { - // 注册 PropertySourcesPlaceholderConfigurer 到 BeanDefinitionRegistry 中,替换 PlaceHolder 为对应的属性值,参考文章 https://leokongwq.github.io/2016/12/28/spring-PropertyPlaceholderConfigurer.html - BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, PropertySourcesPlaceholderConfigurer.class.getName(), PropertySourcesPlaceholderConfigurer.class); - // 注册 ApolloAnnotationProcessor 到 BeanDefinitionRegistry 中,因为 XML 配置的 Bean 对象,也可能存在 @ApolloConfig 和 @ApolloConfigChangeListener 注解。 - BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, ApolloAnnotationProcessor.class.getName(), ApolloAnnotationProcessor.class); - // 注册 SpringValueProcessor 到 BeanDefinitionRegistry 中,用于 PlaceHolder 自动更新机制 - BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, SpringValueProcessor.class.getName(), SpringValueProcessor.class); - // 注册 ApolloJsonValueProcessor 到 BeanDefinitionRegistry 中,因为 XML 配置的 Bean 对象,也可能存在 @ApolloJsonValue 注解。 -// BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, ApolloJsonValueProcessor.class.getName(), ApolloJsonValueProcessor.class); TODO 芋艿:暂时不需要迁移 - - // 处理 XML 配置的 Spring PlaceHolder - processSpringValueDefinition(registry); - } - - /** - * For Spring 3.x versions, the BeanDefinitionRegistryPostProcessor would not be - * instantiated if it is added in postProcessBeanDefinitionRegistry phase, so we have to manually - * call the postProcessBeanDefinitionRegistry method of SpringValueDefinitionProcessor here... - */ - private void processSpringValueDefinition(BeanDefinitionRegistry registry) { - // 创建 SpringValueDefinitionProcessor 对象 - SpringValueDefinitionProcessor springValueDefinitionProcessor = new SpringValueDefinitionProcessor(); - // 处理 XML 配置的 Spring PlaceHolder - springValueDefinitionProcessor.postProcessBeanDefinitionRegistry(registry); - } - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/config/PropertySourcesConstants.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/config/PropertySourcesConstants.java deleted file mode 100644 index 9a4979077..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/config/PropertySourcesConstants.java +++ /dev/null @@ -1,9 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox.spring.config; - -public interface PropertySourcesConstants { - - String APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME = "ApolloBootstrapPropertySources"; - - String APOLLO_PROPERTY_SOURCE_NAME = "ApolloPropertySources"; - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/property/AutoUpdateConfigChangeListener.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/property/AutoUpdateConfigChangeListener.java deleted file mode 100644 index 05dcd0e10..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/property/AutoUpdateConfigChangeListener.java +++ /dev/null @@ -1,159 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox.spring.property; - -import cn.hutool.core.lang.Singleton; -import cn.iocoder.dashboard.framework.apollox.ConfigChangeListener; -import cn.iocoder.dashboard.framework.apollox.enums.PropertyChangeType; -import cn.iocoder.dashboard.framework.apollox.model.ConfigChange; -import cn.iocoder.dashboard.framework.apollox.model.ConfigChangeEvent; -import com.alibaba.fastjson.JSON; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.TypeConverter; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.core.env.Environment; -import org.springframework.util.CollectionUtils; - -import java.lang.reflect.Field; -import java.lang.reflect.Type; -import java.util.Collection; -import java.util.Objects; -import java.util.Set; - -/** - * 自动更新配置监听器 - * - * Create by zhangzheng on 2018/3/6 - */ -public class AutoUpdateConfigChangeListener implements ConfigChangeListener { - - private static final Logger logger = LoggerFactory.getLogger(AutoUpdateConfigChangeListener.class); - - /** - * {@link TypeConverter#convertIfNecessary(Object, Class, Field)} 是否带上 Field 参数,因为 Spring 3.2.0+ 才有该方法 - */ - private final boolean typeConverterHasConvertIfNecessaryWithFieldParameter; - private final Environment environment; - private final ConfigurableBeanFactory beanFactory; - /** - * TypeConverter 对象,参见 https://blog.csdn.net/rulerp2014/article/details/51100857 - */ - private final TypeConverter typeConverter; - private final PlaceholderHelper placeholderHelper; - private final SpringValueRegistry springValueRegistry; - - public AutoUpdateConfigChangeListener(Environment environment, ConfigurableListableBeanFactory beanFactory) { - this.typeConverterHasConvertIfNecessaryWithFieldParameter = testTypeConverterHasConvertIfNecessaryWithFieldParameter(); - this.beanFactory = beanFactory; - this.typeConverter = this.beanFactory.getTypeConverter(); - this.environment = environment; - this.placeholderHelper = Singleton.get(PlaceholderHelper.class); - this.springValueRegistry = Singleton.get(SpringValueRegistry.class); - } - - @Override - public void onChange(ConfigChangeEvent changeEvent) { - // 获得更新的 KEY 集合 - Set keys = changeEvent.changedKeys(); - if (CollectionUtils.isEmpty(keys)) { - return; - } - // 循环 KEY 集合,更新 StringValue - for (String key : keys) { - // 忽略,若不在 SpringValueRegistry 中 - // 1. check whether the changed key is relevant - Collection targetValues = springValueRegistry.get(key); - if (targetValues == null || targetValues.isEmpty()) { - continue; - } - // 校验是否需要更新 - // 2. check whether the value is really changed or not (since spring property sources have hierarchies) - if (!shouldTriggerAutoUpdate(changeEvent, key)) { - continue; - } - // 循环,更新 SpringValue - // 3. update the value - for (SpringValue val : targetValues) { - updateSpringValue(val); - } - } - } - - /** - * Check whether we should trigger the auto update or not. - *
- * For added or modified keys, we should trigger auto update if the current value in Spring equals to the new value. - *
- * For deleted keys, we will trigger auto update anyway. - */ - private boolean shouldTriggerAutoUpdate(ConfigChangeEvent changeEvent, String changedKey) { - ConfigChange configChange = changeEvent.getChange(changedKey); - // 若变更类型为删除,需要触发更新 - if (configChange.getChangeType() == PropertyChangeType.DELETED) { - return true; - } - // 若变更类型为新增或修改,判断 environment 的值是否和最新值相等。 - // 【高能】!!! - return Objects.equals(environment.getProperty(changedKey), configChange.getNewValue()); - } - - private void updateSpringValue(SpringValue springValue) { - try { - // 解析值 - Object value = resolvePropertyValue(springValue); - // 更新 StringValue - springValue.update(value); - logger.info("Auto update apollo changed value successfully, new value: {}, {}", value, springValue); - } catch (Throwable ex) { - logger.error("Auto update apollo changed value failed, {}", springValue.toString(), ex); - } - } - - /** - * Logic transplanted from DefaultListableBeanFactory - * - * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency(org.springframework.beans.factory.config.DependencyDescriptor, String, Set, TypeConverter) - */ - private Object resolvePropertyValue(SpringValue springValue) { - // value will never be null, as @Value and @ApolloJsonValue will not allow that - Object value = placeholderHelper.resolvePropertyValue(beanFactory, springValue.getBeanName(), springValue.getPlaceholder()); - // 如果值数据结构是 JSON 类型,则使用 Gson 解析成对应值的类型 - if (springValue.isJson()) { - value = parseJsonValue((String) value, springValue.getGenericType()); - } else { - // 如果类型为 Field - if (springValue.isField()) { - // org.springframework.beans.TypeConverter#convertIfNecessary(java.lang.Object, java.lang.Class, java.lang.reflect.Field) is available from Spring 3.2.0+ - if (typeConverterHasConvertIfNecessaryWithFieldParameter) { - value = this.typeConverter.convertIfNecessary(value, springValue.getTargetType(), springValue.getField()); - } else { - value = this.typeConverter.convertIfNecessary(value, springValue.getTargetType()); - } - // 如果类型为 Method - } else { - value = this.typeConverter.convertIfNecessary(value, springValue.getTargetType(), springValue.getMethodParameter()); - } - } - - return value; - } - - private Object parseJsonValue(String json, Type targetType) { - try { - return JSON.parseObject(json, targetType); - } catch (Throwable ex) { - logger.error("Parsing json '{}' to type {} failed!", json, targetType, ex); - throw ex; - } - } - - private boolean testTypeConverterHasConvertIfNecessaryWithFieldParameter() { - try { - TypeConverter.class.getMethod("convertIfNecessary", Object.class, Class.class, Field.class); - } catch (Throwable ex) { - return false; - } - return true; - } - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/property/PlaceholderHelper.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/property/PlaceholderHelper.java deleted file mode 100644 index 2bd5fed4f..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/property/PlaceholderHelper.java +++ /dev/null @@ -1,160 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox.spring.property; - -import com.google.common.base.Strings; -import com.google.common.collect.Sets; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.BeanExpressionContext; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.beans.factory.config.Scope; -import org.springframework.util.StringUtils; - -import java.util.Set; -import java.util.Stack; - -/** - * Placeholder 工具类 - * - * Placeholder helper functions. - */ -public class PlaceholderHelper { - - private static final String PLACEHOLDER_PREFIX = "${"; - private static final String PLACEHOLDER_SUFFIX = "}"; - private static final String VALUE_SEPARATOR = ":"; - private static final String SIMPLE_PLACEHOLDER_PREFIX = "{"; - private static final String EXPRESSION_PREFIX = "#{"; - private static final String EXPRESSION_SUFFIX = "}"; - - /** - * Resolve placeholder property values, e.g. - * - * "${somePropertyValue}" -> "the actual property value" - */ - public Object resolvePropertyValue(ConfigurableBeanFactory beanFactory, String beanName, String placeholder) { - // resolve string value - String strVal = beanFactory.resolveEmbeddedValue(placeholder); - // 获得 BeanDefinition 对象 - BeanDefinition bd = (beanFactory.containsBean(beanName) ? beanFactory.getMergedBeanDefinition(beanName) : null); - // resolve expressions like "#{systemProperties.myProp}" - return evaluateBeanDefinitionString(beanFactory, strVal, bd); - } - - private Object evaluateBeanDefinitionString(ConfigurableBeanFactory beanFactory, String value, BeanDefinition beanDefinition) { - if (beanFactory.getBeanExpressionResolver() == null) { - return value; - } - Scope scope = (beanDefinition != null ? beanFactory.getRegisteredScope(beanDefinition.getScope()) : null); - return beanFactory.getBeanExpressionResolver().evaluate(value, new BeanExpressionContext(beanFactory, scope)); - } - - /** - * Extract keys from placeholder, e.g. - *
    - *
  • ${some.key} => "some.key"
  • - *
  • ${some.key:${some.other.key:100}} => "some.key", "some.other.key"
  • - *
  • ${${some.key}} => "some.key"
  • - *
  • ${${some.key:other.key}} => "some.key"
  • - *
  • ${${some.key}:${another.key}} => "some.key", "another.key"
  • - *
  • #{new java.text.SimpleDateFormat('${some.key}').parse('${another.key}')} => "some.key", "another.key"
  • - *
- */ - public Set extractPlaceholderKeys(String propertyString) { - Set placeholderKeys = Sets.newHashSet(); - - if (!isNormalizedPlaceholder(propertyString) && !isExpressionWithPlaceholder(propertyString)) { - return placeholderKeys; - } - - Stack stack = new Stack<>(); - stack.push(propertyString); - - while (!stack.isEmpty()) { - String strVal = stack.pop(); - int startIndex = strVal.indexOf(PLACEHOLDER_PREFIX); - if (startIndex == -1) { - placeholderKeys.add(strVal); - continue; - } - int endIndex = findPlaceholderEndIndex(strVal, startIndex); - if (endIndex == -1) { - // invalid placeholder? - continue; - } - - String placeholderCandidate = strVal.substring(startIndex + PLACEHOLDER_PREFIX.length(), endIndex); - - // ${some.key:other.key} - if (placeholderCandidate.startsWith(PLACEHOLDER_PREFIX)) { - stack.push(placeholderCandidate); - } else { - // some.key:${some.other.key:100} - int separatorIndex = placeholderCandidate.indexOf(VALUE_SEPARATOR); - - if (separatorIndex == -1) { - stack.push(placeholderCandidate); - } else { - stack.push(placeholderCandidate.substring(0, separatorIndex)); - String defaultValuePart = - normalizeToPlaceholder(placeholderCandidate.substring(separatorIndex + VALUE_SEPARATOR.length())); - if (!Strings.isNullOrEmpty(defaultValuePart)) { - stack.push(defaultValuePart); - } - } - } - - // has remaining part, e.g. ${a}.${b} - if (endIndex + PLACEHOLDER_SUFFIX.length() < strVal.length() - 1) { - String remainingPart = normalizeToPlaceholder(strVal.substring(endIndex + PLACEHOLDER_SUFFIX.length())); - if (!Strings.isNullOrEmpty(remainingPart)) { - stack.push(remainingPart); - } - } - } - - return placeholderKeys; - } - - private boolean isNormalizedPlaceholder(String propertyString) { - return propertyString.startsWith(PLACEHOLDER_PREFIX) && propertyString.endsWith(PLACEHOLDER_SUFFIX); - } - - private boolean isExpressionWithPlaceholder(String propertyString) { - return propertyString.startsWith(EXPRESSION_PREFIX) && propertyString.endsWith(EXPRESSION_SUFFIX) - && propertyString.contains(PLACEHOLDER_PREFIX); - } - - private String normalizeToPlaceholder(String strVal) { - int startIndex = strVal.indexOf(PLACEHOLDER_PREFIX); - if (startIndex == -1) { - return null; - } - int endIndex = strVal.lastIndexOf(PLACEHOLDER_SUFFIX); - if (endIndex == -1) { - return null; - } - - return strVal.substring(startIndex, endIndex + PLACEHOLDER_SUFFIX.length()); - } - - private int findPlaceholderEndIndex(CharSequence buf, int startIndex) { - int index = startIndex + PLACEHOLDER_PREFIX.length(); - int withinNestedPlaceholder = 0; - while (index < buf.length()) { - if (StringUtils.substringMatch(buf, index, PLACEHOLDER_SUFFIX)) { - if (withinNestedPlaceholder > 0) { - withinNestedPlaceholder--; - index = index + PLACEHOLDER_SUFFIX.length(); - } else { - return index; - } - } else if (StringUtils.substringMatch(buf, index, SIMPLE_PLACEHOLDER_PREFIX)) { - withinNestedPlaceholder++; - index = index + SIMPLE_PLACEHOLDER_PREFIX.length(); - } else { - index++; - } - } - return -1; - } - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/property/PropertySourcesProcessor.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/property/PropertySourcesProcessor.java deleted file mode 100644 index 9e995cfe0..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/property/PropertySourcesProcessor.java +++ /dev/null @@ -1,72 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox.spring.property; - -import cn.iocoder.dashboard.framework.apollox.spring.config.ConfigPropertySource; -import cn.iocoder.dashboard.framework.apollox.spring.config.ConfigPropertySourceFactory; -import cn.iocoder.dashboard.framework.apollox.spring.util.SpringInjector; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.context.EnvironmentAware; -import org.springframework.core.Ordered; -import org.springframework.core.PriorityOrdered; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.env.Environment; - -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * Apollo Property Sources processor for Spring Annotation Based Application.

- *

- * The reason why PropertySourcesProcessor implements {@link BeanFactoryPostProcessor} instead of - * {@link org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor} is that lower versions of - * Spring (e.g. 3.1.1) doesn't support registering BeanDefinitionRegistryPostProcessor in ImportBeanDefinitionRegistrar - * - {@link com.ctrip.framework.apollo.spring.annotation.ApolloConfigRegistrar} - * - * @author Jason Song(song_s@ctrip.com) - */ -public class PropertySourcesProcessor implements BeanFactoryPostProcessor, EnvironmentAware, PriorityOrdered { - - /** - * 是否初始化的标识 - */ - private static final AtomicBoolean INITIALIZED = new AtomicBoolean(false); - - private final ConfigPropertySourceFactory configPropertySourceFactory = SpringInjector.getInstance(ConfigPropertySourceFactory.class); - /** - * Spring ConfigurableEnvironment 对象 - */ - private ConfigurableEnvironment environment; - - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - if (INITIALIZED.compareAndSet(false, true)) { - // 初始化 AutoUpdateConfigChangeListener 对象,实现属性的自动更新 - initializeAutoUpdatePropertiesFeature(beanFactory); - } - } - - private void initializeAutoUpdatePropertiesFeature(ConfigurableListableBeanFactory beanFactory) { - // 创建 AutoUpdateConfigChangeListener 对象 - AutoUpdateConfigChangeListener autoUpdateConfigChangeListener = new AutoUpdateConfigChangeListener(environment, beanFactory); - // 循环,向 ConfigPropertySource 注册配置变更器 - List configPropertySources = configPropertySourceFactory.getAllConfigPropertySources(); - for (ConfigPropertySource configPropertySource : configPropertySources) { - configPropertySource.addChangeListener(autoUpdateConfigChangeListener); - } - } - - @Override - public void setEnvironment(Environment environment) { - //it is safe enough to cast as all known environment is derived from ConfigurableEnvironment - this.environment = (ConfigurableEnvironment) environment; - } - - @Override - public int getOrder() { - // make it as early as possible - return Ordered.HIGHEST_PRECEDENCE; // 最高优先级 - } - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/property/SpringValue.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/property/SpringValue.java deleted file mode 100644 index 0d96c100b..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/property/SpringValue.java +++ /dev/null @@ -1,152 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox.spring.property; - -import org.springframework.core.MethodParameter; - -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Type; - -/** - * Spring @Value method info - * - * @author github.com/zhegexiaohuozi seimimaster@gmail.com - * @since 2018/2/6. - */ -public class SpringValue { - - /** - * Bean 对象 - */ - private Object bean; - /** - * Bean 名字 - */ - private String beanName; - /** - * Spring 方法参数封装 - */ - private MethodParameter methodParameter; - /** - * Field - */ - private Field field; - /** - * KEY - * - * 即在 Config 中的属性 KEY 。 - */ - private String key; - /** - * 占位符 - */ - private String placeholder; - /** - * 值类型 - */ - private Class targetType; - /** - * 是否 JSON - */ - private boolean isJson; - /** - * 泛型。当是 JSON 类型时,使用 - */ - private Type genericType; - - // Field - public SpringValue(String key, String placeholder, Object bean, String beanName, Field field, boolean isJson) { - this.bean = bean; - this.beanName = beanName; - // Field - this.field = field; - this.key = key; - this.placeholder = placeholder; - // Field 差异 - this.targetType = field.getType(); - this.isJson = isJson; - if (isJson) { - this.genericType = field.getGenericType(); - } - } - - // Method - public SpringValue(String key, String placeholder, Object bean, String beanName, Method method, boolean isJson) { - this.bean = bean; - this.beanName = beanName; - // Method - this.methodParameter = new MethodParameter(method, 0); - this.key = key; - this.placeholder = placeholder; - // Method 差异 - Class[] paramTps = method.getParameterTypes(); - this.targetType = paramTps[0]; - this.isJson = isJson; - if (isJson) { - this.genericType = method.getGenericParameterTypes()[0]; - } - } - - public void update(Object newVal) throws IllegalAccessException, InvocationTargetException { - // Field - if (isField()) { - injectField(newVal); - // Method - } else { - injectMethod(newVal); - } - } - - private void injectField(Object newVal) throws IllegalAccessException { - boolean accessible = field.isAccessible(); - field.setAccessible(true); - field.set(bean, newVal); - field.setAccessible(accessible); - } - - private void injectMethod(Object newVal) throws InvocationTargetException, IllegalAccessException { - methodParameter.getMethod().invoke(bean, newVal); - } - - public String getBeanName() { - return beanName; - } - - public Class getTargetType() { - return targetType; - } - - public String getPlaceholder() { - return this.placeholder; - } - - public MethodParameter getMethodParameter() { - return methodParameter; - } - - public boolean isField() { - return this.field != null; - } - - public Field getField() { - return field; - } - - public Type getGenericType() { - return genericType; - } - - public boolean isJson() { - return isJson; - } - - @Override - public String toString() { - if (isField()) { - return String.format("key: %s, beanName: %s, field: %s.%s", key, beanName, bean.getClass().getName(), field.getName()); - } - return String.format("key: %s, beanName: %s, method: %s.%s", key, beanName, bean.getClass().getName(), - methodParameter.getMethod().getName()); - } - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/property/SpringValueDefinition.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/property/SpringValueDefinition.java deleted file mode 100644 index 34ad40f61..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/property/SpringValueDefinition.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox.spring.property; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * Spring Value 定义 - */ -@Getter -@AllArgsConstructor -public class SpringValueDefinition { - - /** - * KEY - * - * 即在 Config 中的属性 KEY 。 - */ - private final String key; - /** - * 占位符 - */ - private final String placeholder; - /** - * 属性名 - */ - private final String propertyName; - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/property/SpringValueDefinitionProcessor.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/property/SpringValueDefinitionProcessor.java deleted file mode 100644 index a9cf1286b..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/property/SpringValueDefinitionProcessor.java +++ /dev/null @@ -1,94 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox.spring.property; - -import cn.hutool.core.lang.Singleton; -import com.google.common.collect.LinkedListMultimap; -import com.google.common.collect.Multimap; -import org.springframework.beans.BeansException; -import org.springframework.beans.MutablePropertyValues; -import org.springframework.beans.PropertyValue; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.config.TypedStringValue; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; - -import java.util.List; -import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * To process xml config placeholders, e.g. - * - *

- *  <bean class="com.ctrip.framework.apollo.demo.spring.xmlConfigDemo.bean.XmlBean">
- *    <property name="timeout" value="${timeout:200}"/>
- *    <property name="batch" value="${batch:100}"/>
- *  </bean>
- * 
- */ -public class SpringValueDefinitionProcessor implements BeanDefinitionRegistryPostProcessor { - - /** - * SpringValueDefinition 集合 - *

- * KEY:beanName - * VALUE:SpringValueDefinition 集合 - */ - private static final Multimap beanName2SpringValueDefinitions = LinkedListMultimap.create(); - /** - * 是否初始化的标识 - */ - private static final AtomicBoolean initialized = new AtomicBoolean(false); - - private final PlaceholderHelper placeholderHelper = Singleton.get(PlaceholderHelper.class); - - @Override - public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { - processPropertyValues(registry); - } - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - } - - public static Multimap getBeanName2SpringValueDefinitions() { - return beanName2SpringValueDefinitions; - } - - private void processPropertyValues(BeanDefinitionRegistry beanRegistry) { - // 若已经初始化,直接返回 - if (!initialized.compareAndSet(false, true)) { - // already initialized - return; - } - // 循环 BeanDefinition 集合 - String[] beanNames = beanRegistry.getBeanDefinitionNames(); - for (String beanName : beanNames) { - BeanDefinition beanDefinition = beanRegistry.getBeanDefinition(beanName); - // 循环 BeanDefinition 的 PropertyValue 数组 - MutablePropertyValues mutablePropertyValues = beanDefinition.getPropertyValues(); - List propertyValues = mutablePropertyValues.getPropertyValueList(); - for (PropertyValue propertyValue : propertyValues) { - // 获得 `value` 属性。 - Object value = propertyValue.getValue(); - // 忽略非 Spring PlaceHolder 的 `value` 属性。 - if (!(value instanceof TypedStringValue)) { - continue; - } - // 获得 `placeholder` 属性。 - String placeholder = ((TypedStringValue) value).getValue(); - // 提取 `keys` 属性们。 - Set keys = placeholderHelper.extractPlaceholderKeys(placeholder); - if (keys.isEmpty()) { - continue; - } - // 循环 `keys` ,创建对应的 SpringValueDefinition 对象,并添加到 `beanName2SpringValueDefinitions` 中。 - for (String key : keys) { - beanName2SpringValueDefinitions.put(beanName, - new SpringValueDefinition(key, placeholder, propertyValue.getName())); - } - } - } - } - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/property/SpringValueRegistry.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/property/SpringValueRegistry.java deleted file mode 100644 index c2a4bbe1b..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/property/SpringValueRegistry.java +++ /dev/null @@ -1,31 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox.spring.property; - -import com.google.common.collect.LinkedListMultimap; -import com.google.common.collect.Multimap; - -import java.util.Collection; - -/** - * {@link SpringValue} 注册表 - */ -public class SpringValueRegistry { - - /** - * SpringValue 集合 - * - * KEY:属性 KEY ,即 Config 配置 KEY - * VALUE:SpringValue 数组 - */ - private final Multimap registry = LinkedListMultimap.create(); - - // 注册 - public void register(String key, SpringValue springValue) { - registry.put(key, springValue); - } - - // 获得 - public Collection get(String key) { - return registry.get(key); - } - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/util/BeanRegistrationUtil.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/util/BeanRegistrationUtil.java deleted file mode 100644 index 8e1689cd7..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/util/BeanRegistrationUtil.java +++ /dev/null @@ -1,38 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox.spring.util; - -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; - -import java.util.Objects; - -/** - * Bean Registration 工具类 - * - * @author Jason Song(song_s@ctrip.com) - */ -public class BeanRegistrationUtil { - - // 注册 `beanClass` 到 BeanDefinitionRegistry 中,当且仅当 `beanName` 和 `beanClass` 都不存在对应的 BeanDefinition 时 - public static boolean registerBeanDefinitionIfNotExists(BeanDefinitionRegistry registry, String beanName, Class beanClass) { - // 不存在 `beanName` 对应的 BeanDefinition - if (registry.containsBeanDefinition(beanName)) { - return false; - } - - // 不存在 `beanClass` 对应的 BeanDefinition - String[] candidates = registry.getBeanDefinitionNames(); - for (String candidate : candidates) { - BeanDefinition beanDefinition = registry.getBeanDefinition(candidate); - if (Objects.equals(beanDefinition.getBeanClassName(), beanClass.getName())) { - return false; - } - } - - // 注册 `beanClass` 到 BeanDefinitionRegistry 中 - BeanDefinition annotationProcessor = BeanDefinitionBuilder.genericBeanDefinition(beanClass).getBeanDefinition(); - registry.registerBeanDefinition(beanName, annotationProcessor); - return true; - } - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/util/SpringInjector.java b/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/util/SpringInjector.java deleted file mode 100644 index cb6d398b9..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/apollox/spring/util/SpringInjector.java +++ /dev/null @@ -1,14 +0,0 @@ -package cn.iocoder.dashboard.framework.apollox.spring.util; - -import cn.hutool.core.lang.Singleton; - -/** - * Spring 注入器 - */ -public class SpringInjector { - - public static T getInstance(Class clazz) { - return Singleton.get(clazz); - } - -} diff --git a/src/main/resources/META-INF/spring.factories b/src/main/resources/META-INF/spring.factories deleted file mode 100644 index f70022113..000000000 --- a/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,4 +0,0 @@ -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - cn.iocoder.dashboard.framework.apollox.spring.boot.ApolloAutoConfiguration -org.springframework.context.ApplicationContextInitializer=\ - cn.iocoder.dashboard.framework.apollox.spring.boot.ApolloApplicationContextInitializer diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 14248ca39..de8e890c9 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -50,10 +50,9 @@ yudao: # Apollo 配置中心 apollo: bootstrap: - enabled: true + enabled: true # 设置 Apollo 在启动阶段生效 eagerLoad: - enabled: true - autoUpdateInjectedSpringProperties: true + enabled: true # 设置 Apollo 在日志初始化前生效,可以实现日志的动态级别配置 # MyBatis Plus 的配置项 mybatis-plus: