Spring源码解析(一) -- beanfactory

Spring源码解析(一) -- beanfactory

  spring的源码其实来来回回看过不下5次,但是之前没有总结的习惯,总是看了又忘。今天开始计划重新看一遍spring,并做记录和总结。

  进入主题,我们来分析一下beanfactory的创建。

1 beanfactory的创建

    AbstractApplicationContextrefresh()方法是spring容器启动的入口,而创建beanfactory又是其中的第一步。

      

  一路分析到 AbstractRefreshableApplicationContextrefreshBeanFactory()

  

protected final void refreshBeanFactory() throws BeansException {
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			customizeBeanFactory(beanFactory);
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}

  注意下 DefaultListableBeanFactory 的继承关系

  

  注意下这个类DefaultSingletonBeanRegistry可以说,所有实例化好了的Bean都是放在这个类里的。

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
		implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable

  DefaultListableBeanFactory 是实现了 BeanDefinitionRegistry接口的,说明出了用作bean的容器,他另外的作用就是注册BeanDefinition。所以上面代码中的

 loadBeanDefinitions(beanFactory)就是我们要重点分析的方法
 public abstract class AbstractBeanFactory 中的Map就是用来保存所有解析出来的BeanDefinition的
/** Map from bean name to merged RootBeanDefinition */
    private final Map<String, RootBeanDefinition> mergedBeanDefinitions =
            new ConcurrentHashMap<String, RootBeanDefinition>(256);

loadBeanDefinitions

  继续跟代码找到一个重要的类XmlBeanDefinitionReader,它是解析xml,并且把xml中的节点转换成BeanDefinition的关键

  它有一个成员变量,待会也是我们要讲解的重点

  private NamespaceHandlerResolver namespaceHandlerResolver;

  一路分析就找到 XmlBeanDefinitionReader的 doLoadBeanDefinitions,进一步找到DefaultBeanDefinitionDocumentReader的 doRegisterBeanDefinitions。doRegisterBeanDefinitions才是我们要分析的重点

  

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		if (delegate.isDefaultNamespace(root)) {
			NodeList nl = root.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
				Node node = nl.item(i);
				if (node instanceof Element) {
					Element ele = (Element) node;
					if (delegate.isDefaultNamespace(ele)) {
						parseDefaultElement(ele, delegate);
					}
					else {
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}

  先说结论,该方法做的事情很明确就是解析xml文件中的各个节点,delegate.isDefaultNamespace判断该节点是否是默认命名空间,所谓默认命名空间就是指beans这种节点所在的命名空间。而对一些非beans的节点,比如我们也比较常用的aop,还有做定时调度的task标签,走的就是else分支也就是delegate.parseCustomElement。

  对于节点的解析,不必细讲。接下来重点分析BeanDefinitionParserDelegate 及其相关的源码,和spring的命名空间解析机制。

3 Spring的命名空间解析机制

   要分析命名空间,可以从上一节的 BeanDefinitionParserDelegate 入手。 

public BeanDefinitionParserDelegate(XmlReaderContext readerContext) {
		Assert.notNull(readerContext, "XmlReaderContext must not be null");
		this.readerContext = readerContext;
	}

  构造方法是要传入一个XmlReaderContext,继续分析 XmlReaderContext 

public class XmlReaderContext extends ReaderContext {

	private final XmlBeanDefinitionReader reader;

	private final NamespaceHandlerResolver namespaceHandlerResolver;

  XmlReaderContext包含着一个重要的成员变量  NamespaceHandlerResolver 

  ReaderContext是在把配置文件解析成beandefinition过程中创建的,代码在 XmlBeanDefinitionReader 的  registerBeanDefinitions 方法

  

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		int countBefore = getRegistry().getBeanDefinitionCount();
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}

  一路追踪终于找到了 上面说的那个重要的成员变量 namespaceHandlerResolver的初始化位置。

public DefaultNamespaceHandlerResolver(ClassLoader classLoader) {
this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION); // DEFAULT_HANDLER_MAPPINGS_LOCATION 值为 META-INF/spring.handlers
}

  以及里面的重要方法 

  

@Override
	public NamespaceHandler resolve(String namespaceUri) {
		Map<String, Object> handlerMappings = getHandlerMappings();
		Object handlerOrClassName = handlerMappings.get(namespaceUri);
		if (handlerOrClassName == null) {
			return null;
		}

  其中的getHandlerMappings就是去扫描classloader能找到范围内,所有jar包里的META-INF/spring.handlers中内容

  我们可以看一下具体的实现

  

public static Properties loadAllProperties(String resourceName, ClassLoader classLoader) throws IOException {
		Assert.notNull(resourceName, "Resource name must not be null");
		ClassLoader classLoaderToUse = classLoader;
		if (classLoaderToUse == null) {
			classLoaderToUse = ClassUtils.getDefaultClassLoader();
		}
		Enumeration<URL> urls = (classLoaderToUse != null ? classLoaderToUse.getResources(resourceName) :
				ClassLoader.getSystemResources(resourceName));
		Properties props = new Properties();
		while (urls.hasMoreElements()) {
			URL url = urls.nextElement();
			URLConnection con = url.openConnection();
			ResourceUtils.useCachesIfNecessary(con);
			InputStream is = con.getInputStream();
			try {
				if (resourceName.endsWith(XML_FILE_EXTENSION)) {
					props.loadFromXML(is);
				}
				else {
					props.load(is);
				}
			}
			finally {
				is.close();
			}
		}
		return props;
	}

  resourceName就是 META-INF/spring.handlers

  再来看一眼 META-INF/spring.handlers里面是什么样子,以aop为例,

    内容是 

http://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler

这样 每个命名空间和他对应的解析类绑定在一起

  再回到 BeanDefinitionParserDelegate的代码中

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
		String namespaceUri = getNamespaceURI(ele);
		NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
		if (handler == null) {
			error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
			return null;
		}
		return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
	}

  每一个spring xml中的节点都有一个命名空间,这是定义在每一个xsd文件中的,而且spring配置文件的beans的属性中有每个命名空间对应的xsd的名字,这样每个节点标签对应的命名空间就能够获取到了

  总结:

   在AbstractApplicationContext中第一步就是拿到beanfactory,这一步做完之后,通过BeanDefitionReader获取到的BeanDefinition,并注册到beanfactory的map中缓存起来。我这里没说全部的BeanDefinition,因为考虑到Spring如果是以Configuration标签方式而不是xml方式的话,拿到beanfactory的时候只能拿到一个beanfactorypostprocessor,就是ConfigurationClassPostProcessor这个 beanFactoryPostProcessor,再由它去完成BeanDefinition的注册。

原文地址:https://www.cnblogs.com/juniorMa/p/13687648.html