以代码流程方式探索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);
}
}