序文
通常、プロジェクトでは、一元的な構成管理のために 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();
}
复制代码
動的リフレッシュの構成完了フローチャート
上の図を参照すると、動的リフレッシュの構成の基本原理が理解できます。