Spring中BeanFactory解析bean详解
发布时间 - 2026-01-11 00:31:21 点击率:次在该文中来讲讲Spring框架中BeanFactory解析bean的过程,该文之前在小编原文中有发表过,先来看一个在Spring中一个基本的bean定义与使用。
package bean;
public class TestBean {
private String beanName = "beanName";
public String getBeanName() {
return beanName;
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
}
Spring配置文件root.xml定义如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd"> <bean id="testBean" class="bean.TestBean"> </beans>
下面使用XmlBeanFactory来获取该bean:
public class BeanTest {
private static final java.util.logging.Logger logger = LoggerFactory.getLogger(BeanTest.class);
@Test
public void getBeanTest() {
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("root.xml"));
TestBean bean = factory.getBean("testBean");
logger.info(bean.getBeanName);
}
}
这个单元测试运行结果就是输出beanName,上面就是Spring最基本的bean的获取操作,这里我用BeanFactory作为容器来获取bean的操作并不多见,在企业开发中一般是使用功能更完善的ApplicationContext,这里先不讨论这个,下面重点讲解使用BeanFactory获取bean的过程。
现在就来分析下上面的测试代码,看看Spring到底为我们做了什么工作,上面代码完成功能的流程不外乎如此:
1. 读取Spring配置文件root.xml;
2. 根据root.xml中的bean配置找到对应的类的配置,并实例化;
3. 调用实例化后的对象输出结果。
先来看看XmlBeanFactory源码:
public class XmlBeanFactory extends DefaultListableBeanFactory {
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
}
从上面可以看出XmlBeanFactory继承了DefaultListableBeanFactory,DefaultListableBeanFactory是Spring注册加载bean的默认实现,它是整个bean加载的核心部分,XmlBeanFactory与它的不同点就是XmlBeanFactory使用了自定义的XML读取器XmlBeanDefinitionReader,实现了自己的BeanDefinitionReader读取。
XmlBeanFactory加载bean的关键就在于XmlBeanDefinitionReader,下面看看XmlBeanDefinitionReader的源码(只列出部分):
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
private Class<?> documentReaderClass = DefaultBeanDefinitionDocumentReader.class;
private ProblemReporter problemReporter = new FailFastProblemReporter();
private ReaderEventListener eventListener = new EmptyReaderEventListener();
private SourceExtractor sourceExtractor = new NullSourceExtractor();
private NamespaceHandlerResolver namespaceHandlerResolver;
private DocumentLoader documentLoader = new DefaultDocumentLoader();
private EntityResolver entityResolver;
private ErrorHandler errorHandler = new SimpleSaxErrorHandler(logger);
}
XmlBeanDefinitionReader继承自AbstractBeanDefinitionReader,下面是AbstractBeanDefinitionReader的源码(只列出部分):
public abstract class AbstractBeanDefinitionReader implements EnvironmentCapable, BeanDefinitionReader {
protected final Log logger = LogFactory.getLog(getClass());
private final BeanDefinitionRegistry registry;
private ResourceLoader resourceLoader;
private ClassLoader beanClassLoader;
private Environment environment;
private BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator();
}
XmlBeanDefinitionReader主要通过以下三步来加载Spring配置文件中的bean:
1. 通过继承自AbstractBeanDefinitionReader中的方法,使用ResourLoader将资源文件(root.xml)路径转换为对应的Resource文件;
2. 通过DocumentLoader对Resource文件进行转换,将Resource文件转换为Ducument文件;
3. 通过DefaultBeanDefinitionDocumentReader类对Document进行解析,最后再对解析后的Element进行解析。
了解以上基础后,接下来详细分析下一开始例子中的代码:
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("root.xml"));
先看看下面XmlBeanFactory初始化的时序图来进一步了解这段代码的执行,
在这里可以看出BeanTest测试类通过向ClassPathResource的构造方法传入spring的配置文件构造一个Resource资源文件的实例对象,再通过这个Resource资源文件来构造我们想要的XmlBeanFactory实例。在前面XmlBeanFactory源码中的构造方法可以看出,
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
this.reader.loadBeanDefinition(resource)就是资源加载真正的实现,时序图中XmlBeanDefinitionReader加载数据就是在这里完成的。
接下来跟进this.reader.loadBeanDefinition(resource)方法里面,
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
}
在loadBeanDefinition(resource)方法里对资源文件resource使用EncodedResource进行编码处理后继续传入loadBeanDefinitions方法,继续跟进loadBeanDefinitions(new EncodedResource(resource))方法源码:
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
// 通过属性记录已加载的资源
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<EncodedResource>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
// 从resource中获取对应的InputStream,用于下面构造InputSource
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// 调用doLoadBeanDefinitions方法
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
继续跟进doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法,这是整个bean加载过程的核心方法,在这个方法执行bean的加载。
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);
}
/* 省略一堆catch */
}
跟进doLoadDocument(inputSource, resource)源码:
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
在doLoadDocument(inputSource, resource)方法里就使用到了前面讲的documentLoader加载Document,这里DocumentLoader是个接口,真正调用的是其实现类DefaultDocumentLoader的loadDocument方法,跟进源码:
public class DefaultDocumentLoader implements DocumentLoader {
@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isDebugEnabled()) {
logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
}
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
}
}
从源码可以看出这里先创建DocumentBuilderFactory,再用它创建DocumentBuilder,进而解析inputSource来返回Document对象。得到Document对象后就可以准备注册我们的Bean信息了。
在上面的doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法中拿到Document对象后下面就是执行registerBeanDefinitions(doc, resource)方法了,看源码:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
documentReader.setEnvironment(getEnvironment());
// 还没注册bean前的BeanDefinition加载个数
int countBefore = getRegistry().getBeanDefinitionCount();
// 加载注册bean
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 本次加载注册的BeanDefinition个数
return getRegistry().getBeanDefinitionCount() - countBefore;
}
这里的doc就是上面的loadDocument方法加载转换来的,从上面可以看出主要工作是交给BeanDefinitionDocumentReader的registerBeanDefinitions()方法实现的,这里BeanDefinitionDocumentReader是个接口,注册bean功能在默认实现类DefaultBeanDefinitionDocumentReader的该方法实现,跟进它的源码:
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}
到这里通过doc.getDocumentElement()获得Element对象后,交给doRegisterBeanDefinitions()方法后就是真正执行XML文档的解析了,跟进doRegisterBeanDefinitions()方法源码:
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);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
return;
}
}
}
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
到这里处理流程就很清晰了,先是对profile进行处理,之后就通过parseBeanDefinitions()方法进行文档的解析操作,跟进parseBeanDefinitions()方法源码:
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;
// 下面对bean进行处理
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
上面if-else语句块中的parseDefaultElement(ele, delegate)和delegate.parseCustomElement(ele)就是对Spring配置文件中的默认命名空间和自定义命名空间进行解析用的。在Spring的XML配置中,默认Bean声明就如前面定义的:
<bean id="testBean" class="bean.TestBean">
自定义的Bean声明如:
<tx:annotation-driven />
XmlBeanFactory加载bean的整个过程基本就讲解到这里了。希望对大家的学习有所帮助,也希望大家多多支持。
# spring
# beanfactory
# spring的beanfactory
# spring中beanfactory
# Spring扩展BeanFactoryPostProcessor使用技巧详解
# Spring BeanFactory 与 FactoryBean 的区别详情
# Spring源码BeanFactoryPostProcessor详解
# Spring BeanFactory和FactoryBean区别解析
# spring中的BeanFactory与FactoryBean的讲解
# Spring BeanFactory工厂使用教程
# 加载
# 可以看出
# 配置文件
# 自定义
# 是个
# 在这里
# 转换为
# 自己的
# 的是
# 这是
# 文档
# 还没
# 在这个
# 中有
# 它是
# 这段
# 我用
# 下一
# 在上面
# 就来
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
linux写shell需要注意的问题(必看)
如何利用DOS批处理实现定时关机操作详解
详解免费开源的.NET多类型文件解压缩组件SharpZipLib(.NET组件介绍之七)
制作无缝贴图网站有哪些,3dmax无缝贴图怎么调?
教学论文网站制作软件有哪些,写论文用什么软件
?
Laravel怎么实现搜索高亮功能_Laravel结合Scout与Algolia全文检索【实战】
西安专业网站制作公司有哪些,陕西省建行官方网站?
Laravel如何配置和使用队列处理异步任务_Laravel队列驱动与任务分发实例
利用python获取某年中每个月的第一天和最后一天
如何快速搭建二级域名独立网站?
非常酷的网站设计制作软件,酷培ai教育官方网站?
百度输入法ai面板怎么关 百度输入法ai面板隐藏技巧
在线制作视频网站免费,都有哪些好的动漫网站?
简单实现Android验证码
如何在腾讯云免费申请建站?
如何在沈阳梯子盘古建站优化SEO排名与功能模块?
Laravel如何使用withoutEvents方法临时禁用模型事件
使用PHP下载CSS文件中的所有图片【几行代码即可实现】
🚀拖拽式CMS建站能否实现高效与个性化并存?
微信h5制作网站有哪些,免费微信H5页面制作工具?
Android实现代码画虚线边框背景效果
如何用虚拟主机快速搭建网站?详细步骤解析
想要更高端的建设网站,这些原则一定要坚持!
Laravel如何实现本地化和多语言支持_Laravel多语言配置与翻译文件管理
Laravel storage目录权限问题_Laravel文件写入权限设置
如何使用 Go 正则表达式精准提取括号内首个纯字母标识符(忽略数字与嵌套)
如何为不同团队 ID 动态生成多个“认领值班”按钮
阿里云高弹*务器配置方案|支持分布式架构与多节点部署
Laravel如何使用Livewire构建动态组件?(入门代码)
香港服务器建站指南:外贸独立站搭建与跨境电商配置流程
laravel怎么配置Redis作为缓存驱动_laravel Redis缓存配置教程
如何续费美橙建站之星域名及服务?
网站优化排名时,需要考虑哪些问题呢?
HTML透明颜色代码怎么让下拉菜单透明_下拉菜单透明背景指南【技巧】
如何在云主机上快速搭建网站?
浅谈redis在项目中的应用
通义万相免费版怎么用_通义万相免费版使用方法详细指南【教程】
Laravel 419 page expired怎么解决_Laravel CSRF令牌过期处理
专业商城网站制作公司有哪些,pi商城官网是哪个?
JavaScript如何实现错误处理_try...catch如何捕获异常?
制作网站软件推荐手机版,如何制作属于自己的手机网站app应用?
微信小程序制作网站有哪些,微信小程序需要做网站吗?
Laravel如何实现邮件验证激活账户_Laravel内置MustVerifyEmail接口配置【步骤】
Laravel怎么使用Collection集合方法_Laravel数组操作高级函数pluck与map【手册】
百度输入法全感官ai怎么关 百度输入法全感官皮肤关闭
阿里云网站搭建费用解析:服务器价格与建站成本优化指南
Laravel Octane如何提升性能_使用Laravel Octane加速你的应用
齐河建站公司:营销型网站建设与SEO优化双核驱动策略
标题:Vue + Vuex + JWT 身份认证的正确实践与常见误区解析
原生JS获取元素集合的子元素宽度实例

