欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java Apollo是如何實現(xiàn)配置更新的

 更新時間:2021年03月02日 16:24:52   作者:雙鬼單帶  
這篇文章主要介紹了Java Apollo是如何實現(xiàn)配置更新的,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下

這篇文檔主要關(guān)注下配置修改后對應(yīng)的 Java 對象是如何更新,并不關(guān)注整體的配置改動流程

所有代碼都來自 apollo-client 項目

更新流程

在 Apollo 控制臺進行配置修改并發(fā)布后,對應(yīng)的 client 端拉取到更新后,會調(diào)用到 com.ctrip.framework.apollo.spring.property.AutoUpdateConfigChangeListener#onChange 方法

在調(diào)用 onChange 會收到對應(yīng)的修改的配置信息 ConfigChangeEvent, 其中包含改動的 key 和 value, 則改動流程如下:

  1. 根據(jù)改動的配置的 key 從 springValueRegistry 找到對應(yīng)的關(guān)聯(lián)到這個 key 的 Spring Bean 信息,如果找不到則不處理
  2. 根據(jù)找到的 Spring Bean 信息,進行對應(yīng)關(guān)聯(lián)配置的更新

在第二步中會判斷關(guān)聯(lián)配置是用過屬性關(guān)聯(lián)還是方法進行關(guān)聯(lián)的,代碼如下

public void update(Object newVal) throws IllegalAccessException, InvocationTargetException {
  if (isField()) {
    injectField(newVal);
  } else {
    injectMethod(newVal);
  }
}

在上面的問題中,還有兩個問題存疑

  1. 如何通過 key 找到對應(yīng)的 Spring Bean 信息
  2. 如何將 Apollo 的配置值轉(zhuǎn)換為 Spring 的識別的值
public class AutoUpdateConfigChangeListener implements ConfigChangeListener{
 private static final Logger logger = LoggerFactory.getLogger(AutoUpdateConfigChangeListener.class);

 private final boolean typeConverterHasConvertIfNecessaryWithFieldParameter;
 private final Environment environment;
 private final ConfigurableBeanFactory beanFactory;
 private final TypeConverter typeConverter;
 private final PlaceholderHelper placeholderHelper;
 private final SpringValueRegistry springValueRegistry;
 private final Gson gson;

 public AutoUpdateConfigChangeListener(Environment environment, ConfigurableListableBeanFactory beanFactory){
  this.typeConverterHasConvertIfNecessaryWithFieldParameter = testTypeConverterHasConvertIfNecessaryWithFieldParameter();
  this.beanFactory = beanFactory;
  this.typeConverter = this.beanFactory.getTypeConverter();
  this.environment = environment;
  this.placeholderHelper = SpringInjector.getInstance(PlaceholderHelper.class);
  this.springValueRegistry = SpringInjector.getInstance(SpringValueRegistry.class);
  this.gson = new Gson();
 }

 @Override
 public void onChange(ConfigChangeEvent changeEvent) {
  Set<String> keys = changeEvent.changedKeys();
  if (CollectionUtils.isEmpty(keys)) {
   return;
  }
  for (String key : keys) {
   // 1. check whether the changed key is relevant
   Collection<SpringValue> targetValues = springValueRegistry.get(beanFactory, key);
   if (targetValues == null || targetValues.isEmpty()) {
    continue;
   }

   // 2. update the value
   for (SpringValue val : targetValues) {
    updateSpringValue(val);
   }
  }
 }

 private void updateSpringValue(SpringValue springValue) {
  try {
   Object value = resolvePropertyValue(springValue);
   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, java.lang.String, java.util.Set, org.springframework.beans.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());

  if (springValue.isJson()) {
   value = parseJsonValue((String)value, springValue.getGenericType());
  } else {
   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());
    }
   } else {
    value = this.typeConverter.convertIfNecessary(value, springValue.getTargetType(),
      springValue.getMethodParameter());
   }
  }

  return value;
 }

 private Object parseJsonValue(String json, Type targetType) {
  try {
   return gson.fromJson(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;
 }
}

如何將配置 key 和 Spring Bean 關(guān)聯(lián)起來

在 Spring 常見配置包括 2 種

public class ApiConfig {
 
 	// 1. 直接在 Field 是進行注入
  @Value("${feifei.appId}")
  protected String appId;

  protected String predUrl;

 	// 2. 在方法上進行注入
  @Value("${predUrl}")
  public void setPredUrl(String predUrl) {
    this.predUrl = predUrl;
  }
}

在 Apollo 代碼中,通過實現(xiàn) BeanPostProcessor 接口來檢測所有的Spring Bean 的創(chuàng)建過程,在 Spring Bean 創(chuàng)建的過程中會調(diào)用對應(yīng)的 org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization 方法。

Apollo 通過在 Bean 生成過程中,檢測 Bean 類中屬性和方法是否存在 @Value 注解,如果存在,提出其中的 key, 其處理方法在 processFieldprocessMethod 分別處理 Field 和 Method 中可能出現(xiàn)的 @Value 注解。如果存在注解則將對應(yīng)的信息存到 SpringValue 對應(yīng) springValueRegistry 全局對象中,方便在其它地方可以直接獲取。

在屬性除了通過 @Value 注入,也可以用過 xml 進行配置,在這種情況通過 processBeanPropertyValues 方法來處理

通過兩種處理方式就可以將 key 和對應(yīng)的 Spring Bean 信息關(guān)聯(lián)起來

public class SpringValueProcessor extends ApolloProcessor implements BeanFactoryPostProcessor, BeanFactoryAware {

 private static final Logger logger = LoggerFactory.getLogger(SpringValueProcessor.class);

 private final ConfigUtil configUtil;
 private final PlaceholderHelper placeholderHelper;
 private final SpringValueRegistry springValueRegistry;

 private BeanFactory beanFactory;
 private Multimap<String, SpringValueDefinition> beanName2SpringValueDefinitions;

 public SpringValueProcessor() {
  configUtil = ApolloInjector.getInstance(ConfigUtil.class);
  placeholderHelper = SpringInjector.getInstance(PlaceholderHelper.class);
  springValueRegistry = SpringInjector.getInstance(SpringValueRegistry.class);
  beanName2SpringValueDefinitions = LinkedListMultimap.create();
 }

 @Override
 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
   throws BeansException {
  if (configUtil.isAutoUpdateInjectedSpringPropertiesEnabled() && beanFactory instanceof BeanDefinitionRegistry) {
   beanName2SpringValueDefinitions = SpringValueDefinitionProcessor
     .getBeanName2SpringValueDefinitions((BeanDefinitionRegistry) beanFactory);
  }
 }

 @Override
 public Object postProcessBeforeInitialization(Object bean, String beanName)
   throws BeansException {
  if (configUtil.isAutoUpdateInjectedSpringPropertiesEnabled()) {
   super.postProcessBeforeInitialization(bean, beanName);
   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;
  }
  Set<String> keys = placeholderHelper.extractPlaceholderKeys(value.value());

  if (keys.isEmpty()) {
   return;
  }

  for (String key : keys) {
   SpringValue springValue = new SpringValue(key, value.value(), bean, beanName, field, false);
   springValueRegistry.register(beanFactory, key, springValue);
   logger.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;
  }
  //skip Configuration bean methods
  if (method.getAnnotation(Bean.class) != null) {
   return;
  }
  if (method.getParameterTypes().length != 1) {
   logger.error("Ignore @Value setter {}.{}, expecting 1 parameter, actual {} parameters",
     bean.getClass().getName(), method.getName(), method.getParameterTypes().length);
   return;
  }

  Set<String> keys = placeholderHelper.extractPlaceholderKeys(value.value());

  if (keys.isEmpty()) {
   return;
  }

  for (String key : keys) {
   SpringValue springValue = new SpringValue(key, value.value(), bean, beanName, method, false);
   springValueRegistry.register(beanFactory, key, springValue);
   logger.info("Monitoring {}", springValue);
  }
 }


 private void processBeanPropertyValues(Object bean, String beanName) {
  Collection<SpringValueDefinition> propertySpringValues = beanName2SpringValueDefinitions
    .get(beanName);
  if (propertySpringValues == null || propertySpringValues.isEmpty()) {
   return;
  }

  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(beanFactory, definition.getKey(), springValue);
    logger.debug("Monitoring {}", springValue);
   } catch (Throwable ex) {
    logger.error("Failed to enable auto update feature for {}.{}", bean.getClass(),
      definition.getPropertyName());
   }
  }

  // clear
  beanName2SpringValueDefinitions.removeAll(beanName);
 }

 @Override
 public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
  this.beanFactory = beanFactory;
 }
}

以上就是Java Apollo是如何實現(xiàn)配置更新的的詳細(xì)內(nèi)容,更多關(guān)于Java Apollo 配置更新的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java用自定義的類作為HashMap的key值實例

    Java用自定義的類作為HashMap的key值實例

    下面小編就為大家?guī)硪黄狫ava用自定義的類作為HashMap的key值實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-12-12
  • Java NumberFormat格式化float類型的bug

    Java NumberFormat格式化float類型的bug

    今天小編就為大家分享一篇關(guān)于Java NumberFormat格式化float類型的bug,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-10-10
  • java struts2學(xué)習(xí)筆記之線程安全

    java struts2學(xué)習(xí)筆記之線程安全

    這篇文章主要為大家詳細(xì)介紹了java struts2學(xué)習(xí)筆記之線程安全,感興趣的朋友可以參考一下
    2016-04-04
  • Java異常處理實例教程

    Java異常處理實例教程

    這篇文章主要為大家分享一份非常詳細(xì)的Java異常處理實例教程,幫助大家更好的學(xué)習(xí)java異常處理,感興趣的小伙伴們可以參考一下
    2016-02-02
  • Mybatis第三方PageHelper分頁插件的使用與原理

    Mybatis第三方PageHelper分頁插件的使用與原理

    提到插件相信大家都知道,插件的存在主要是用來改變或者增強原有的功能,MyBatis中也一樣,下面這篇文章主要給大家介紹了關(guān)于Mybatis第三方PageHelper分頁插件的使用與原理,需要的朋友可以參考下
    2022-02-02
  • Spring Security基于json登錄實現(xiàn)過程詳解

    Spring Security基于json登錄實現(xiàn)過程詳解

    這篇文章主要介紹了Spring Security基于json登錄實現(xiàn)過程詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-08-08
  • Java 中Map 的用法詳解

    Java 中Map 的用法詳解

    本文主要介紹java 中的Map 接口, 這里對Map 接口下的幾個類做了詳細(xì)介紹,希望對學(xué)習(xí)java 編程的小伙伴有所幫助
    2016-07-07
  • springboot使用Thymeleaf報錯常見的幾種解決方案

    springboot使用Thymeleaf報錯常見的幾種解決方案

    這篇文章主要介紹了springboot使用Thymeleaf報錯常見的幾種解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • java 使用idea將工程打成jar并創(chuàng)建成exe文件類型執(zhí)行的方法詳解

    java 使用idea將工程打成jar并創(chuàng)建成exe文件類型執(zhí)行的方法詳解

    這篇文章主要介紹了java 使用idea將工程打成jar并創(chuàng)建成exe文件類型執(zhí)行,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2020-09-09
  • java對同一個文件進行讀寫操作方法

    java對同一個文件進行讀寫操作方法

    在本篇文章里我們給大家詳細(xì)講述了java對同一個文件進行讀寫操作的方法和知識點,需要的朋友們可以參考學(xué)習(xí)下。
    2018-10-10

最新評論