• 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的注册。

  • 相关阅读:
    九度OJ 1154:Jungle Roads(丛林路径) (最小生成树)
    九度OJ 1153:括号匹配问题 (DP)
    九度OJ 1152:点菜问题 (01背包、DP)
    九度OJ 1151:位操作练习 (位操作)
    数论——素数算法
    wubi安装ubuntu-12.04.3
    快速FQ
    linux下的软硬链接区别
    死锁问题总结
    windows下用XShell远程ubuntu时连接失败
  • 原文地址:https://www.cnblogs.com/juniorMa/p/13687648.html
Copyright © 2020-2023  润新知