Во-первых, давайте поймем роль аннотации @RefreshScope. В Spring Cloud @RefreshScope — это специальная аннотация области, которая используется для обозначения bean-компонентов, которые необходимо динамически обновлять. Когда Bean аннотируется с помощью @RefreshScope, контейнер Spring создает для Bean специальную область, называемую областью обновления. Это означает, что при изменении конфигурации контейнер Spring может заново создать экземпляр компонента и использовать новую конфигурацию.
@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;
}
Аннотация @Scope используется для указания области действия bean-компонентов, управляемых контейнером Spring IoC. Область действия определяет жизненный цикл, время создания и метод хранения компонента. Общие области действия Spring включают синглтон, прототип, запрос, сеанс и т. д.
В Spring Cloud процесс @RefreshScope для достижения динамического обновления можно резюмировать в виде следующих шагов:
Подводя итог, для достижения динамического обновления мы в основном достигаем следующих двух основных целей:
Поскольку класс аннотирован с помощью @RefreshScope, область действия в информации BeanDefinition является «обновленной», и логика будет обрабатываться отдельно при полученииBean.
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);
}
}
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 будут созданы и кэшированы для последующего использования.
После изменения центра конфигурации будет получено событие RefreshEvent, и прослушиватель RefreshEventListner будет прослушивать это событие.
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-компоненты.
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();
}
В целом, этот процесс гарантирует, что при изменении конфигурации приложение может динамически обновлять свою среду и компоненты с пометкой @RefreshScope без перезапуска всего приложения. Это мощная функция Spring Cloud, которая позволяет приложениям микросервисов динамически реагировать на изменения конфигурации во время выполнения.
Объединив аннотацию @RefreshScope, реализацию RefreshScope и GenericScope и управление жизненным циклом компонента в контейнере Spring, Spring Cloud может добиться динамического обновления конфигурации. Это позволяет приложениям микросервисов реагировать на изменения внешней конфигурации без перезапуска всего приложения, тем самым повышая гибкость и скорость реагирования системы.
Важно отметить, что хотя динамическое обновление конфигураций является очень полезной функцией, оно также имеет некоторые ограничения и предостережения. Например, не все bean-компоненты подходят для маркировки @RefreshScope, поскольку повторное создание экземпляра bean-компонента может привести к потере некоторого состояния. Кроме того, частые изменения и обновления конфигурации могут повлиять на производительность и стабильность системы. Поэтому при использовании динамически обновляемых конфигураций необходимо взвесить все «за» и «против» и тщательно выбирать компоненты и конфигурации, которые необходимо обновить.
Надеюсь, эта статья поможет вам лучше понять Принцип. динамического обновления, реализованный с помощью @RefreshScope в Spring Cloud., и правильно применять эту возможность в реальных проектах.
Навыки обновляются благодаря обмену ими, и каждый раз, когда я получаю новые знания, мое сердце переполняется радостью. Искренне приглашаем вас подписаться на публичный аккаунт 『
код тридцать пять
』 , для получения дополнительной технической информации.