@RefreshScope 動的更新メカニズム

序文

通常、プロジェクトでは、一元的な構成管理のために Nacos を統合し、動的リフレッシュをサポートする構成になっています。プロジェクト内では @RefreshScope アノテーションが使用されます。ここでは、動的リフレッシュを実現するための @RefreshScope の原理を見てみましょう。

@スコープアノテーション

@RefreshScope は @Scope アノテーションのおかげで動的リフレッシュを実現できます。@Scope は Bean のスコープを表します。プロパティを見てみましょう。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {

	/**
	 * Alias for {@link #scopeName}.
	 * @see #scopeName
	 */
	@AliasFor("scopeName")
	String value() default "";

	/**
	 *  singleton  表示该bean是单例的。(默认)
     *  prototype    表示该bean是多例的,即每次使用该bean时都会新建一个对象。
     *  request        在一次http请求中,一个bean对应一个实例。
     *  session        在一个httpSession中,一个bean对应一个实例
	 */
	@AliasFor("value")
	String scopeName() default "";

	/**
    *   DEFAULT			不使用代理。(默认)
	* 	NO				不使用代理,等价于DEFAULT。
	* 	INTERFACES		使用基于接口的代理(jdk dynamic proxy)。
	* 	TARGET_CLASS	使用基于类的代理(cglib)。
    */
	ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;

}

コードを通して、2 つの主要な属性 value と proxyMode が明確にわかります。値については多くは言いませんが、注釈を読み取るためによく使用できます。proxyMode は @RefreshScope 実装の本質です。ScopedProxyMode.TARGET_CLASS 属性に注意する必要があります。ScopedProxyMode が TARGET_CLASS の場合、現在作成されている Bean に対してプロキシ オブジェクトが生成され、そのプロキシ オブジェクトを介してアクセスされ、そのたびに新しいオブジェクトが作成されます。アクセスされました。

@RefreshScope ソースコード

まず @RefreshScope アノテーションを見てください。

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Scope("refresh")
@Documented
public @interface RefreshScope {
	/**
	 * @see Scope#proxyMode()
	 */
	ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;

}

 @Scope を使用しており、その内部プロパティのデフォルトが ScopedProxyMode.TARGET_CLASS であることがわかります。Spring Scope を通じて実装されていることは簡単にわかるので、Scope インターフェイスを見てみましょう。

public interface Scope {

	/**
	 * Return the object with the given name from the underlying scope,
	 * {@link org.springframework.beans.factory.ObjectFactory#getObject() creating it}
	 * if not found in the underlying storage mechanism.
	 * <p>This is the central operation of a Scope, and the only operation
	 * that is absolutely required.
	 * @param name the name of the object to retrieve
	 * @param objectFactory the {@link ObjectFactory} to use to create the scoped
	 * object if it is not present in the underlying storage mechanism
	 * @return the desired object (never {@code null})
	 * @throws IllegalStateException if the underlying scope is not currently active
	 */
	Object get(String name, ObjectFactory<?> objectFactory);

 
	@Nullable
	Object remove(String name);

 
	void registerDestructionCallback(String name, Runnable callback);

 
	@Nullable
	Object resolveContextualObject(String key);

	 
	@Nullable
	String getConversationId();

}

インターフェイスを見てください。Object get(String name, ObjectFactory<?> objectFactory); だけを見てください。このメソッドは、新しい Bean の作成に役立ちます。つまり、@RefreshScope は、refresh New を呼び出すときにこのメソッドを使用して作成します。これにより、スプリング アセンブリ メカニズムを通じてプロパティを再注入でき、いわゆる動的リフレッシュが実現されます。

Class にはアノテーションが付けられているため@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 {
 
				// 如果scope是单例的情况, 这里不进行分析
				if (mbd.isSingleton()) {
				 .....
				}
                // 如果scope是prototype的情况, 这里不进行分析
				else if (mbd.isPrototype()) {
					......
				}
                // 如果scope是其他的情况,本例中是reresh
				else {
					String scopeName = mbd.getScope();
					if (!StringUtils.hasLength(scopeName)) {
						throw new IllegalStateException("No scope name defined for bean '" + beanName + "'");
					}
                    // 获取refresh scope的实现类RefreshScope,这个类在哪里注入,我们后面讲
					Scope scope = this.scopes.get(scopeName);
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
					}
					try {
                        // 这边是获取bean,调用的是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);
	}
    
}
复制代码

RefreshScope は GenericScope クラスを継承し、最後に GenericScope の get メソッドを呼び出します。

public class GenericScope
		implements Scope, BeanFactoryPostProcessor, BeanDefinitionRegistryPostProcessor, DisposableBean {
                 @Override
	
  public Object get(String name, ObjectFactory<?> objectFactory) {
		// 将bean添加到缓存cache中
        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() {
            // 如果bean为空,则创建bean
			if (this.bean == null) {
				synchronized (this.name) {
					if (this.bean == null) {
						this.bean = this.objectFactory.getObject();
					}
				}
			}
            // 否则返回之前创建好的bean
			return this.bean;
		}
            }
        }
复制代码

ここのコードからも分かるように、作成したBeanはスコープのキャッシュにキャッシュされ、まずキャッシュから取得することになりますが、キャッシュがnullの場合は再度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());
            // 会调用refresh方法,进行刷新
			Set<String> keys = this.refresh.refresh();
			log.info("Refresh keys changed: " + keys);
		}
	}
 
}
 
// 这个是ContextRefresher类中的刷新方法
public synchronized Set<String> refresh() {
        // 刷新spring的envirionment 变量配置
		Set<String> keys = refreshEnvironment();
        // 刷新其他scope
		this.scope.refreshAll();
		return keys;
	}
复制代码

refresh メソッドは最後に destroy メソッドを呼び出して、以前にキャッシュされた 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() {
		// 调用父类的destroy
        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();
	}
复制代码

動的リフレッシュの構成完了フローチャート

 上の図を参照すると、動的リフレッシュの構成の基本原理が理解できます。

 

おすすめ

転載: blog.csdn.net/qq_28165595/article/details/130594915
おすすめ