1、Spring DI xml配置文件方式解析原理

本文深入探讨了Spring依赖注入(DI)通过XML配置文件的工作原理,重点解析了beanDefinitionParserDelegate如何处理XML标签及其属性,揭示了Spring框架在加载和解析配置文件时的内部机制。
摘要由CSDN通过智能技术生成

前几天看了Spring DI部分的源码,今天写个简单的博客记录自己的学习路程。
首先准备解析的xml文件和java代码:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
<bean id="test1" class="com.test.springdemo.Test1"></bean>
</beans>

public class Work {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("application.xml");
MyBean bean=(MyBean)context.getBean("test1");
bean.dowork();
}
}

可以看到xml文件配置的很简单,接下来就根据该配置文件的解析一步步地探究Spring容器是怎么进行加载bean的。
一:ApplicationContext context=new ClassPathXmlApplicationContext("application.xml");
进入ClassPathXmlApplicationContext内部,可以看到以下代码:

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh,
ApplicationContext parent) throws BeansException {
super(parent);
//xml配置文件记录在context中
setConfigLocations(configLocations);
if (refresh) {
//创建并刷新上下文,上下文中存入beandefinition
refresh();
}
}

refresh()就是一个重要的方法,调用了父类AbstractApplicationContext中的refresh()方法:
源码如下:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//准备刷新的上下文环境
prepareRefresh();

//初始化BeanFactory,并进行xml文件的读取
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

//对BeanFactory进行各种功能填充,其实就是对BeanFactory中的各种属性填充值
prepareBeanFactory(beanFactory);

try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);

// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);

// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);

// Initialize message source for this context.
initMessageSource();

// Initialize event multicaster for this context.
initApplicationEventMulticaster();

// Initialize other special beans in specific context subclasses.
onRefresh();

// Check for listener beans and register them.
registerListeners();

// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);

// Last step: publish corresponding event.
finishRefresh();
}

catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}

// Destroy already created singletons to avoid dangling resources.
destroyBeans();

// Reset 'active' flag.
cancelRefresh(ex);

// Propagate exception to caller.
throw ex;
}
}
}
1、首先重点分析 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
该方法会创建默认的defaultListableBeanFactory,并进行xml文件的读取解析,注册BeanDefinition。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//创建defaultListableBeanFactory,记录在Context内,并进行xml文件的读取(加载
BeanDefinition),加载@Autowire和@Qualifier解析器
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
调用父类AbstractRefreshableApplicationContext的 refreshBeanFactory()方法:
protected final void refreshBeanFactory() throws BeansException {
//判断context中beanfactory是否已经存在,若已经存在,则将beanfactory中的
beandefinition清空并关闭beanfactory(其实就是销毁beanfactory)
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//创建beanfactory,继承父context中的父beanfactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
//设置@Autowire和@Qualifier注解解析器QualifierAnnotationAutowireResolver
customizeBeanFactory(beanFactory);
//初始化DocumentReader,进行对xml文件的读取,加载beandefinition
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for
" + getDisplayName(), ex);
}
}
调用子类AbstractXmlApplicationContext中的loadBeanDefinitions(beanFactory)方法:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws
BeansException, IOException {
//创建XmlBeanDefinitionReader
XmlBeanDefinitionReader beanDefinitionReader = new
XmlBeanDefinitionReader(beanFactory);

beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
initBeanDefinitionReader(beanDefinitionReader);
//进行xml的读取和beandefinition的注册
loadBeanDefinitions(beanDefinitionReader);
}
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
//获取context中注册的配置文件路径
String[] configLocations = getConfigLocations();
if (configLocations != null) {
//加载xml配置文件
reader.loadBeanDefinitions(configLocations);
}
}
进入AbstractBeanDefinitionReader执行loadBeanDefinitions(String... locations)方法:
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
int counter = 0;
for (String location : locations) {
//根据xml配置文件依次加载beandefinition
counter += loadBeanDefinitions(location);
}
//返回加载beandefinition数目
return counter;
}
在loadBeanDefinitions(String location)中主要做了两件事:
根据配置文件路径转化为resource,对resource进行解析和注册BeanDefinition
回到子类XmlBeanDefinition中执行loadBeanDefinitions(Resource resource)方法,并将解析注册工作委托给了
doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
int validationMode = getValidationModeForResource(resource);
Document doc = this.documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode,
isNamespaceAware());
return registerBeanDefinitions(doc, resource);
}catch...
..........
....
}
在该方法内,getValidationModeForResource(resource)会根据文件中是否存在DOCTYPE字符串来获取xml文件的校验模式(dtd或xsd),
getEntityResolver()方法会根据xml校验模式尝试获取本地的dtd或xsd文件,避免网络下载的耗时;
该方法最主要的功能是将resource转化为Document格式,可以根据Document节点进行依次解析。

解析注册功能委托给registerBeanDefinitions(doc, resource)方法进行:
根据反射创建DefaultBeanDefinitionDocumentReader实例,调用其registerBeanDefinitions(Document doc, XmlReaderContext readerContext)方法:
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
//获取document中的根节点
Element root = doc.getDocumentElement();
//根据根节点root进行解析注册
doRegisterBeanDefinitions(root);
}

protected void doRegisterBeanDefinitions(Element root) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec,
BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!getEnvironment().acceptsProfiles(specifiedProfiles)) {
return;
}
}

                    //beanDefinitionParserDelegate实例用来进行标签的实际解析工作,其中存在多个标签属性

                       BeanDefinitionParserDelegate parent = this.delegate;

this.delegate = createDelegate(this.readerContext, root, parent);

preProcessXml(root);
//将根节点解析,转化为BeanDefinition并进行注册
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);

this.delegate = parent;
}
查看parseBeanDefinitions方法:
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//查看root节点是否是默认命名空间,分配不同的解析方法
if (delegate.isDefaultNamespace(root)) {
//获取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);
}
}
在这里,主要分析parseDefaultElement(ele,delegate)方法,因为parseCustomElement方法是针对自命名空间进行解析的,如<context:component-scan base="com.test.myspring"/>;

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
//导入其他配置xml文件,进行解析
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
//别名注册,解析<alias/>标签
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
                        //针对<bean/>标签解析
                        processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
//对子标签<beans/>进行递归解析,知道解析注册完成
doRegisterBeanDefinitions(ele);
}
}
从上述方法中,可以看到processBeanDefinition方法是最核心的方法, importBeanDefinitionResource和doRegisterBeanDefinitions最终都会循环调用parseDefaultElement或parseCustomElement方法;
processBeanDefinition会根据bean标签/节点进行解析注册:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//解析bean标签,创建beanDefinitioin中,并存入BeanDefinitionHolder中
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
//处理标签
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
//实现注册功能
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,
getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
//注册监视器,spring默认情况下监视器不做任何处理,开发人员可按照业务自行配置
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
1、delegate.parseBeanDefinitionElement(ele)
调用BeanDefinitionParserDelegate的parseBeanDefinitionElement(Element ele)方法:
a、获取xml中bean的id和name
b、name被解析为别名aliases[String],
c、若id没有配置,则将name配置中的第一项(即alises的第一个别名)设置为id,并移除该别名
d、将bean id和别名aliases存放在BeanDefinitionParserDelegate的usedNames缓存中
e、AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
创建beanDefinitioni对象(GenericBeanDefinition类型),进行各种属性的填充,如bean id,class属性,
构造器constructor属性(<constructor>),setter属性(<property>)等。
f、若bean id仍然没有(bean id和name都没有设置),则按照默认规则进行设置bean id,设置为类名首字母
小写
g、根据beanDefinition创建beanDefinitionHolder
2、BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
a、将新建的beanDefinitionHolder中的beanDefinition中取出,存放至 defaultListableBeanFactory中一个
map中,bean name(id)-beanDefinition键值对形式存储。
b、注册别名



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值