spring源码学习(四)

在(三)里我们已经进入了AbstractApplicationContext的刷新方法,refresh,代码如下

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) {
				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

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

				// Propagate exception to caller.
				throw ex;
			}
		}
	}

它由很多个步骤组成,我们在(四)中就研究

prepareRefresh

这个步骤,看看刷新的第一步做了什么

顾名思义,刷新前的预处理,处理了什么,我们点进去看一下

protected void prepareRefresh() {
		this.startupDate = System.currentTimeMillis();

		synchronized (this.activeMonitor) {
			this.active = true;
		}

		if (logger.isInfoEnabled()) {
			logger.info("Refreshing " + this);
		}

		// Initialize any placeholder property sources in the context environment
		initPropertySources();

		// Validate that all properties marked as required are resolvable
		// see ConfigurablePropertyResolver#setRequiredProperties
		this.environment.validateRequiredProperties();
	}

 可以看到,这个方法里它产生了容器的开始时间,接着改了active状态,这里稍微要注意一下

synchronized (this.activeMonitor) {
			this.active = true;
		}

他在改状态的时候用activeMonitor加了锁,并且改了active的状态为true,作者这么写的用意一定是为了让active在后续的某个操作或某个判断中去使用,既然active = true是在容器启动的开始时改的,那么后续肯定是在一些需要判断容器是否启动了来的地方使用,我们可以认为它是容器的激活状态,后续在任何修改容器的激活状态的地方,也一定会用activeMonitor来加锁。

再往下,打印一行refresh的日志

if (logger.isInfoEnabled()) {
			logger.info("Refreshing " + this);
		}

继续看

// Initialize any placeholder property sources in the context environment
		initPropertySources();

初始化propertySources,主意哈,这个propertySources不是一堆propertySource啊,而是一个容器

spring里有两个概念,一个是propertySource,一个是propertySources,后者不是简单的表示一堆前者,而是表示一个存放前者的容器。我之前也在强调,spring里有很多名词,我们都需要去熟悉它们,知道它们是干什么的。  这里稍微偏移了扩展一下,我们看看propertySource和PropertySources的源码

在core包下,我们找到propertySource

public abstract class PropertySource<T> {

	protected final Log logger = LogFactory.getLog(this.getClass());

	protected final String name;

	protected final T source;

	/**
	 * Create a new {@code PropertySource} with the given name and source object.
	 */
	public PropertySource(String name, T source) {
		Assert.hasText(name, "Property source name must contain at least one character");
		Assert.notNull(source, "Property source must not be null");
		this.name = name;
		this.source = source;
	}

 从泛型和构造我们可以大致了解,propertySource,是一个用来存放property的keyValue的实体,构造表示,我拿任意类型的对象进来,都可以设置一个key,存在成员name上,然后把对象存放在成员source上,是不是很像一个Map.Entry<String,Object>对象呢。

这里稍微需要了解下他有2个静态内部类StubPropertySource和ComparisonPropertySource

StubPropertySource是Property的子类,用于充当存根,它的注释中提到实际的PropertySource的初始化工作,不能在容器创建之前进行,所以用存根来充当一个占位符,他用来占用一个预期的propertySource位置,等容器refresh的时候用来进行替换,替换后成一个真正的值

如果这段话不好理解,可以先稍微知道有这么个东西,它是一个变量,用来占着你的property的位置,等刷新的时候再进行填充。

ComparisonPropertySource是存根的子类,仅仅用于集合的操作。注释中也是这么说,我们在propertySource的named方法上按f2

Return a PropertySource implementation intended for collection comparison purposes only. 

Primarily for internal use, but given a collection of PropertySource objects, may be used as follows: 

 List<PropertySource<?>> sources = new ArrayList<PropertySource<?>>();
 sources.add(new MapPropertySource("sourceA", mapA));
 sources.add(new MapPropertySource("sourceB", mapB));
 assert sources.contains(PropertySource.named("sourceA"));
 assert sources.contains(PropertySource.named("sourceB"));
 assert !sources.contains(PropertySource.named("sourceC"));

 这里的集合比较不是简单的比较大小的意思,而 是一系列集合可比较的操作,如contains,equals等。

看完propertySource,我们再看propertySources

public interface PropertySources extends Iterable<PropertySource<?>> {

	/**
	 * Return whether a property source with the given name is contained.
	 * @param name the {@linkplain PropertySource#getName() name of the property source} to find
	 */
	boolean contains(String name);

	/**
	 * Return the property source with the given name, {@code null} if not found.
	 * @param name the {@linkplain PropertySource#getName() name of the property source} to find
	 */
	PropertySource<?> get(String name);

}

 可以看到,他是propertySource的一个容器接口,具备迭代能力,有2个容器级别的方法,contains和get,这都是集合中常见的概念,它只有一个实现类,MutablePropertySources

再看看MutablePropertySources

public class MutablePropertySources implements PropertySources {

	static final String NON_EXISTENT_PROPERTY_SOURCE_MESSAGE = "PropertySource named [%s] does not exist";
	static final String ILLEGAL_RELATIVE_ADDITION_MESSAGE = "PropertySource named [%s] cannot be added relative to itself";

	private final Log logger;

	private final LinkedList<PropertySource<?>> propertySourceList = new LinkedList<PropertySource<?>>();

他有一个propertySourceList,是用链表List来实现的,用于存放所有的propertySource,并且MutablePropertySources实现了contanins,get等集合方法,还有addFirst,addLast等添加对象的方法。在我们还没看到propertyResolver之前,先可以认为propertySource的检索都由容器自己完成,后续会看到一个全新的工具resolver,专门用于propertySources的检索,解析等相关工作

扯远了,回到容器刷新的第一步,initPropertySources方法来

@Override
	protected void initPropertySources() {
		super.initPropertySources();
		WebApplicationContextUtils.initServletPropertySources(
				this.getEnvironment().getPropertySources(), this.servletContext,
				this.servletConfig);
	}

super的init是空实现,具体的初始化工作,直接由第2行开始

我们可以看到,initServletProperSources,顾名思义,就是初始化propertySources这个容器,那我们应该可以想到,它应该是往容器里塞点东西,  再看后面2个参数,一个是servletContext,一个servletConfig,那么我们知道了,它这个初始化,其实是想把servletContext,一个servletConfig这两个对象给存到容器的propertySources容器里去,我们点进去看看是不是和我们想得一样

public static void initServletPropertySources(
			MutablePropertySources propertySources, ServletContext servletContext, ServletConfig servletConfig) {
		Assert.notNull(propertySources, "propertySources must not be null");
		if(servletContext != null &&
				propertySources.contains(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) &&
				propertySources.get(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) {
			propertySources.replace(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, new ServletContextPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, servletContext));
		}
		if(servletConfig != null &&
				propertySources.contains(SERVLET_CONFIG_PROPERTY_SOURCE_NAME) &&
				propertySources.get(SERVLET_CONFIG_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) {
			propertySources.replace(SERVLET_CONFIG_PROPERTY_SOURCE_NAME, new ServletConfigPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME, servletConfig));
		}
	}

 果然是这样,key是常量,value是一个PropertySource,

我们看下replace的实现,实现当然在propertySources的唯一一个实现类中,MutablePropertySources

/**
	 * Replace the property source with the given name with the given property source object.
	 * @param name the name of the property source to find and replace
	 * @param propertySource the replacement property source
	 * @throws IllegalArgumentException if no property source with the given name is present
	 * @see #contains
	 */
	public void replace(String name, PropertySource<?> propertySource) {
		logger.debug(String.format("Replacing [%s] PropertySource with [%s]",
				name, propertySource.getName()));
		int index = assertPresentAndGetIndex(name);
		this.propertySourceList.set(index, propertySource);
	}

 可以看到,他先用name查出这个propertySource的下标,然后进行替换

由于我们在之前容器里已经有servletContext了,那么现在servletContext就被propertySources接管了,它用replace方法,将servletContext存放了进来,key是一个常量,value是一个ServetPropertySource对象,ServetPropertySource就类似一个Entry.

读到这里我明白了,原来preRefresh预刷新,其实就是把记录一下开始时间,打印一下日志,然后把servletConfig和servletContext放到spring容器的propertySources容器里面.

好了,下一节我们一起去读refresh的第2步,

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

 这一步里,spring开始使用Reader,Resource,Document,Registry,去解析配置文件,生成BeanDefinition,并且在注册器里进行注册,可以说,这一步,完成bean的读取解析的很重要的工作。

猜你喜欢

转载自rkdu2-163-com.iteye.com/blog/2003638