Springの@RefreshScope秘匿(一)

33066 ワード

Spring BenFactory@RefshScope注釈を含むbeanに対してロードする
Spring BenFactoryはBenに対する負荷は大きく3種類に分けられています.第一は単一の例bean、ScopeはSingleton、第二は多例bean、ScopeはProttype、そして第三種類で、上記の2種類を除いて、Scapeはrefreshのbeanであり、@Refreshopeによって注釈されています.BenFactory Scopeに対するrefreshのbeanのローディングコードは以下の通りです.
	String scopeName = mbd.getScope();
	final Scope scope = this.scopes.get(scopeName);
	if (scope == null) {
		throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
	}
	try {
		Object scopedInstance = scope.get(beanName, () -> {
			beforePrototypeCreation(beanName);
			try {
				return createBean(beanName, mbd, args);
			}
			finally {
				afterPrototypeCreation(beanName);
			}
		});
		bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
	}
	catch (IllegalStateException ex) {
		throw new BeanCreationException(beanName,
				"Scope '" + scopeName + "' is not active for the current thread; consider " +
				"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
				ex);
	}
分析は以下の通りです
  • .まずBenDefinitionからscopeNameを取得し、その後Scopeのインスタンスリストから該当するインスタンスを取得し、@RefreshScopeに対して、scopeNameはrefshであり、ScopeはRefshScopeタイプの例
  • である.
  • .続いてScopeのget(String name,Object Factory)方法によってインスタンスを取得し、取得できない場合は転送されたObject Factory工場オブジェクトを通じて一例を作成する(作成の具体的なプロセスはここでは説明しない)
  • .
    RefshScope類の詳細
    RefreshScopeは、GeneraicScopeのget(String name、Object Factory>Object Factory)方法を継承しています.Generic Scopeではbeanのために簡単なキャッシュをします.以下の通りです.
    	@Override
    	public Object get(String name, ObjectFactory<?> objectFactory) {
    		BeanLifecycleWrapper value = this.cache.put(name,
    				new BeanLifecycleWrapper(name, objectFactory));
    		this.locks.putIfAbsent(name, new ReentrantReadWriteLock());
    		try {
    			return value.getBean();
    		}
    		catch (RuntimeException e) {
    			this.errors.put(name, e);
    			throw e;
    		}
    	}
    
    分析は以下の通りです
  • .beanライフサイクルを管理するBenLifecycleWrapperの例を初期化し、このbeanNameのキャッシュが存在しない場合、このBenLifecycleWrapperのインスタンスをBenLifecycleWrapperキャッシュに入れる(ここにはいくつかの欠陥がありますが、毎回BenLifecycleWrapperを初期化する必要はありません.
  • .このbeanNameに対応する読み書きロックがなければ、Rentrant ReadWriteLockをlocksというmapセットに初期化します.
  • .Bean
  • は、BenLifecycleWrapperのget Bean()方式で取得される.
  • .異常があったら、まず異常を統計してから、
  • に投げ直します.
    BenLifecycleWrapperのget Bean()
    	public Object getBean() {
    		if (this.bean == null) {
    			synchronized (this.name) {
    				if (this.bean == null) {
    					this.bean = this.objectFactory.getObject();
    				}
    			}
    		}
    		return this.bean;
    	}
    
    実は対象工場Object Factoryを通じて新たなbeanを作成しました.(ここのダブルチェックロックは問題があります.beanフィールドはvolatileを使っていません.マルチスレッドを作成すると、あるスレッドができても、他のスレッドは感知されません.)
    このことから、BenLifecycleWrapper CacheにおいてbeanNameのキャッシュに関するBenLifecycleWrapperのインスタンスが変わらないなら、ずっと取得しているのは上記のプロセスで作成されたbeanであることが分かります.
    RefreshScopeのrefreshとrefreshAll
    	@ManagedOperation(description = "Dispose of the current instance of bean name "
    			+ "provided and force a refresh on next method execution.")
    	public boolean refresh(String name) {
    		if (!name.startsWith(SCOPED_TARGET_PREFIX)) {
    			// User wants to refresh the bean with this name but that isn't the one in the
    			// cache...
    			name = SCOPED_TARGET_PREFIX + name;
    		}
    		// Ensure lifecycle is finished if bean was disposable
    		if (super.destroy(name)) {
    			this.context.publishEvent(new RefreshScopeRefreshedEvent(name));
    			return true;
    		}
    		return false;
    	}
    
    	@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());
    	}
    
    分析は以下の通りです
  • 1.refreshは特定のbeanに対して、成功的に廃棄した後、Refresh Scope RefreshedEventイベント
  • を送ります.
  • .refreshAllは、すべてのScropeをrefreshのbeanとして廃棄し、廃棄が完了したらRefresh Scope RefreshedEventイベント
  • を転送します.
    refresh beanの廃棄
    単一廃棄
    	protected boolean destroy(String name) {
    		BeanLifecycleWrapper wrapper = this.cache.remove(name);
    		if (wrapper != null) {
    			Lock lock = this.locks.get(wrapper.getName()).writeLock();
    			lock.lock();
    			try {
    				wrapper.destroy();
    			}
    			finally {
    				lock.unlock();
    			}
    			this.errors.remove(name);
    			return true;
    		}
    		return false;
    	}
    
    分析は以下の通りです
  • 1.BenLifecycleWrapperCacheからbeanに設定されたキャッシュ
  • を除去する.
  • .このbeanのキャッシュがあれば、まずリード・書き込み・ロックを取得し、その後ロックをかけてBenLifecycleWrapperのdestrooy()方法を実行し、完了したらロックを解除し、エラーリストerrors内の当該beanに対応するエラー情報
  • を除去する.
    全部廃棄する
    	@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 {
    					wrapper.destroy();
    				}
    				finally {
    					lock.unlock();
    				}
    			}
    			catch (RuntimeException e) {
    				errors.add(e);
    			}
    		}
    		if (!errors.isEmpty()) {
    			throw wrapIfNecessary(errors.get(0));
    		}
    		this.errors.clear();
    	}
    
    単独廃棄はほぼ同じ過程であり,詳しく述べるまでもない.
    BenLifecycleWrapperのdestroy()
    	public void destroy() {
    		if (this.callback == null) {
    			return;
    		}
    		synchronized (this.name) {
    			Runnable callback = this.callback;
    			if (callback != null) {
    				callback.run();
    			}
    			this.callback = null;
    			this.bean = null;
    		}
    	}
    
    廃棄方法を実行し、キャッシュされたbeanとcalbackをnullに設定し、次のbeanを取得するとObject FactoryのgetObject()を再実行し、新しいbeanを取得します.
    登録焼却方法
        // BeanLifecycleWrapper
        public void setDestroyCallback(Runnable callback) {
    		this.callback = callback;
    	}
    
         // GenericScope
    	@Override
    	public void registerDestructionCallback(String name, Runnable callback) {
    		BeanLifecycleWrapper value = this.cache.get(name);
    		if (value == null) {
    			return;
    		}
    		value.setDestroyCallback(callback);
    	}
    
        // AbstractBeanFactory
    	protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
    		AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
    		if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
    			if (mbd.isSingleton()) {
    				// Register a DisposableBean implementation that performs all destruction
    				// work for the given bean: DestructionAwareBeanPostProcessors,
    				// DisposableBean interface, custom destroy method.
    				registerDisposableBean(beanName,
    						new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
    			}
    			else {
    				// A bean with a custom scope...
    				Scope scope = this.scopes.get(mbd.getScope());
    				if (scope == null) {
    					throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
    				}
    				scope.registerDestructionCallback(beanName,
    						new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
    			}
    		}
    	}
    
    register Displable BenIfNecessaryという方法を見た時、前に書いたSpring beanが破壊した文章を見たことがある友達は比較的親しい系ができます.文章の住所:Spring beanが破壊する過程です.