ApplicationContext和BeanFactory

766 阅读3分钟

前言

Spirng中ApplicationContextBeanFactory道理有什么区别,可以先看看官方文档是如何解释的。

The org.springframework.beans and org.springframework.context packages are the 
basis for Spring Framework’s IoC container. The BeanFactory interface provides 
an advanced configuration mechanism capable of managing any type of object. 
ApplicationContext is a sub-interface of BeanFactory. It adds:

Easier integration with Springs AOP features

Message resource handling (for use in internationalization)

Event publication

Application-layer specific contexts such as the WebApplicationContext for use 
in web applications.

文档中表达的意思就是ApplicationContextBeanFactory基础上添加了很多的功能,但是对这两个接口的理解还是不够深刻,于是我打算看一看SpringBoot中是如何使用这两个接口的。

ApplicationContext

ApplicationContext的初始化

一切都要从SpringBoot的启动开始说起,在SpringBoot启动的时候会更具应用类型创建不同的上下文:

protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
				case SERVLET:
					contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
					break;
				case REACTIVE:
					contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
					break;
				default:
					contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
				}
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
						"Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass",
						ex);
			}
		}
		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}

如果是SERVLET则会创建AnnotationConfigServletWebServerApplicationContext类型的上下文(本文也是讨论改种类型)。

AnnotationConfigServletWebServerApplicationContext类Diagram图

AbstractApplicationContext

AbstractApplicationContext实现了ConfigurableApplicationContext接口。

/**
 * SPI interface to be implemented by most if not all application contexts.
 * Provides facilities to configure an application context in addition
 * to the application context client methods in the
 * {@link org.springframework.context.ApplicationContext} interface.
 *
 * <p>Configuration and lifecycle methods are encapsulated here to avoid
 * making them obvious to ApplicationContext client code. The present
 * methods should only be used by startup and shutdown code.
 */

该接口提供对应用上下文配置能力。该接口中有又一个比较重要的方法org.springframework.context.ConfigurableApplicationContext#refresh,在AbstractApplicationContext抽象类中的实现方法如下:

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

refresh()方法会在org.springframework.boot.SpringApplication#refresh方法中调用

  protected void refresh(ApplicationContext applicationContext) {
      Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
      ((AbstractApplicationContext) applicationContext).refresh();
  }

refresh()方法文档中解释说明在执行了该方法后,所有的singleton都会被实例化。

GenericApplicationContext

/**
 * Generic ApplicationContext implementation that holds a single internal
 * {@link org.springframework.beans.factory.support.DefaultListableBeanFactory}
 * instance and does not assume a specific bean definition format. Implements
 * the {@link org.springframework.beans.factory.support.BeanDefinitionRegistry}
 * interface in order to allow for applying any bean definition readers to it.
 */

GenericApplicationContext持有了DefaultListableBeanFactory实例,并实现了BeanDefinitionRegistry接口提供了对BeanDefinition增、删、查功能。

GenericApplicationContext.java

private final DefaultListableBeanFactory beanFactory;

public GenericApplicationContext() {
	this.beanFactory = new DefaultListableBeanFactory();
}

GenericApplicationContext的构造函数中初始化了DefaultListableBeanFactory, 对BeanDefinition增、删、查的功能都委托给了DefaultListableBeanFactory对象实现。其实在AbstractApplicationContext中实现了对bean的查找能力

AbstractApplicationContext.java

	@Override
	public Object getBean(String name) throws BeansException {
		assertBeanFactoryActive();
		return getBeanFactory().getBean(name);
	}

但是genBeanFactory()AbstractApplicationContext中是一个抽象方法,GenericApplicationContext实现了该方法

GenericApplicationContext.java

	@Override
	public final ConfigurableListableBeanFactory getBeanFactory() {
		return this.beanFactory;
	}

我们经常在写Spring测试代码的时候都都会对一个Bean进行查找如下:

public class CdtftApplication {

	public static void main(String[] args) {
		ApplicationContext context = new AnnotationConfigApplicationContext("github.cdtft");
		MyTestBean myTestBean = (MyTestBean) context.getBean("myTestBean");
		System.out.println(myTestBean);
	}
}

背后真正提供这种查找能力的是DefaultListableBeanFactory

总结