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

以代码流程方式探索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);
	}
}
上一篇:autojs热更新dex


下一篇:spring配置文件加载为BeanDefinition过程分析