Spring IoC 和DI

IoC和DI通俗地说就是避免new关键字,解耦和。 但是需要一种描述让容器知道要创建的对象与对象的关系,即配置(文件和注解等)。

问题

1.对象与对象的关系表示
可以通过XML、properties等语义化配置文件表示
2.描述对象关系的文件存放地址
classpath,filesystem,url,servletContext
3.不同配置文件对对象描述不一样,如标准,自定义声明,需要统一
在框架内部需要有一个统一的关于对象的定义,所有外部的描述都必须转换成统一的描述定义
4.对不同的配置文件进行解析?
不同配置文件语法不一样,需要有不同的解析器

总结一下:即该如何收集,管理不同对象之间的对应关系

Spring核心容器类图

在spring—beans模块下的org.springframework.beans包中
image.png

BeanFactory

image.png
其中XmlBeanFactory已经不推荐使用。
BeanFactory的子接口共有3个ListableBeanFactory、HierarchicalBeanFactory和AutowireCapableBeanFactory。其最终默认实现类都为DefaultListableBeanFactory。

众多子接口的作用

每个接口都有其使用场合,为了区分spring内部操作过程中对象的传递和转化,对对象的数据访问所作的限制。

  • ListableBeanFactory:Bean可列表化
  • HierarchicalBeanFactory:Bean有继承关系,每个Bean可能有父Bean
  • AutowireCapableBeanFactory:定义Bean的自动装配规则
    三个接口共同定义Bean的集合,它们之间的关系及行为。

BeanFactory接口源码

public interface BeanFactory {
        /**
	 * @Author jtao
	 * @Date 2020/12/6 18:47
	 * @Description BeanFactory只对IoC容器的基本行为做了定义
	 */
	//对FactoryBean的转义定义,如果使用Bean的名字检索FactoryBean得到的是工程生产的Bean对象
	//如果需要得到工厂本身需要转义字符
	String FACTORY_BEAN_PREFIX = "&";

	//根据Bean名字得到Ioc中的Bean实例
	Object getBean(String name) throws BeansException;

	//根据Bean名字和Class类型来得到Bean实例,增加了类型安全验证机制
	<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;

	Object getBean(String name, Object... args) throws BeansException;

	<T> T getBean(Class<T> requiredType) throws BeansException;

	<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

	//提供对Bean的检索,看看在IoC容器中是否有这个名字的Bean
	boolean containsBean(String name);

	//根据Bean名字得到Bean实例,同时判断这个Bean是不是单例,原型,类型是否匹配
	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

	boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

	boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

	//得到Bean实例的Class类型
	@Nullable
	Class<?> getType(String name) throws NoSuchBeanDefinitionException;

	//得到Bean的别名,如果根据别名检索,那么其原名也会被检索出来
	String[] getAliases(String name);

}

ApplicationContext

ApplicationContext是一个更高级的IoC容器,其与BeanFactory关系如下图
image.png
除了继承BeanFactory接口基本功能外其还定义了以下功能
1.支持信息源,可以实现国际化 MessageSource
2.访问资源 ResourcePatternResolver
3.支持应用事件 ApplicationEventPublisher

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
		MessageSource, ApplicationEventPublisher, ResourcePatternResolver {

	/**
	 * Return the unique id of this application context.
	 * @return the unique id of the context, or {@code null} if none
	 */
	@Nullable
	String getId();

	/**
	 * Return a name for the deployed application that this context belongs to.
	 * @return a name for the deployed application, or the empty String by default
	 */
	String getApplicationName();

	/**
	 * Return a friendly name for this context.
	 * @return a display name for this context (never {@code null})
	 */
	String getDisplayName();

	/**
	 * Return the timestamp when this context was first loaded.
	 * @return the timestamp (ms) when this context was first loaded
	 */
	long getStartupDate();

	/**
	 * Return the parent context, or {@code null} if there is no parent
	 * and this is the root of the context hierarchy.
	 * @return the parent context, or {@code null} if there is no parent
	 */
	@Nullable
	ApplicationContext getParent();

	/**
	 * Expose AutowireCapableBeanFactory functionality for this context.
	 * <p>This is not typically used by application code, except for the purpose of
	 * initializing bean instances that live outside of the application context,
	 * applying the Spring bean lifecycle (fully or partly) to them.
	 * <p>Alternatively, the internal BeanFactory exposed by the
	 * {@link ConfigurableApplicationContext} interface offers access to the
	 * {@link AutowireCapableBeanFactory} interface too. The present method mainly
	 * serves as a convenient, specific facility on the ApplicationContext interface.
	 * <p><b>NOTE: As of 4.2, this method will consistently throw IllegalStateException
	 * after the application context has been closed.</b> In current Spring Framework
	 * versions, only refreshable application contexts behave that way; as of 4.2,
	 * all application context implementations will be required to comply.
	 * @return the AutowireCapableBeanFactory for this context
	 * @throws IllegalStateException if the context does not support the
	 * {@link AutowireCapableBeanFactory} interface, or does not hold an
	 * autowire-capable bean factory yet (e.g. if {@code refresh()} has
	 * never been called), or if the context has been closed already
	 * @see ConfigurableApplicationContext#refresh()
	 * @see ConfigurableApplicationContext#getBeanFactory()
	 */
	AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;

BeanDefinition

Bean对象在Spring实现中是以BeanDefinition来描述定义
image.png

BeanDefinitionReader

Bean的解析主要为对Spring配置文件进行解析。主要通过BeanDefinitionReader完成
image.png

Web Ioc容器初体验

在spring-webmvc模块中的org.springframework.web.servlet包中
image.png

DispatcherServlet的init()初始化方法在其父类HttpServletBean中
image.png

public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {

	/**
	 * Logger available to subclasses
	 */
	protected final Log logger = LogFactory.getLog(getClass());

	@Nullable
	private ConfigurableEnvironment environment;

	private final Set<String> requiredProperties = new HashSet<>(4);
        /**
	 * @Author jtao
	 * @Date 2020/12/6 22:10
	 * @Description 将配置参数映射到该servlet的bean属性上,并调用子类初始化。
	 * 如果bean属性无效(或缺少必需的属性),或者子类初始化失败,则抛出ServletException。
	 */
@Override
	public final void init() throws ServletException {
		if (logger.isDebugEnabled()) {
			logger.debug("Initializing servlet '" + getServletName() + "'");
		}

		// 注入bean属性
		PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
		if (!pvs.isEmpty()) {
			try {
				//定位资源
				BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
				//加载配置信息
				ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
				bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
				initBeanWrapper(bw);
				bw.setPropertyValues(pvs, true);
			} catch (BeansException ex) {
				if (logger.isErrorEnabled()) {
					logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
				}
				throw ex;
			}
		}

		//具体的初始化步骤实现方法由其子类具体实现 例如FrameworkServlet.initServletBean()方法
		initServletBean();  // 该类并未实现该方法。protected void initServletBean() throws ServletException { }

		if (logger.isDebugEnabled()) {
			logger.debug("Servlet '" + getServletName() + "' configured successfully");
		}
	}

其中initServletBean(); 是关键。HttpServletBean并没有实现该方法,而是由其子类实现。

protected void initServletBean() throws ServletException {
	}

FrameworkServlet重写了该方法

/**
	 * @Author jtao
	 * @Date 2020/12/6 22:16
	 * @Description {@link HttpServletBean}的重写方法,在设置任何bean属性之后调用。创建此Servlet的WebApplicationContext。
	 */
	@Override
	protected final void initServletBean() throws ServletException {
		getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
		if (this.logger.isInfoEnabled()) {
			this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
		}
		long startTime = System.currentTimeMillis();

		try {
			//具体的初始化方法 initWebApplicationContext
			this.webApplicationContext = initWebApplicationContext();
			initFrameworkServlet();
		} catch (ServletException ex) {
			this.logger.error("Context initialization failed", ex);
			throw ex;
		} catch (RuntimeException ex) {
			this.logger.error("Context initialization failed", ex);
			throw ex;
		}

		if (this.logger.isInfoEnabled()) {
			long elapsedTime = System.currentTimeMillis() - startTime;
			this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
					elapsedTime + " ms");
		}
	}

其中分别调用的方法如下


	/**
	 * @Author jtao
	 * @Date 2020/12/6 22:18
	 * @Description *初始化并发布此Servlet的WebApplicationContext。 * <p>代表{@link #createWebApplicationContext}用于实际创建上下文。可以在子类中覆盖。
	 * * @返回WebApplicationContext实例* @请参阅#FrameworkServlet(WebApplicationContext)* @请参阅#setContextClass * @请参阅#setContextConfigLocation
	 */
	protected WebApplicationContext initWebApplicationContext() {
		//从ServletContext中获得父容器WebApplicationContext
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		//声明子容器 wac
		WebApplicationContext wac = null;
		//建立父子容器之间的关联关系,分别判断情况
		if (this.webApplicationContext != null) {
			// A context instance was injected at construction time -> use it
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
					// The context has not yet been refreshed -> provide services such as
					// setting the parent context, setting the application context id, etc
					if (cwac.getParent() == null) {
						// The context instance was injected without an explicit parent -> set
						// the root application context (if any; may be null) as the parent
						cwac.setParent(rootContext);
					}
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}

		//先去ServletContext中查找web容器的引用是否存在,并创建好默认的空IoC容器
		if (wac == null) {
			// No context instance was injected at construction time -> see if one
			// has been registered in the servlet context. If one exists, it is assumed
			// that the parent context (if any) has already been set and that the
			// user has performed any initialization such as setting the context id
			wac = findWebApplicationContext();
		}
		//给上一步创建好的IoC容器赋值
		if (wac == null) {
			// No context instance is defined for this servlet -> create a local one
			wac = createWebApplicationContext(rootContext);
		}
		//private boolean refreshEventReceived = false;
		//true ,进行onRefresh()方法
		if (!this.refreshEventReceived) {
			// Either the context is not a ConfigurableApplicationContext with refresh
			// support or the context injected at construction time had already been
			// refreshed -> trigger initial onRefresh manually here.
			onRefresh(wac);
		}
		//private boolean publishContext = true;
		//true ,在getServletContext容器中添加
		if (this.publishContext) {
			// Publish the context as a servlet context attribute.
			String attrName = getServletContextAttributeName();
			getServletContext().setAttribute(attrName, wac);
			if (this.logger.isDebugEnabled()) {
				this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
						"' as ServletContext attribute with name [" + attrName + "]");
			}
		}

		return wac;
	}

	/**
	 * @Author jtao
	 * @Date 2020/12/6 22:38
	 * @Description 使用{@link #setContextAttribute配置名称}从{@code ServletContext} *属性中检索{@code WebApplicationContext}。
	 * 				在初始化(或调用)此Servlet之前,必须已经将{{code WebApplicationContext}加载并存储在{{code ServletContext}中。
	 * 				子类可以重写此方法以提供不同的* {@code WebApplicationContext}检索策略。 * * @返回此servlet的WebApplicationContext,
	 * 				如果找不到,则返回{@code null} * @see #getContextAttribute()
	 */
	@Nullable
	protected WebApplicationContext findWebApplicationContext() {
		String attrName = getContextAttribute();
		if (attrName == null) {
			return null;
		}
		WebApplicationContext wac =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);
		if (wac == null) {
			throw new IllegalStateException("No WebApplicationContext found: initializer not registered?");
		}
		return wac;
	}



	protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
		return createWebApplicationContext((ApplicationContext) parent);
	}



protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
		// public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;
		Class<?> contextClass = getContextClass();
		if (this.logger.isDebugEnabled()) {
			this.logger.debug("Servlet with name '" + getServletName() +
					"' will try to create custom WebApplicationContext context of class '" +
					contextClass.getName() + "'" + ", using parent context [" + parent + "]");
		}
		//public boolean isAssignableFrom(类<?> cls)确定由此类对象表示的类或接口是否与由指定的Class 类表示的类或接口相同或是超类或类接口。
		//如果是,则返回true ; 否则返回false 。 如果此类对象表示基本类型,则如果指定的类参数正好是类对象,则此方法返回true ; 否则返回false 。
		if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
			throw new ApplicationContextException(
					"Fatal initialization error in servlet with name '" + getServletName() +
							"': custom WebApplicationContext class [" + contextClass.getName() +
							"] is not of type ConfigurableWebApplicationContext");
		}
		ConfigurableWebApplicationContext wac =
				(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

		wac.setEnvironment(getEnvironment());
		wac.setParent(parent);
		String configLocation = getContextConfigLocation();
		if (configLocation != null) {
			wac.setConfigLocation(configLocation);
		}
		configureAndRefreshWebApplicationContext(wac);

		return wac;
	}

	protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
			// The application context id is still set to its original default value
			// -> assign a more useful id based on available information
			if (this.contextId != null) {
				wac.setId(this.contextId);
			} else {
				// Generate default id...
				//String APPLICATION_CONTEXT_ID_PREFIX = WebApplicationContext.class.getName() + ":";
				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
						ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
			}
		}

		wac.setServletContext(getServletContext());
		wac.setServletConfig(getServletConfig());
		wac.setNamespace(getNamespace());
		wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

		// The wac environment's #initPropertySources will be called in any case when the context
		// is refreshed; do it eagerly here to ensure servlet property sources are in place for
		// use in any post-processing or initialization that occurs below prior to #refresh
		ConfigurableEnvironment env = wac.getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
			((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
		}

		postProcessWebApplicationContext(wac);
		applyInitializers(wac);
		//最终调用ConfigurableWebApplicationContext.refresh()方法
		wac.refresh();
	}

最终调用的是ConfigurableWebApplicationContext.refresh()方法,该方法继承自ConfigurableApplicationContext接口

	/**
	 * @Author jtao 
	 * @Date 2020/12/6 23:46
	 * @Description 加载或刷新配置的持久表示形式,可能是XML文件,属性文件或关系数据库架构。 <p>
	 *     由于这是一种启动方法,因此,如果失败,它应该销毁已创建的单例,以避免悬挂资源。换句话说,在调用该方法之后,应实例化所有单例或根本不实例化。
	 *     @throws  BeansException如果无法初始化bean工厂
	 *     @throws  IllegalStateException如果已经初始化并且不支持多次刷新尝试
	 */
	void refresh() throws BeansException, IllegalStateException;

接着是onRefresh方法,该方法在FrameworkServlet类是默认不执行的,但其子类DispatcherServlet重写了该方法

/**
	 * @Author jtao
	 * @Date 2020/12/6 23:52
	 * @Description 对于子类默认情况下不执行操作
	 */
	protected void onRefresh(ApplicationContext context) {
		// For subclasses: do nothing by default.
	}

	/**
	 * This implementation calls {@link #initStrategies}.
	 */
	@Override
	protected void onRefresh(ApplicationContext context) {
		initStrategies(context);
	}

	/**
	 * Initialize the strategy objects that this servlet uses.
	 * <p>May be overridden in subclasses in order to initialize further strategy objects.
	 */
	/**
	 * @Author jtao 
	 * @Date 2020/12/6 23:59
	 * @Description 初始化此servlet使用的策略对象。 可以在子类中重写,以初始化其他策略对象。 
	 * 			SpringMVC的9大组件初始化
	 * 
	 */
	protected void initStrategies(ApplicationContext context) {
		//多文件上传组件
		initMultipartResolver(context);
		//初始化本地语言环境
		initLocaleResolver(context);
		//初始化模板处理器
		initThemeResolver(context);
		//初始化handerMapping
		initHandlerMappings(context);
		//初始化参数适配器
		initHandlerAdapters(context);
		//初始化异常拦截器
		initHandlerExceptionResolvers(context);
		//初始化视图预处理器
		initRequestToViewNameTranslator(context);
		//初始化视图转换器
		initViewResolvers(context);
		//初始化FlashMap管理器
		initFlashMapManager(context);
	}

总结

Web IoC容器初始化通过DispatcherServlet的父类HttpServletBean的 public final void init()方法初始化,该方法内部的initServletBean()负责具体的初始化方法,并且具体的初始化步骤实现方法由其子类具体实现,HttpServletBean并没有方法实现。FrameworkServlet重写了initServletBean()方法,在该方法内部调用initWebApplicationContext()继续调用onRefresh(),由其子类DispatcherServlet重写加载SpringMVC的九大组件,至此完毕!

基于XML的IoC容器初始化

IoC容器的初始化包括BeanDefinition的Resource定位、加载和注册!
以ApplicationContext为例。举例最常用的实现类XmlWebApplicationContext
image.png

private static final String PATH = "/org/springframework/context/support/";
private static final String FQ_SIMPLE_CONTEXT = PATH + "simpleContext.xml";
@Test
	public void testSingleConfigLocation() {
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(FQ_SIMPLE_CONTEXT);
		assertTrue(ctx.containsBean("someMessageSource"));
		ctx.close();
	}
	/**
	 * *创建一个新的ClassPathXmlApplicationContext,从给定的XML文件中加载定义*并自动刷新上下文。
	 * * @param configLocation资源位置
	 * *如果上下文创建失败,则@throws BeansException */
	 public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
		this(new String[] {configLocation}, true, null);
	}

	/**
	 * @Author jtao 
	 * @Date 2020/12/7 20:27
	 * @Description 使用给定的父级创建一个新的ClassPathXmlApplicationContext,从给定的XML文件中加载定义。 
	 *  @param configLocations资源位置数组
	 *  @param refresh是否自动刷新上下文,加载所有bean定义并创建所有单例。或者,在进一步配置上下文之后,手动调用刷新。
	 *  @param parent父上下文@throws BeansException如果上下文创建失败
	 *  @see #refresh()
	 */
	public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {

		super(parent);
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();
		}
	}

其中super(parent)一直往上调用到AbstractApplicationContext的AbstractApplicationContext方法。refresh()也是调用父类AbstractApplicationContext的方法

static {
		// Eagerly load the ContextClosedEvent class to avoid weird classloader issues
		// on application shutdown in WebLogic 8.1. (Reported by Dustin Woods.)
		//静态初始化代码块,在整个容器创建过程中只执行一次,避免应用程序在Weblogic 8.1关闭时出现类加载异常更为题
		//加载IoC容器关闭事件类
		ContextClosedEvent.class.getName();
	}

/**
	 * Create a new AbstractApplicationContext with the given parent context.
	 * @param parent the parent context
	 */
	public AbstractApplicationContext(@Nullable ApplicationContext parent) {
		this();
		setParent(parent);
	}


/**
	 * @Author jtao 
	 * @Date 2020/12/7 20:39
	 * @Description 创建一个没有父级的新AbstractApplicationContext。
	 */
	public AbstractApplicationContext() {
		this.resourcePatternResolver = getResourcePatternResolver();
	}

/**
	 * @Author jtao
	 * @Date 2020/12/7 20:40
	 * @Description 设置此应用程序上下文的父级。 *
	 * 父{@linkplain ApplicationContext#getEnvironment()环境}是
	 * {@linkplain ConfigurableEnvironment#merge(ConfigurableEnvironment)合并}与
	 * (父)非{@code null}且此(子)应用上下文环境*它的环境是{@link ConfigurableEnvironment}的实例。
	 * @请参阅ConfigurableEnvironment#merge(ConfigurableEnvironment)
	 */
	@Override
	public void setParent(@Nullable ApplicationContext parent) {
		this.parent = parent;
		if (parent != null) {
			Environment parentEnvironment = parent.getEnvironment();
			if (parentEnvironment instanceof ConfigurableEnvironment) {
				getEnvironment().merge((ConfigurableEnvironment) parentEnvironment);
			}
		}
	}

其中AbstractApplicationContext类中的public AbstractApplicationContext构造方法中的this()调用了同类中的ResourcePatternResolver方法加载资源,如下图所示
image.png

protected ResourcePatternResolver getResourcePatternResolver() {
		return new PathMatchingResourcePatternResolver(this);
	}

image.png
setConfigLocations(configLocations);方法实现如下

	//处理单个文件路径为一个字符串的情况
	public void setConfigLocation(String location) {
		//String CONFIG_LOCATION_DELIMITERS = ",; \t\n";
		//即多个资源文件路径之间用",; \t\n"分割,解析成数组
		setConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS));
	}

	/**
	 * Set the config locations for this application context.
	 * <p>If not set, the implementation may use a default as appropriate.
	 */
	//解析Bean定义资源文件的路径,处理多个资源文件字符串数组
	public void setConfigLocations(@Nullable String... locations) {
		if (locations != null) {
			Assert.noNullElements(locations, "Config locations must not be null");
			this.configLocations = new String[locations.length];
			for (int i = 0; i < locations.length; i++) {
				//resolvePath 为同一个类中将字符串解析为路径的方法
				this.configLocations[i] = resolvePath(locations[i]).trim();
			}
		}
		else {
			this.configLocations = null;
		}
	}

最终调用AbstractApplicationContext.refresh();方法刷新。

@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			//1.调用容器准备刷新的方法,获取容器的当前事时间,同时给容器设置同步标识
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			//2.告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的载入从子类的refreshBeanFactory()方法启动
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			//3.为BeanFactory配置容器特性,例如类加载器、事件处理器
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				//4.为容器的某些子类指定特殊的post事件处理器
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				//5.调用所有注册的BeanFactoryPostProcessor的Bean
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				//6.为BeanFactory注册Post事件处理器
				//BeanPostProcessor时Bean后置处理器,用于监听容器触发的事件
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				//7.初始化信息源,和国际化相关
				initMessageSource();

				// Initialize event multicaster for this context.
				//8.初始化容器事件传播器
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				//9.调用子类的某些特殊Bean的初始化方法
				onRefresh();

				// Check for listener beans and register them.
				//10.为事件传播器注册事件监听器
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				//11.初始化所有剩余的Bean单例
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				//12.初始化容器的生命周期事件处理器,并发布容器的生命周期事件
				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.
				//13.销毁已创建的Bean
				destroyBeans();

				// Reset 'active' flag.
				//14.取消刷新操作,重置容器的同步标识
				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...
				//15.设置公共缓存
				resetCommonCaches();
			}
		}
	}
  • refresh方法的主要为IoC容器Bean的生命周期管理提供条件,Spring IoC容器载入Bean配置信息从其子类容器的refreshBeanFactory()方法启动,所以整个refresh()方法中
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

以后的代码都是在注册容器的信息源和生命周期事件,载入就通过这句代码启动。
refresh()方法的主要作用是:在创建IoC容器前,如果已经有容器存在,需要把已有的容器销毁和关闭,以保证在refresh()方法之后使用的是新创建的IoC容器。它类似于对IoC容器的重启,在新创建的容器中对容器进行初始化,对Bean配置资源进行载入。

创建容器

image.png

//2.告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的载入从子类的refreshBeanFactory()方法启动
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

/**
	 * @Author jtao
	 * @Date 2020/12/8 18:01
	 * @Description 让子类刷新bean工厂
	 */
	protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		//
		refreshBeanFactory();
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		if (logger.isDebugEnabled()) {
			logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
		}
		return beanFactory;
	}

protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;

AbstractApplicationContext抽象类的抽象方法refreshBeanFactory由其子类实现如下图所示
image.png
AbstractRefreshableApplicationContext的refreshBeanFactory方法如下。其中还实现了序列化beanfactory

/**
	 * @Author jtao
	 * @Date 2020/12/8 18:09
	 * @Description 此实现对此上下文的基础bean工厂执行实际的刷新,关闭先前的bean工厂(如果有),并为上下文生命周期的下一阶段初始化一个新的bean工厂。
	 */
	@Override
	protected final void refreshBeanFactory() throws BeansException {
		//检查是否有容器,如果有则销毁容器中的bean,关闭容器
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			//创建IoC容器
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			//指定一个ID以进行序列化,如果需要的话,允许将该BeanFactory从该ID反序列化回BeanFactory对象。
			beanFactory.setSerializationId(getId());
			//对IoC容器进行定制化,设置启动参数、开启注解的自动装配等
			customizeBeanFactory(beanFactory);
			//调用载入Bean定义的方法,抽象方法,未实现,由其子类实现,委派模式
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}

上述refreshBeanFactory代码先判断beanFactory是否存在,如果存在则先销毁Bean并关闭beanFactory,接着创建DefaultListableBeanFactory,并调用loadBeanDefinitions()方法装载Bean定义。loadBeanDefinitions(beanFactory)由AbstractXmlApplicationContext类实现

/**
	 * @Author jtao 
	 * @Date 2020/12/8 23:09
	 * @Description 通过XmlBeanDefinitionReader加载bean定义
	 */
	@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// Create a new XmlBeanDefinitionReader for the given BeanFactory.
		//创建XmlBeanDefinitionReader,即创建Bean读取器
		//并通过回调设置到容器中,容器使用该读取器读取Bean配置资源
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// Configure the bean definition reader with this context's
		// resource loading environment.
		//为Bean读取器设置Spring资源加载器
		//AbstractXmlApplicationContext的祖先父类AbstractApplicationContext继承DefaultResourceLoader
		//因此容器本身也是一个资源加载器
		//
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		//为Bean读取器设置SAX xml解析器
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		// Allow a subclass to provide custom initialization of the reader,
		// then proceed with actually loading the bean definitions.
		//当Bean读取器读取Bean定义的xml资源文件时,启用xml的校验机制
		initBeanDefinitionReader(beanDefinitionReader);
		//Bean读取器真正实现加载的方法
		loadBeanDefinitions(beanDefinitionReader);
	}

/**
	 * @Author jtao 
	 * @Date 2020/12/8 23:12
	 * @Description 初始化用于加载此上下文的bean *定义的bean定义读取器。默认实现为空。
	 * 可以在子类中覆盖,例如用于关闭XML验证*或使用其他XmlBeanDefinitionParser实现。
	 */
	protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
		reader.setValidating(this.validating);
	}


/**
	 * @Author jtao
	 * @Date 2020/12/8 23:14
	 * @Description 使用给定的XmlBeanDefinitionReader加载bean定义。
	 * bean工厂的生命周期由{@link #refreshBeanFactory}
	 * 方法处理;因此,该方法仅用于加载和/或注册Bean定义。
	 * xml Bean读取器加载Bean配置资源
	 */
	protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
		//在该类中protected Resource[] getConfigResources() {return null;	}为null
		//可以在子类中重写该方法
		Resource[] configResources = getConfigResources();
		if (configResources != null) {
			//xml Bean读取器调用XmlBeanDefinitionReader父类AbstractBeanDefinitionReader读取定位的Bean配置资源
			reader.loadBeanDefinitions(configResources);
		}
		//
		//如果子类中获取的Bean配置资源定位为空,
		//则获取ClasspathXmlApplicationContext构造方法中setConfigLocation方法设置的资源
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			//xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位的Bean配置资源
			reader.loadBeanDefinitions(configLocations);
		}
	}

/**
	 * @Author jtao
	 * @Date 2020/12/8 23:41
	 * @Description  返回一个资源对象数组,引用此上下文应使用的XML bean定义文件。
	 * 				默认实现返回{@code null}。子类可以重写
	 * 				以提供预构建的Resource对象,而不是位置字符串。
	 */
	//这里又使用了一个委派模式,调用子类的获取Bean配置资源定位的方法
	//该方法在ClassPathXmlApplicationContext中实现
	@Nullable
	protected Resource[] getConfigResources() {
		return null;
	}

以ClassPathXmlApplicationContext为例,getConfigResource()方法的返回值为null,因此程序执行read.loadBeanDefinitions(configLocations)分支。

分配路径处理策略







这个家伙很懒,啥也没有留下😋