ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

以代码流程方式探索Spring源码(一)--BeanDefiniton解析的准备工作

2021-01-31 15:29:04  阅读:200  来源: 互联网

标签:count Resource beanFactory -- Spring int 源码 loadBeanDefinitions throws


以代码流程方式探索Spring源码--BeanDefinition解析的准备工作

梦开始的地方

这段时间在跟随Jack老师学习Spring源码,在整个的直播学习过程中,收获了很多东西。通过博客的方式把这些记录下来,包括一些学习的笔记和自己的心得,是一种复习、一种整理,也是一种分享。

传统手艺

对于sring项目工程来说,我们的传统手艺就是基于配置文件(也就是我们的xml文件)的方式来生成Bean,虽然现在普遍都在基于注解开发,但是作为一名手艺人,肯定要把传统的手艺学到手。何况在工作过程中,我们不可避免要接触到一些old项目的扩展和改造,这个时候不会传统手艺可就玩不转了!!!

废话不多说,先上呈一份配置文件:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="
	http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
	http://www.springframework.org/schema/aop/spring-aop-3.2.xsd"
    default-lazy-init="false">

    <!--自定义标签-->
    <context:component-scan base-package="com.enjoy.jack">

    </context:component-scan>
    <context:property-placeholder location="classpath:application.properties"/>
    <bean id="propertyConfigurerForProject"
          class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="order" value="1"/>
        <property name="ignoreUnresolvablePlaceholders" value="true"/>
        <property name="location">
            <value>classpath:application.properties</value>
        </property>
    </bean>
    <context:property-placeholder/>
    <aop:aspectj-autoproxy/>
    <import resource="spring.xml"/>
    <bean id="parent" class=""></bean>

    <bean id="student" name="xx,xx1,xx2" class="com.enjoy.jack.bean.Student"  depends-on="james13"/>

    <bean class="com.enjoy.jack.bean.Student" id="student2" p:username="1234" factory-bean="" factory-method="" init-method="" abstract="true" parent="" lazy-init="true" primary="true">
        <description>这个是student</description>
        <meta key="name1" value="Jack"/>
    </bean>

    <bean id="woman" class="com.enjoy.jack.bean.Woman" init-method="init"/>
    <bean id="people" class="com.enjoy.jack.bean.ShowSixClass">
        <lookup-method name="getPeople" bean="woman"></lookup-method>
    </bean>

    <bean id="repalceClass" class="com.enjoy.jack.bean.ReplaceClass"/>
    <bean id="originClass" class="com.enjoy.jack.bean.OriginClass">
        <replaced-method name="method" replacer="repalceClass">
            <arg-type match="java.lang.String"/>
        </replaced-method>
    </bean>

    <bean class="com.enjoy.jack.bean.ConstructorArgBean" id="constructorArgBean">
        <constructor-arg name="username" value="Jack" index="0" type="java.lang.String"/>
        <constructor-arg name="password" value="123" index="1" type="java.lang.String"/>
    </bean>

    <bean class="com.enjoy.jack.bean.PropertyBean" id="propertyBean">
        <property name="username" value="Jack"/>
        <property name="password" value="123"/>
    </bean>

    <bean class="com.enjoy.jack.bean.DecoratorBean" id="decoratorBean"
          p:username="Jack" p:password="123" c:age="12" c:sex="1"/>
    <bean id="factoryMethodbean" class="com.enjoy.jack.bean.FactoryMethodBean"/>
    <bean id="jack" factory-bean="factoryMethodbean" factory-method="factoryMethod"/>

    <bean class="com.enjoy.jack.bean.Jack" id="sdfsaf">
        <property name="cq" ref="CQ"/>
    </bean>

    <bean id="propertiesBean" class="com.enjoy.jack.bean.propertiesbean.PropertiesBean">
        <!--占位符-->
        <property name="name" value="${enjoy.name}"/>
        <property name="password" value="${enjoy.password}"/>
        <aop:scoped-proxy/>
    </bean>
</beans>
</beans>

上述配置文件中展示了一些在spring.xml中一些默认标签和自定义标签的示例,主要是一些bean标签的属性的示例。那Spring是怎么把我们配置文件中的这些标签转化为Spring容器中的bean的呢?大门在下面开启。。。

Spring容器的初始化

ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");

正是上面这行代码开启了Spring容器的魔幻之旅。

一切从一个构造函数说起

public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
	this(new String[] {configLocation}, true, null);
}

public ClassPathXmlApplicationContext(
	String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
		throws BeansException {
	//可追溯到AbstractApplicationContext的构造函数
	super(parent);
	//AbstractRefreshableConfigApplicationContext类中有一个属性String[] configLocations;
	//可一次性传入多个xml文件
	setConfigLocations(configLocations);
	if (refresh) {
		//fresh什么呢?没错,就是我们的IOC容器
		refresh();
	}
}

refresh()、refresh()、refresh()重要的方法说三遍

所属类:AbstractApplicationContext

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// 准备工作,设置一些容器的属性:开始时间、是否关闭、是否激活,创建监听器等
			prepareRefresh();

			// 重点方法,我们所要探索的BeanDefinition的生成就在这个方法中。
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
			prepareBeanFactory(beanFactory);
			try {
				postProcessBeanFactory(beanFactory);
				invokeBeanFactoryPostProcessors(beanFactory);
				registerBeanPostProcessors(beanFactory);
				initMessageSource();
				initApplicationEventMulticaster();
				onRefresh();
				registerListeners();
				finishBeanFactoryInitialization(beanFactory);
				finishRefresh();
			}
			//...省略异常捕获
			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
}

1、obtainFreshBeanFactory()在这个方法中完成了beanFactory的新建和beanDefinition的生成。下面就对这个方法中的具体细节进行探索。

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
	refreshBeanFactory();
	return getBeanFactory();
}

类:AbstractRefreshableApplicationContext
protected final void refreshBeanFactory() throws BeansException {
		if (hasBeanFactory()) {
			destroyBeans();   //如果容器中已经存在了beanFactory,进行bean的销毁和beanFactory的关闭
			closeBeanFactory();
		}
		try {
			//DefaultListableBeanFactory 这个就是我们容器中默认beanFactory的真实子类型
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());、
			
			//在这个自定义beanFactory的方法中,对两个属性进行了设置。
			//1、this.allowBeanDefinitionOverriding
			//2、this.allowCircularReferences
			//他们两个方法的set方法是public类型的,这给我们提供了自定义的接口
			customizeBeanFactory(beanFactory);
			
			//*load 这个方法就是要加载我们的xml中定义的标签进beanFactory
			loadBeanDefinitions(beanFactory);
			this.beanFactory = beanFactory;
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
}

2、loadBeanDefinitions(beanFactory);

先来几波LOAD的转移

类:AbstractXmlApplicationContext
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// 委托模式,定义一个Bean定义的阅读器来进行专属任务处理
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
		
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
		initBeanDefinitionReader(beanDefinitionReader);

		//乾坤大挪移第一转
		loadBeanDefinitions(beanDefinitionReader);
}

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
		Resource[] configResources = getConfigResources();
		if (configResources != null) {
			reader.loadBeanDefinitions(configResources);
		}
		//在这个地方获取到之前set的xml文件的位置和名字
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			//乾坤大挪移第二转,将load的任务交给专属的reader来做。
			reader.loadBeanDefinitions(configLocations);
		}
}

类:AbstractBeanDefinitionReader
//返回值是一个int,这个地方返回的就是加载的beanDefinition的个数
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
        Assert.notNull(locations, "Location array must not be null");
        int count = 0;
        String[] var3 = locations;
        int var4 = locations.length;

        for(int var5 = 0; var5 < var4; ++var5) {
        	//在一个循环中解析xml文件,如果只传过来一个xml文件,这个循环中就执行一次
            String location = var3[var5];
            //乾坤大挪移第三转,针对具体的一个xml文件进行解析
            count += this.loadBeanDefinitions(location);
        }
        return count;
 }

public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
		//乾坤大挪移第四转
        return this.loadBeanDefinitions(location, (Set)null);
}

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
		//这个Loader是我们新建Reader的时候设置进去的
        ResourceLoader resourceLoader = this.getResourceLoader();
        if (resourceLoader == null) {
            throw new BeanDefinitionStoreException("Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
        } else {
            int count;
            if (resourceLoader instanceof ResourcePatternResolver) {
                try {
                	//把字符串类型的xml文件路径,形如:classpath*:user/**/*-context.xml,转换成Resource对象类型,
                	//其实就是用流的方式加载配置文件,然后封装成Resource对象。
                    Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location);
                    //乾坤大挪移第五转
                    count = this.loadBeanDefinitions(resources);
                    if (actualResources != null) {
                        Collections.addAll(actualResources, resources);
                    }
                    if (this.logger.isTraceEnabled()) {
                        this.logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
                    }
                    return count;
            } else {
                Resource resource = resourceLoader.getResource(location);
                //乾坤大挪移并列第五转
                count = this.loadBeanDefinitions((Resource)resource);
                if (actualResources != null) {
                    actualResources.add(resource);
                }
                if (this.logger.isTraceEnabled()) {
                    this.logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
                }
                return count;
            }
        }
}

public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
        Assert.notNull(resources, "Resource array must not be null");
        int count = 0;
        Resource[] var3 = resources;
        int var4 = resources.length;
		//对每一个Resource进行加载
        for(int var5 = 0; var5 < var4; ++var5) {
            Resource resource = var3[var5];
            //乾坤大挪移第六转
            count += this.loadBeanDefinitions((Resource)resource);
        }
        return count;
}

类:XmlBeanDefinitonReader
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
	Assert.notNull(encodedResource, "EncodedResource must not be null");
	if (logger.isTraceEnabled()) {
		logger.trace("Loading XML bean definitions from " + encodedResource);
	}
	Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
	if (!currentResources.add(encodedResource)) {
		throw new BeanDefinitionStoreException(
				"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
	}
	//获取Resource对象中的xml文件流对象
	try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
		//InputSource是jdk中的sax xml文件解析对象
		InputSource inputSource = new InputSource(inputStream);
		if (encodedResource.getEncoding() != null) {
			inputSource.setEncoding(encodedResource.getEncoding());
		}
		//挪移了好几次,终于到了do方法,do方法就是spring中真正做事情的方法。
		return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
	}
	catch (IOException ex) {
		throw new BeanDefinitionStoreException(
				"IOException parsing XML document from " + encodedResource.getResource(), ex);
	}
	finally {
		currentResources.remove(encodedResource);
		if (currentResources.isEmpty()) {
			this.resourcesCurrentlyBeingLoaded.remove();
		}
	}
}

3、doLoadBeanDefinitons()

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
	try {
		//把inputSource 封装成Document文件对象,这是jdk的API
		Document doc = doLoadDocument(inputSource, resource);
		//终于看到了关键词,register,根据解析出来的document对象,拿到里面的标签元素封装成BeanDefinition
		int count = registerBeanDefinitions(doc, resource);
		if (logger.isDebugEnabled()) {
			logger.debug("Loaded " + count + " bean definitions from " + resource);
		}
		return count;
	}
}

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
	//又来一记委托模式,BeanDefinitionDocumentReader委托这个类进行document的解析
	BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
	int countBefore = getRegistry().getBeanDefinitionCount();
	
	//register的转移,
	//createReaderContext(resource) XmlReaderContext上下文,封装了XmlBeanDefinitionReader对象
	documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
	//这个地方返回的是一个差值,自己编写代码的时候可以借鉴下,不要在用临时变量来++操作统计数量了。
	return getRegistry().getBeanDefinitionCount() - countBefore;
}

类:DefaultBeanDefinitionDocumentReader
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
	this.readerContext = readerContext;
	//终于又到了do方法,核心逻辑所在
	doRegisterBeanDefinitions(doc.getDocumentElement());
}

4、doRegisterBeanDefinitons()

protected void doRegisterBeanDefinitions(Element root) {
	BeanDefinitionParserDelegate parent = this.delegate;
	this.delegate = createDelegate(getReaderContext(), root, parent);

	if (this.delegate.isDefaultNamespace(root)) {
		String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
		if (StringUtils.hasText(profileSpec)) {
			String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
					profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
				// We cannot use Profiles.of(...) since profile expressions are not supported
				// in XML config. See SPR-12458 for details.
			if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
				if (logger.isDebugEnabled()) {
					logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
							"] not matching: " + getReaderContext().getResource());
				}
				return;
			}
		}
	}
	preProcessXml(root);
	//load->doLoad->register->doRegister->parse,就问你看累了没???有前置和后置方法,方便扩展
	parseBeanDefinitions(root, this.delegate);
	postProcessXml(root);
	this.delegate = parent;
}

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);
	}
}

标签:count,Resource,beanFactory,--,Spring,int,源码,loadBeanDefinitions,throws
来源: https://blog.csdn.net/wangguodong1993/article/details/113433011

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有