Принцип динамического обновления, реализованный с помощью @RefreshScope в Spring Cloud.
Принцип динамического обновления, реализованный с помощью @RefreshScope в Spring Cloud.

1. Роль аннотации @RefreshScope

Во-первых, давайте поймем роль аннотации @RefreshScope. В Spring Cloud @RefreshScope — это специальная аннотация области, которая используется для обозначения bean-компонентов, которые необходимо динамически обновлять. Когда Bean аннотируется с помощью @RefreshScope, контейнер Spring создает для Bean специальную область, называемую областью обновления. Это означает, что при изменении конфигурации контейнер Spring может заново создать экземпляр компонента и использовать новую конфигурацию.

Язык кода:javascript
копировать
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Scope("refresh")
@Documented
public @interface RefreshScope {

	/**
	 * @see Scope#proxyMode()
	 * @return proxy mode
	 */
	ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;

}
  • Выше приведен исходный код RefreshScope.,Аннотация используется @Scopeannotation.,@Scopeиспользуется для сравненияSpring Область применения бина.
  • Атрибут proxyMode аннотации по умолчанию использует TARGET_CLASS в качестве прокси.
  • @RefreshScope — это, по сути, специальная область, которая реализует логику обновления конфигурации путем наследования класса GenericScope и переопределения связанных методов.
  • Функция @Scopeаннотация — указать RefreshScope в качестве нового типа области. Конкретно,@Scopeannotation используется внутри @RefreshScopeannotation.,и установите для атрибута значения значение «обновить»,Это означает, что новое доменное имя области определяется как обновление. Когда контейнер Spring встречает @RefreshScopeаннотация,Он будет знать, что это особая сфера,Создание, кэширование и уничтожение Beans должно осуществляться в соответствии с логикой RefreshScope.

Аннотация @Scope используется для указания области действия bean-компонентов, управляемых контейнером Spring IoC. Область действия определяет жизненный цикл, время создания и метод хранения компонента. Общие области действия Spring включают синглтон, прототип, запрос, сеанс и т. д.

2. @RefreshScope реализует процесс динамического обновления.

В Spring Cloud процесс @RefreshScope для достижения динамического обновления можно резюмировать в виде следующих шагов:

  1. Определите аннотацию @RefreshScope: Разработчики используют аннотацию @RefreshScope для компонентов, которые необходимо динамически обновлять. Эта аннотация использовалась @Scopeannotation для внутренних целей.,и установите для него значение «обновить»,Новое доменное имя области определяется как обновление.
  2. Анализ контейнера Spring @RefreshScope: Когда запускается контейнер Spring,Он будет анализировать все определения bean-компонентов,и встретил @RefreshScopeаннотация,Контейнер Spring будет знать, что это особая область действия. Он использует класс RefreshScope (унаследованный от GenericScope) для управления жизненным циклом этих bean-компонентов.
  3. Создание и кэширование bean-компонентов: Когда приложение впервые запрашивает компонент, отмеченный @RefreshScope, контейнер Spring вызывает метод get RefreshScope для создания экземпляра компонента. После создания этот экземпляр Bean будет кэширован в RefreshScope для быстрого последующего извлечения.
  4. Изменения конфигурации: Во время выполнения, если конфигурация изменяется во внешнем источнике конфигурации (например, через Spring Cloud Config Сервер), клиентские приложения должны быть уведомлены об этих изменениях.
  5. Триггерное событие обновления: Клиентские приложения могут инициировать события обновления различными способами, например, через Spring. Cloud Шина передает сообщения об изменении конфигурации или напрямую вызывает конечную точку /actuator/refresh.
  6. Обновите локальный объект Environment: До или после запуска события обновления локальный объект среды необходимо обновить, чтобы отразить последнюю конфигурацию из внешнего источника конфигурации. Обычно это делается с помощью класса реализации среды (например, StandardEnvironment или MutablePropertySources). Метод обновления может заключаться в добавлении, изменении или удалении PropertySource или непосредственном управлении списком источников свойств в MutablePropertySources.
  7. Обновить bean-компоненты в области видимости: Когда объект Environment обновляется, RefreshScope просматривает все bean-компоненты в своем кеше, уничтожает и воссоздает их. Это достигается путем вызова методов управления жизненным циклом, предоставляемых GenericScope. Старый экземпляр Bean уничтожается, а новый экземпляр Bean создается и кэшируется на основе последней конфигурации (полученной из обновленной среды).
  8. Примените новую конфигурацию: После операции обновления,Компоненты в приложении будут использовать новую конфигурацию. Поскольку @RefreshScope влияет только на компоненты, отмеченные этой аннотацией.,Поэтому немаркированные бобы не затрагиваются.

Подводя итог, для достижения динамического обновления мы в основном достигаем следующих двух основных целей:

  • Позвольте контейнеру Spring перезагрузить переменные конфигурации среды.
  • Пусть Spring Bean воссоздает поколение

3. Интерпретация исходного кода

1. Просмотрите стек через точки останова и получите компонент, аннотированный RefreshScope.

Поскольку класс аннотирован с помощью @RefreshScope, область действия в информации BeanDefinition является «обновленной», и логика будет обрабатываться отдельно при полученииBean.

Язык кода:javascript
копировать
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {

protected <T> T doGetBean(
   String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
   throws BeansException {

    // Если область действия является одноэлементной, Здесь не проводится анализ
    if (mbd.isSingleton()) {
     .....
    }
                // Если областью действия является прототип, Здесь не проводится анализ
    else if (mbd.isPrototype()) {
     ......
    }
                // Если область действия другая, в этом случае она обновляется.
    else {
     String scopeName = mbd.getScope();
     if (!StringUtils.hasLength(scopeName)) {
      throw new IllegalStateException("No scope name defined for bean '" + beanName + "'");
     }
                    // Обновить Класс реализации области RefreshScope, куда внедряется этот класс? Мы поговорим об этом позже.
     Scope scope = this.scopes.get(scopeName);
     if (scope == null) {
      throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
     }
     try {
                        // Вот полученный компонент, вызывающий метод в RefreshScope
      Object scopedInstance = scope.get(beanName, () -> {
       beforePrototypeCreation(beanName);
       try {
        return createBean(beanName, mbd, args);
       }
       finally {
        afterPrototypeCreation(beanName);
       }
      });
      beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
     }
     catch (IllegalStateException ex) {
      throw new ScopeNotActiveException(beanName, scopeName, ex);
     }
    }
   }
   catch (BeansException ex) {
    beanCreation.tag("exception", ex.getClass().toString());
    beanCreation.tag("message", String.valueOf(ex.getMessage()));
    cleanupAfterBeanCreationFailure(beanName);
    throw ex;
   }
   finally {
    beanCreation.end();
   }
  }

  return adaptBeanInstance(name, beanInstance, requiredType);
 }
    
}
2. RefreshScope наследует GenericScope, и в конечном итоге вызывается метод get GenericScope.
Язык кода:javascript
копировать
public class GenericScope
  implements Scope, BeanFactoryPostProcessor, BeanDefinitionRegistryPostProcessor, DisposableBean {
                 @Override
 
  public Object get(String name, ObjectFactory<?> objectFactory) {
  // Добавить bean-компоненты в кеш
        BeanLifecycleWrapper value = this.cache.put(name, new BeanLifecycleWrapper(name, objectFactory));
  this.locks.putIfAbsent(name, new ReentrantReadWriteLock());
  try {
            // Вызовите метод getBean ниже
   return value.getBean();
  }
  catch (RuntimeException e) {
   this.errors.put(name, e);
   throw e;
  }
 }       

private static class BeanLifecycleWrapper {
        
  public Object getBean() {
            // Если бин пуст, создайте бин
   if (this.bean == null) {
    synchronized (this.name) {
     if (this.bean == null) {
      this.bean = this.objectFactory.getObject();
     }
    }
   }
            // В противном случае верните ранее созданный компонент.
   return this.bean;
  }
            }
        }

Вышеупомянутое утверждение можно подтвердить из исходного кода среды Spring. Для bean-компонентов, использующих @RefreshScope (или другие пользовательские области действия), контейнер Spring кэширует экземпляр bean-компонента в кеш соответствующей области после его создания. При последующем запросе того же компонента Spring сначала попытается получить экземпляр компонента из этого кэша.

Если кэш равен нулю, это означает, что компонент не был создан или уничтожен. В это время Spring снова выполнит процесс создания компонента, включая анализ определения компонента, выполнение внедрения зависимостей и другие шаги, а также выполнение других шагов. наконец, снова кэширует вновь созданный экземпляр Bean для функции в домене.

Этот механизм кэширования гарантирует, что для запросов с одинаковой областью действия и одним и тем же определением компонента Spring может быстро предоставить экземпляры компонента без необходимости каждый раз воссоздавать их. В то же время для специальных областей, таких как @RefreshScope, это также позволяет экземплярам Bean динамически обновляться во время выполнения для адаптации к изменениям конфигурации. В процессе обновления старые экземпляры Bean в кеше будут уничтожены, а новые экземпляры Bean будут созданы и кэшированы для последующего использования.

3. Обновите кэш компонентов после обновления центра конфигурации.

После изменения центра конфигурации будет получено событие RefreshEvent, и прослушиватель RefreshEventListner будет прослушивать это событие.

Язык кода:javascript
копировать
public class RefreshEventListener implements SmartApplicationListener {

 
........

 public void handle(RefreshEvent event) {
  if (this.ready.get()) { // don't handle events before app is ready
   log.debug("Event received " + event.getEventDesc());
            // Метод обновления будет вызван для обновления
   Set<String> keys = this.refresh.refresh();
   log.info("Refresh keys changed: " + keys);
  }
 }

}

// Это метод обновления в классе ContextRefresher.
public synchronized Set<String> refresh() {
        // Освежить весеннюю обстановку Переменная конфигурация
  Set<String> keys = refreshEnvironment();
        // Обновить другие области
  this.scope.refreshAll();
  return keys;
 }

Метод обновления, наконец, вызывает метод уничтожения, чтобы очистить ранее кэшированные bean-компоненты.

Язык кода:javascript
копировать
public class RefreshScope extends GenericScope
  implements ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, Ordered {

 @ManagedOperation(description = "Dispose of the current instance of all beans "
   + "in this scope and force a refresh on next method execution.")
 public void refreshAll() {
  // Вызов уничтожения родительского класса
        super.destroy();
  this.context.publishEvent(new RefreshScopeRefreshedEvent());
 }
}


@Override
 public void destroy() {
  List<Throwable> errors = new ArrayList<Throwable>();
  Collection<BeanLifecycleWrapper> wrappers = this.cache.clear();
  for (BeanLifecycleWrapper wrapper : wrappers) {
   try {
    Lock lock = this.locks.get(wrapper.getName()).writeLock();
    lock.lock();
    try {
                    // Главное здесь — установить для предыдущего bean-компонента значение null. Вам еще раз придется пройти процесс createBean.
     wrapper.destroy();
    }
    finally {
     lock.unlock();
    }
   }
   catch (RuntimeException e) {
    errors.add(e);
   }
  }
  if (!errors.isEmpty()) {
   throw wrapIfNecessary(errors.get(0));
  }
  this.errors.clear();
 }
  • Когда изменения конфигурации происходят в центре конфигурации, эти изменения обычно распространяются через механизм мониторинга Spring Cloud Bus или Spring Cloud Config. Как только клиентское приложение обнаруживает эти изменения, оно запускает событие обновления, обычно RefreshEvent.
  • Класс RefreshEventListener реализует интерфейс SmartApplicationListener и используется для прослушивания события RefreshEvent. При срабатывании RefreshEvent будет вызываться метод handle для выполнения логики обновления.
  • В логике обновления сначала обновляется объект среды Spring, что обычно включает перезагрузку свойств конфигурации. Затем вызывается метод restartAll RefreshScope для обновления всех bean-компонентов, отмеченных @RefreshScope.
  • RefreshScope — это расширение GenericScope, которое предоставляет дополнительную логику для обработки обновления конфигурации. В методе replaceAll все экземпляры Bean-компонентов, кэшированные в текущей области, уничтожаются путем вызова метода уничтожения родительского класса. Этот процесс уничтожения установит для экземпляра Bean в кеше значение null и освободит связанные ресурсы. Как только экземпляр компонента будет уничтожен, контейнер Spring воссоздаст его заново при следующем запросе компонента.
  • После завершения уничтожения RefreshScope опубликует событие RefreshScopeRefreshedEvent, чтобы уведомить других прослушивателей о завершении операции обновления.

В целом, этот процесс гарантирует, что при изменении конфигурации приложение может динамически обновлять свою среду и компоненты с пометкой @RefreshScope без перезапуска всего приложения. Это мощная функция Spring Cloud, которая позволяет приложениям микросервисов динамически реагировать на изменения конфигурации во время выполнения.

4. Резюме

Объединив аннотацию @RefreshScope, реализацию RefreshScope и GenericScope и управление жизненным циклом компонента в контейнере Spring, Spring Cloud может добиться динамического обновления конфигурации. Это позволяет приложениям микросервисов реагировать на изменения внешней конфигурации без перезапуска всего приложения, тем самым повышая гибкость и скорость реагирования системы.

Важно отметить, что хотя динамическое обновление конфигураций является очень полезной функцией, оно также имеет некоторые ограничения и предостережения. Например, не все bean-компоненты подходят для маркировки @RefreshScope, поскольку повторное создание экземпляра bean-компонента может привести к потере некоторого состояния. Кроме того, частые изменения и обновления конфигурации могут повлиять на производительность и стабильность системы. Поэтому при использовании динамически обновляемых конфигураций необходимо взвесить все «за» и «против» и тщательно выбирать компоненты и конфигурации, которые необходимо обновить.

Надеюсь, эта статья поможет вам лучше понять Принцип. динамического обновления, реализованный с помощью @RefreshScope в Spring Cloud., и правильно применять эту возможность в реальных проектах.


Навыки обновляются благодаря обмену ими, и каждый раз, когда я получаю новые знания, мое сердце переполняется радостью. Искренне приглашаем вас подписаться на публичный аккаунт код тридцать пять , для получения дополнительной технической информации.

boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose