携程Apollo热发布机制分析

背景

最近在使用apollo的时候,发现@Value注释的值,有部分能热更新,部分不能;对于ConfigurationProperties的配置类,完全不支持更新。所以想着了解一下apollo的热发布机制,看看是怎么实现的热发布。

官方介绍

Apollo配置中心设计
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

从上面三个图可以看出:热更新,就是基于http长连接,当在apollo管理界面上修改配置时,会通知客户端有变更,然后客户端再去拉取最新的配置。

官方文档只是介绍了一下热更新机制,但是对于客户端拿到最新的配置后,怎么做到更新spring的bean的属性,却没提到。

SpringValueProcessor代码

com.ctrip.framework.apollo.spring.annotation目录下的类如下:
在这里插入图片描述
可以看到里面有一个SpringValueProcessor,从名字看,这个类的作用似乎是对spring中加了@Value注解的bean做处理的。

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 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);
    }
  }
  
  ......
  ......
  
}

SpringValueProcessor.postProcessBeforeInitialization

SpringValueProcessor重要的是postProcessBeforeInitialization方法:

@Override
  public Object postProcessBeforeInitialization(Object bean, String beanName)
      throws BeansException {
      // 是否支持自动更新,不支持就啥事情都不做
    if (configUtil.isAutoUpdateInjectedSpringPropertiesEnabled()) {
      // 调用父类的postProcessBeforeInitialization
      super.postProcessBeforeInitialization(bean, beanName);
      // PropertyValues的一些处理,不需要关心
      processBeanPropertyValues(bean, beanName);
    }
    return bean;
  }

SpringValueProcessor.postProcessBeforeInitialization方法调用的是父类的postProcessBeforeInitialization方法,父类的方法内容如下:

public abstract class ApolloProcessor implements BeanPostProcessor, PriorityOrdered {
  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName)
      throws BeansException {
    Class clazz = bean.getClass();
    for (Field field : findAllField(clazz)) {
      processField(bean, beanName, field);
    }
    for (Method method : findAllMethod(clazz)) {
      processMethod(bean, beanName, method);
    }
    return bean;
  }
  
  ......
  
  protected abstract void processField(Object bean, String beanName, Field field);
  protected abstract void processMethod(Object bean, String beanName, Method method);

  ......
  
  private List<Field> findAllField(Class clazz) {
    final List<Field> res = new LinkedList<>();
    ReflectionUtils.doWithFields(clazz, new ReflectionUtils.FieldCallback() {
      @Override
      public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
        res.add(field);
      }
    });
    return res;
  }

  private List<Method> findAllMethod(Class clazz) {
    final List<Method> res = new LinkedList<>();
    ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() {
      @Override
      public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
        res.add(method);
      }
    });
    return res;
  }
}

父类的postProcessBeforeInitialization方法做的事情就是拿到bean的所有属性,遍历调用processField;拿到所有方法,遍历调用procecssMethod。

而processField和procecssMethod两个方法是由子类实现的。

SpringValueProcessor.proccessField

SpringValueProcessor 类的proccessField干的事情如下:

 @Override
  protected void processField(Object bean, String beanName, Field field) {
    // 获取属性上的@Value注解
    Value value = field.getAnnotation(Value.class);
    // 如果没有@Value注解,不需要处理
    if (value == null) {
      return;
    }
    // 根据@Value注解的value,生成apollo需要关注的配置字符串
    Set<String> keys = placeholderHelper.extractPlaceholderKeys(value.value());

    if (keys.isEmpty()) {
      return;
    }
	// 将每一个关注的配置字符串注册到springValueRegistry对象中
    for (String key : keys) {
      SpringValue springValue = new SpringValue(key, value.value(), bean, beanName, field, false);
      springValueRegistry.register(beanFactory, key, springValue);
      logger.debug("Monitoring {}", springValue);
    }
  }

这里面有个点需要关注下:
为什么placeholderHelper.extractPlaceholderKeys返回的是一个Set,而不是String?@Value不是只能指定一个value么?
@Value支持这样的用法:

@Value("${first.config:${second.config}}")

@Value的默认值机制可以支持变量,而不单单是字面值,当默认值是变量,
需要关注的配置就会是多个,Set刚好启动去重的作用,避免重复设置值。

SpringValueProcessor.proccessMethod

SpringValueProcessor.proccessMethod干的事情如下

  @Override
  protected void processMethod(Object bean, String beanName, Method method) {
    // 获取方法上的@Value注解
    Value value = method.getAnnotation(Value.class);
    // 没有@Value注解的方法直接忽略
    if (value == null) {
      return;
    }
    // 如果方法有@Bean注解,则直接忽略
    if (method.getAnnotation(Bean.class) != null) {
      return;
    }
    // 期望加了@Value注解的方法是setter方法,setter方法应该只有一个参数
    if (method.getParameterTypes().length != 1) {
      logger.error("Ignore @Value setter {}.{}, expecting 1 parameter, actual {} parameters",
          bean.getClass().getName(), method.getName(), method.getParameterTypes().length);
      return;
    }
	// 与processField一样,根据@Value注解的value,得到需要关注的配置
    Set<String> keys = placeholderHelper.extractPlaceholderKeys(value.value());

    if (keys.isEmpty()) {
      return;
    }
	// 遍历,将关注的配置注册到springValueRegistry
    for (String key : keys) {
      SpringValue springValue = new SpringValue(key, value.value(), bean, beanName, method, false);
      springValueRegistry.register(beanFactory, key, springValue);
      logger.info("Monitoring {}", springValue);
    }
  }

代码逻辑基本与processField方法一致~

初步结论

apollo利用BeanFactoryPostProcessor的postProcessBeforeInitialization方法,对spring的bean进行遍历,把bean中加了@Value注解的field和method以SpringValue的形式注册到SpringValueRegistry中。

SpringValue包含了更新值需要的信息,例如@Value加在field上,那么Spring就包含了bean对象,加了注解的Field。@Value加在方法上,则会包含加了注解的Method。

到这,基本可以猜到热更的逻辑了:
1、配置发生变更
2、从SpringValueRegistry中找到需要更新的SpringValue对象
3、利用反射设置SpringValue持有的bean对象的Field或调用method方法

RemoteConfigRepository

RemoteConfigRepository的构造方法如下:

  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);
    // new RemoteConfigRepository对象的时候,就会尝试同步一次配置
    this.trySync();
    // 周期性的拉取配置进行更新
    this.schedulePeriodicRefresh();
    // 看方法名字就知道,定时的长轮询,监听配置的变更
    this.scheduleLongPollingRefresh();
  }

  private void scheduleLongPollingRefresh() {
  	// 就是提交了制定namespace的长轮询任务
    remoteConfigLongPollService.submit(m_namespace, this);
  }

有一点值得留意:
RemoteConfigRepository的构造方法有一个参数,namespace。也就是说,每个namespace的配置更新是独立~

从官方文档,我们了解到,热发布机制是通过长轮询机制实现的,所以相应的代码应该是在RemoteConfigLongPollService中。

RemoteConfigLongPollService

比较重要的代码如下:

public class RemoteConfigLongPollService {
  ......
  ......
  public boolean submit(String namespace, RemoteConfigRepository remoteConfigRepository) {
  	// 通过RemoteConfigRepository的构造函数知道,RemoteConfigLongPollService是单例的,RemoteConfigRepository是每个namespace独立的,所以通过map缓存需要监听的namespace和它对应的RemoteConfigRepository
    boolean added = m_longPollNamespaces.put(namespace, remoteConfigRepository);
    // 设置初始的通知id
    m_notifications.putIfAbsent(namespace, INIT_NOTIFICATION_ID);
    // 第一次提交任务的时候,启动轮询
    if (!m_longPollStarted.get()) {
      startLongPolling();
    }
    return added;
  }

  private void startLongPolling() {
  	// 并发控制
    if (!m_longPollStarted.compareAndSet(false, true)) {
      //already started
      return;
    }
    try {
      // 获取各种配置
      final String appId = m_configUtil.getAppId();
      final String cluster = m_configUtil.getCluster();
      final String dataCenter = m_configUtil.getDataCenter();
      final String secret = m_configUtil.getAccessKeySecret();
      final long longPollingInitialDelayInMills = m_configUtil.getLongPollingInitialDelayInMills();
      m_longPollingService.submit(new Runnable() {
        @Override
        public void run() {
          if (longPollingInitialDelayInMills > 0) {
            try {
              logger.debug("Long polling will start in {} ms.", longPollingInitialDelayInMills);
              // 每隔longPollingInitialDelayInMills毫秒请求一次
              TimeUnit.MILLISECONDS.sleep(longPollingInitialDelayInMills);
            } catch (InterruptedException e) {
              //ignore
            }
          }
          // 真正的长轮询是在该方法
          doLongPollingRefresh(appId, cluster, dataCenter, secret);
        }
      });
    } catch (Throwable ex) {
      m_longPollStarted.set(false);
      ApolloConfigException exception =
          new ApolloConfigException("Schedule long polling refresh failed", ex);
      Tracer.logError(exception);
      logger.warn(ExceptionUtil.getDetailMessage(exception));
    }
  }

 private void doLongPollingRefresh(String appId, String cluster, String dataCenter, String secret) {
    final Random random = new Random();
    ServiceDTO lastServiceDto = null;
    while (!m_longPollingStopped.get() && !Thread.currentThread().isInterrupted()) {
      if (!m_longPollRateLimiter.tryAcquire(5, TimeUnit.SECONDS)) {
        //wait at most 5 seconds
        try {
          TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
        }
      }
      Transaction transaction = Tracer.newTransaction("Apollo.ConfigService", "pollNotification");
      String url = null;
      try {
        if (lastServiceDto == null) {
          // 拿到全部服务地址
          List<ServiceDTO> configServices = getConfigServices();
          // 通过random.nextInt起到负载均衡的作用
          lastServiceDto = configServices.get(random.nextInt(configServices.size()));
        }
		// 构建请求地址
        url =
            assembleLongPollRefreshUrl(lastServiceDto.getHomepageUrl(), appId, cluster, dataCenter,
                m_notifications);

        logger.debug("Long polling from {}", url);

        HttpRequest request = new HttpRequest(url);
        request.setReadTimeout(LONG_POLLING_READ_TIMEOUT);
        if (!StringUtils.isBlank(secret)) {
          Map<String, String> headers = Signature.buildHttpHeaders(url, appId, secret);
          request.setHeaders(headers);
        }

        transaction.addData("Url", url);
		// 发送轮询请求
        final HttpResponse<List<ApolloConfigNotification>> response =
            m_httpUtil.doGet(request, m_responseType);

        logger.debug("Long polling response: {}, url: {}", response.getStatusCode(), url);
        // http状态码是200并且body不空,代表有更新
        if (response.getStatusCode() == 200 && response.getBody() != null) {
        
       	  // 更新m_notifications里的通知id
          updateNotifications(response.getBody());
          // 更新已经存在于m_remoteNotificationMessages缓存中的内容,更新方式为合并,以版本大的为准
          updateRemoteNotifications(response.getBody());
          transaction.addData("Result", response.getBody().toString());
          // 真正的更新动作,是这个方法
          notify(lastServiceDto, response.getBody());
        }

        //状态码是304,随机是否将lastServiceDto设置为null,lastServiceDto设置为null,则会触发新一轮的负载均衡,随机选择新的服务地址
        if (response.getStatusCode() == 304 && random.nextBoolean()) {
          lastServiceDto = null;
        }

        m_longPollFailSchedulePolicyInSecond.success();
        transaction.addData("StatusCode", response.getStatusCode());
        transaction.setStatus(Transaction.SUCCESS);
      } catch (Throwable ex) {
        lastServiceDto = null;
        Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(ex));
        transaction.setStatus(ex);
        long sleepTimeInSecond = m_longPollFailSchedulePolicyInSecond.fail();
        logger.warn(
            "Long polling failed, will retry in {} seconds. appId: {}, cluster: {}, namespaces: {}, long polling url: {}, reason: {}",
            sleepTimeInSecond, appId, cluster, assembleNamespaces(), url, ExceptionUtil.getDetailMessage(ex));
        try {
          TimeUnit.SECONDS.sleep(sleepTimeInSecond);
        } catch (InterruptedException ie) {
          //ignore
        }
      } finally {
        transaction.complete();
      }
    }
  }

 private void notify(ServiceDTO lastServiceDto, List<ApolloConfigNotification> notifications) {
    if (notifications == null || notifications.isEmpty()) {
      return;
    }
    for (ApolloConfigNotification notification : notifications) {
      String namespaceName = notification.getNamespaceName();
      //create a new list to avoid ConcurrentModificationException
	  // 从m_longPollNamespaces拿到namespace对应的RemoteConfigRepository,并包装成一个List。官方备注是避免ConcurrentModificationException异常,但是没太懂,怎么用,会抛这个异常
      List<RemoteConfigRepository> toBeNotified =
          Lists.newArrayList(m_longPollNamespaces.get(namespaceName));
      // 在doLongPollingRefresh方法中调用updateRemoteNotifications方法,把ApolloNotificatiobMessages设置到了m_remoteNotificationMessages中,现在取出来
      ApolloNotificationMessages originalMessages = m_remoteNotificationMessages.get(namespaceName);
      ApolloNotificationMessages remoteMessages = originalMessages == null ? null : originalMessages.clone();
      //since .properties are filtered out by default, so we need to check if there is any listener for it
      // updateRemoteNotifications和这里都是这样,对namespace对额外的检查,例如namespace是zidongxiangxi,那么还会检查zidongxiangxi.properties。这机制,可能得再了解下才行
      toBeNotified.addAll(m_longPollNamespaces
          .get(String.format("%s.%s", namespaceName, ConfigFileFormat.Properties.getValue())));
      for (RemoteConfigRepository remoteConfigRepository : toBeNotified) {
        try {
          // 最终是调用RemoteConfigRepository的onLongPollNotified方法来执行更新
          remoteConfigRepository.onLongPollNotified(lastServiceDto, remoteMessages);
        } catch (Throwable ex) {
          Tracer.logError(ex);
        }
      }
    }
  }
  ......
  ......
}

RemoteConfigLongPollService只是负责监听配置是否更新,配置的更新最终还是由RemoteConfigRepository完成。
RemoteConfigRepository.onLongPollNotified方法代码很简单,如下:

  public void onLongPollNotified(ServiceDTO longPollNotifiedServiceDto, ApolloNotificationMessages remoteMessages) {
    m_longPollServiceDto.set(longPollNotifiedServiceDto);
    m_remoteMessages.set(remoteMessages);
    m_executorService.submit(new Runnable() {
      @Override
      public void run() {
        m_configNeedForceRefresh.set(true);
        trySync();
      }
    });
  }

其实就是设置了强制刷新标识,然后执行一次尝试配置同步。trySync方法底层调用的是sync方法,只是做了一些异常处理。
RemoteConfigRepository.sync代码如下:

@Override
  protected synchronized void sync() {
    Transaction transaction = Tracer.newTransaction("Apollo.ConfigService", "syncRemoteConfig");

    try {
      ApolloConfig previous = m_configCache.get();
      ApolloConfig current = loadApolloConfig();

      // 上一次版本号和当前版本号不相等才需要更新
      if (previous != current) {
        logger.debug("Remote Config refreshed!");
        // 更新版本号
        m_configCache.set(current);
        // 触发更新
        this.fireRepositoryChange(m_namespace, this.getConfig());
      }

      if (current != null) {
        Tracer.logEvent(String.format("Apollo.Client.Configs.%s", current.getNamespaceName()),
            current.getReleaseKey());
      }

      transaction.setStatus(Transaction.SUCCESS);
    } catch (Throwable ex) {
      transaction.setStatus(ex);
      throw ex;
    } finally {
      transaction.complete();
    }
  }

fireRepositoryChange是RemoteConfigRepository的父类AbstractConfigRepository的方法:

protected void fireRepositoryChange(String namespace, Properties newProperties) {
    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);
      }
    }
  }

配置更新,就是遍历所有RepositoryChangeListener,调用这些RepositoryChangeListener的onRepositoryChange方法。

DefaultConfig

DefaultConfig实现了RepositoryChangeListener接口,在initialize方法中,把把自己注册到ConfigRepository的时间监听里。

public class DefaultConfig extends AbstractConfig implements RepositoryChangeListener {
  ......
   public DefaultConfig(String namespace, ConfigRepository configRepository) {
    m_namespace = namespace;
    m_resourceProperties = loadFromResource(m_namespace);
    m_configRepository = configRepository;
    m_configProperties = new AtomicReference<>();
    m_warnLogRateLimiter = RateLimiter.create(0.017); // 1 warning log output per minute
    initialize();
  }

  private void initialize() {
    try {
      updateConfig(m_configRepository.getConfig(), m_configRepository.getSourceType());
    } catch (Throwable ex) {
      Tracer.logError(ex);
      logger.warn("Init Apollo Local Config failed - namespace: {}, reason: {}.",
          m_namespace, ExceptionUtil.getDetailMessage(ex));
    } finally {
      //就是这里了!!!把自己注册到ConfigRepository的事件监听里
      m_configRepository.addChangeListener(this);
    }
  }

  // 实现RepositoryChangeListener接口的onRepositoryChange方法
  @Override
  public synchronized void onRepositoryChange(String namespace, Properties newProperties) {
    // 新老配置相同,不处理
    if (newProperties.equals(m_configProperties.get())) {
      return;
    }

    ConfigSourceType sourceType = m_configRepository.getSourceType();
    Properties newConfigProperties = propertiesFactory.getPropertiesInstance();
    newConfigProperties.putAll(newProperties);
	// 计算新老配置中的不同
    Map<String, ConfigChange> actualChanges = updateAndCalcConfigChanges(newConfigProperties, sourceType);

    //没有不同,不处理
    if (actualChanges.isEmpty()) {
      return;
    }
	// fireConfigChange触发配置更新
    this.fireConfigChange(new ConfigChangeEvent(m_namespace, actualChanges));

    Tracer.logEvent("Apollo.Client.ConfigChanges", m_namespace);
  }

  protected void fireConfigChange(final ConfigChangeEvent changeEvent) {
    // 最终是调用ConfigChangeListener来更新配置。。。。  		
    for (final ConfigChangeListener listener : m_listeners) {
      // 不感兴趣的事件不处理
      if (!isConfigChangeListenerInterested(listener, changeEvent)) {
        continue;
      }
      m_executorService.submit(new Runnable() {
        @Override
        public void run() {
          String listenerName = listener.getClass().getName();
          Transaction transaction = Tracer.newTransaction("Apollo.ConfigChangeListener", listenerName);
          try {
            listener.onChange(changeEvent);
            transaction.setStatus(Transaction.SUCCESS);
          } catch (Throwable ex) {
            transaction.setStatus(ex);
            Tracer.logError(ex);
            logger.error("Failed to invoke config change listener {}", listenerName, ex);
          } finally {
            transaction.complete();
          }
        }
      });
    }
  }
  ......
}

嵌套的有点深,DefaultConfig自己并不直接执行配置的更新,而是触发注册到它们这的ConfigChangeListener监听。。。。

AutoUpdateConfigChangeListener

在apollo中,自动更新是AutoUpdateConfigChangeListener来实现的。

public class AutoUpdateConfigChangeListener implements ConfigChangeListener{

  ......
  
  @Override
  public void onChange(ConfigChangeEvent changeEvent) {
    Set<String> keys = changeEvent.changedKeys();
    if (CollectionUtils.isEmpty(keys)) {
      return;
    }
    for (String key : keys) {
      // 根据跟新配置的key,查找对应的SpringValue集合
      Collection<SpringValue> targetValues = springValueRegistry.get(beanFactory, key);
      if (targetValues == null || targetValues.isEmpty()) {
        continue;
      }

      // 遍历更新配置
      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);
    }
  }
  
  ......
  
}

到这里,可以确定,跟一开始得出的初步结论一致~

SpringValue

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

  private void injectField(Object newVal) throws IllegalAccessException {
    Object bean = beanRef.get();
    if (bean == null) {
      return;
    }
    boolean accessible = field.isAccessible();
    field.setAccessible(true);
    field.set(bean, newVal);
    field.setAccessible(accessible);
  }

  private void injectMethod(Object newVal)
      throws InvocationTargetException, IllegalAccessException {
    Object bean = beanRef.get();
    if (bean == null) {
      return;
    }
    methodParameter.getMethod().invoke(bean, newVal);
  }
  ......
}

SpringValue方法的代码也很简单:

  • 是加在@Value属性的SpringValue,就利用反射调用Field设置值
  • 是加在setter方法上的SpringValue,就用反射调用对应的方法
  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值