首先我覺得分析ApplicationContext必須從它的實現類開始進行分析,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的類結構圖:
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的父接口,如下圖:
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,它的類結構圖如下所示:
還有一個比較重要的屬性就是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 <strong>registerShutdownHook</strong>() {if (this.shutdownHook == null) {// No shutdown hook registered yet.this.shutdownHook = new Thread() {@Overridepublic void run() {synchronized (startupShutdownMonitor) {doClose();}}};Runtime.getRuntime().addShutdownHook(this.shutdownHook);}}<br><br>
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}}}
}