天天看點

Spring 源碼分析之AbstractApplicationContext源碼分析

  首先我覺得分析ApplicationContext必須從它的實作類開始進行分析,AbstractApplicationContext我覺得是一個不錯的選擇,那我們就從這裡開始逐一分析吧,首先我自己手畫了一張圖,作為索引吧,其中藍色的為類,紫色的為接口,箭頭 指向的方向是父類或者父接口。

Spring 源碼分析之AbstractApplicationContext源碼分析
  因為裡面接口和方法過多,是以不做展示,下面具體來進行代碼分析。首先我們來看看這句話,MESSAGE_SOURCE_BEAN_NAME。

public static final String MESSAGE_SOURCE_BEAN_NAME = "messageSource";
      

  它這句話翻譯成中文就是消息資源的bean的一個name,我們暫時把它看成一個普通的beanName,我們來看看有哪些地方引用到了這個屬性,首先在initMessageSource方法裡面有引用到,我把這些地方标紅顯示了。

protected void initMessageSource() {
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
			this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
			// Make MessageSource aware of parent MessageSource.
			if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
				HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
				if (hms.getParentMessageSource() == null) {
					// Only set parent context as parent MessageSource if no parent MessageSource
					// registered already.
					hms.setParentMessageSource(getInternalParentMessageSource());
				}
			}
			if (logger.isTraceEnabled()) {
				logger.trace("Using MessageSource [" + this.messageSource + "]");
			}
		}
		else {
			// Use empty MessageSource to be able to accept getMessage calls.
			DelegatingMessageSource dms = new DelegatingMessageSource();
			dms.setParentMessageSource(getInternalParentMessageSource());
			this.messageSource = dms;
			beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
			if (logger.isTraceEnabled()) {
				logger.trace("No '" + MESSAGE_SOURCE_BEAN_NAME + "' bean, using [" + this.messageSource + "]");
			}
		}
	}
      

  還有一個顯示的地方就是在StaticApplicationContext類中的構造器當中有使用到。下面是StaticApplicationContext的類結構圖:

Spring 源碼分析之AbstractApplicationContext源碼分析
public StaticApplicationContext(@Nullable ApplicationContext parent) throws BeansException {
		super(parent);

		// Initialize and register a StaticMessageSource.
		this.staticMessageSource = new StaticMessageSource();
		getBeanFactory().registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.staticMessageSource);
	}
      

  我們下面再來看一下AbstractApplicationContext這個類的一些Fields,并且來理清一下對象和對象之間的依賴關系,首先是parent -ApplicationContext,我們找到是一個叫做getParent()的方法對這個私有的屬性進行了調用,然後又發現了getParentBeanFactory方法也對其進行了間接調用,因為BeanFactory是ApplicationContext的父接口,如下圖:

Spring 源碼分析之AbstractApplicationContext源碼分析
private ApplicationContext parent;      
public ApplicationContext getParent() {
   return this.parent;
}      
public BeanFactory getParentBeanFactory() {
   return getParent();
}      

  在這個類中還有一個屬性是environment,這個environment在這裡也有getter和setter方法,唯一需要注意的是如果調用getEnvironment()方法在environment為空的情況下會建立一個StandardEnvironment對象。

private ConfigurableEnvironment environment;      
public ConfigurableEnvironment getEnvironment() {
   if (this.environment == null) {
      this.environment = createEnvironment();
   }
   return this.environment;
}      

  StandardEnvironment類繼承了抽象的AbstractEnvironment,它的類結構圖如下所示:

Spring 源碼分析之AbstractApplicationContext源碼分析

  還有一個比較重要的屬性就是beanFactoryPostProcessors,這事一個ArrayList的數組,Doc當中給出的解釋是當onRefresh的時候有用到。我找到了3個方法引用到了這個屬性,下面都已标紅。

private final List<BeanFactoryPostProcessor> beanFactoryPostProcessors = new ArrayList<>();      
public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor) {
   Assert.notNull(postProcessor, "BeanFactoryPostProcessor must not be null");
   this.beanFactoryPostProcessors.add(postProcessor);
}      
public List<BeanFactoryPostProcessor> getBeanFactoryPostProcessors() {
   return this.beanFactoryPostProcessors;
}      
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
   PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

   // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
   // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
   if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
      beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
      beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
   }
}      

  往下面看還有一個屬性active,它是線程安全的,用到了CAS技術。它常常和closed這個屬性一起使用,我們發現,在如下3個地方有組合引用,我已用相應的顔色辨別出來。

  • prepareRefresh
  • doClose
  • assertBeanFactoryActive
private final AtomicBoolean active = new AtomicBoolean();      
private final AtomicBoolean closed = new AtomicBoolean();      
protected void prepareRefresh() {
   // Switch to active.
   this.startupDate = System.currentTimeMillis();
   this.closed.set(false);
   this.active.set(true);
   ....................
   ....................
}

      
protected void doClose() {
   // Check whether an actual close attempt is necessary...
   if (this.active.get() && this.closed.compareAndSet(false, true)) {
      if (logger.isDebugEnabled()) {
         logger.debug("Closing " + this);
      }
    ........................
    ........................      
//Switch to inactive.
   this.active.set(false);      
}      
protected void assertBeanFactoryActive() {
   if (!this.active.get()) {
      if (this.closed.get()) {
         throw new IllegalStateException(getDisplayName() + " has been closed already");
      }
      else {
         throw new IllegalStateException(getDisplayName() + " has not been refreshed yet");
      }
   }
}      

  我們繼續往下看,有一個startupShutdownMonitor的屬性,字面意思上面了解就是啟動關閉螢幕,屬性在這個類當中的命名表示了它所發揮的作用,我們來看一下有哪些方法引用到了這個屬性。大家不知道發現沒有,還有一個“很像”的屬性在這裡就是shutdownHook,這個和startupShutdownMonitor是配合在一起使用的。shudownHook在這裡是一個線程類型的屬性。

private final Object startupShutdownMonitor = new Object();      
private Thread shutdownHook;      
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {......      
}......
}
      
public void registerShutdownHook() {
   if (this.shutdownHook == null) {
      // No shutdown hook registered yet.
      this.shutdownHook = new Thread() {
         @Override
         public void run() {
            synchronized (startupShutdownMonitor) {
               doClose();
            }
         }
      };
      Runtime.getRuntime().addShutdownHook(this.shutdownHook);
   }
}      
public void close() {
   synchronized (this.startupShutdownMonitor) {
      doClose();
      // If we registered a JVM shutdown hook, we don't need it anymore now:
      // We've already explicitly closed the context.
      if (this.shutdownHook != null) {
         try {
            Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
         }
         catch (IllegalStateException ex) {
            // ignore - VM is already shutting down
         }
      }
   }
}      

 既然shutdownHook和startupShutdownMonitor一起使用,那麼它們之間的關系我們得分析一下,hook顧名思義鈎子,說簡單點這個就是一個鈎子,也算是一個擴充點。我們來仔細分析一下它的幾個方法,首先是registerShutdownHook方法:這個方法有一句話特别重要,就是Runtime.getRuntime().addShutdownHook(this.shutdownHook);它實際上在系統層面上把鈎子線程添加到了JVM虛拟機。在鈎子運作的時候,就會執行doClose方法關閉并銷毀applicationContext。需要注意的一點是明白registerShutdownHook方法和close方法的不同點,在close方法中如果發現已經調用registerShutdownHook在JVM層面上注冊了鈎子,那麼就調用Runtime.getRuntime().removeShutdownHook(this.shutdownHook)移除此鈎子,另外這個close的實作來自于closable接口的父接口AutoClosable接口方法,而registerShutdownHook則在PropertyResolver當中被定義。

public void registerShutdownHook() {
		if (this.shutdownHook == null) {
			// No shutdown hook registered yet.
			this.shutdownHook = new Thread() {
				@Override
				public void run() {
					synchronized (startupShutdownMonitor) {
						doClose();
					}
				}
			};
			Runtime.getRuntime().addShutdownHook(this.shutdownHook);
		}
	}

      
public void close() {
   synchronized (this.startupShutdownMonitor) {
      doClose();
      // If we registered a JVM shutdown hook, we don't need it anymore now:
      // We've already explicitly closed the context.
      if (this.shutdownHook != null) {
         try {
            Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
         }
         catch (IllegalStateException ex) {
            // ignore - VM is already shutting down
         }
      }
   }
}      

 時間不早了,今天就寫到這裡吧。

Spring 源碼分析之AbstractApplicationContext源碼分析

作者:

KMSFan

出處:http://www.cnblogs.com/kmsfan

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。

歡迎大家加入KMSFan之家,以及通路我的優酷空間!