解析XML获取Bean
Posted 为人师表好少年
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了解析XML获取Bean相关的知识,希望对你有一定的参考价值。
一、基础代码
Spring
加载bean实例的代码
public static void main(String[] args) throws IOException
// 1.获取资源
Resource resource = new ClassPathResource("bean.xml");
// 2.获取BeanFactory
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// 3.获取资源的解析器
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
// 4.装载资源
reader.loadBeanDefinitions(resource);
// 5.获取Bean实例
Book book = factory.getBean(Book.class);
System.out.println(book);
这段代码是 Spring 中编程式使用 IoC 容器,通过这四段简单的代码,我们可以初步判断 IoC 容器的使用过程。整个过程就分为三个步骤:
资源定位。我们一般用外部资源来描述 Bean 对象,所以在初始化 IoC 容器的第一步就是需要定位这个外部资源。
装载。装载就是 BeanDefinition
的载入。BeanDefinitionReader
读取、解析 Resource 资源,也就是将用户定义的 Bean 表示成 IoC 容器的内部数据结构:BeanDefinition
。
注册。向 IoC 容器注册在第二步解析好的 BeanDefinition
,这个过程是通过BeanDefinitionRegistry
接口来实现的。在 IoC 容器内部其实是将第二个过程解析得到的BeanDefinition
注入到一个HashMap
容器中,IoC 容器就是通过这个HashMap
来维护这些BeanDefinition
的。
1)在这里需要注意的一点是这个过程并没有完成依赖注入(Bean 创建),Bean 创建是发生在应用第一次调用getBean()
方法,向容器索要 Bean 时。
2)当然我们可以通过设置预处理,即对某个 Bean 设置lazyinit = false
属性,那么这个 Bean 的依赖注入就会在容器初始化的时候完成。
简单点说,上面步骤的结果是:XML Resource -> XML Document -> Bean Definition
二、BeanDefinition
上面讲完了介绍到我们解析的Bean
标签会封装成BeanDefinition
,那么接下来看一下
1.基础介绍
org.springframework.beans.factory.config.BeanDefinition
,是一个接口,它描述了一个 Bean 实例的定义,包括属性值、构造方法值和继承自它的类的更多信息。代码如下:
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement
// 其中方法就不粘贴了,太多了
2.BeanDefinition 的父关系
BeanDefinition
继承 AttributeAccessor
和 BeanMetadataElement
接口。两个接口定义如下:
org.springframework.cor.AttributeAccessor
接口,定义了与其它对象的(元数据)进行连接和访问的约定,即对属性的修改,包括获取、设置、删除。代码如下:
public interface AttributeAccessor
void setAttribute(String name, @Nullable Object value);
@Nullable
Object getAttribute(String name);
@Nullable
Object removeAttribute(String name);
boolean hasAttribute(String name);
String[] attributeNames();
org.springframework.beans.BeanMetadataElement
接口,Bean 元对象持有的配置元素可以通过 getSource()
方法来获取。代码如下:
public interface BeanMetadataElement
@Nullable
default Object getSource()
return null;
3.BeanDefinition 的子关系
我们常用的三个实现类有:
org.springframework.beans.factory.support.ChildBeanDefinition
org.springframework.beans.factory.support.RootBeanDefinition
org.springframework.beans.factory.support.GenericBeanDefinition
ChildBeanDefinition
、RootBeanDefinition
、GenericBeanDefinition
三者都继承AbstractBeanDefinition
抽象类,即AbstractBeanDefinition
对三个子类的共同的类信息进行抽象。- 如果配置文件中定义了父 和 子 ,则父 用
RootBeanDefinition
表示,子 用ChildBeanDefinition
表示,而没有父 的就使用RootBeanDefinition
表示。 GenericBeanDefinition
为一站式服务类。
三、调用链
1.loadBeanDefinitions() 加载
加载BeanDefinition
// XmlBeanDefinitionReader XML解析类
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException
// 将Resource资源封装成EncodedResource,封装的原因是为了进行编码,保证内容读取的正确性
return loadBeanDefinitions(new EncodedResource(resource));
// XmlBeanDefinitionReader XML解析类
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();
// 将当前资源加入记录中,如果已存在,则抛出异常
// 主要为了避免一个EncodedResource在加载时,还没加载完成,又加载自身,从而导致死循环
if (!currentResources.add(encodedResource))
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
// 获取输入流
try (InputStream inputStream = encodedResource.getResource().getInputStream())
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null)
// 设置编码
inputSource.setEncoding(encodedResource.getEncoding());
// **核心逻辑部分,执行加载 BeanDefinition
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();
-> doLoadBeanDefinitions()
// XmlBeanDefinitionReader XML解析类
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException
try
// 获取XML Document实例
Document doc = doLoadDocument(inputSource, resource);
// 根据 Document 实例,注册 Bean 信息
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled())
logger.debug("Loaded " + count + " bean definitions from " + resource);
return count;
catch (BeanDefinitionStoreException ex)
throw ex;
catch (SAXParseException ex)
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
catch (SAXException ex)
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"XML document from " + resource + " is invalid", ex);
catch (ParserConfigurationException ex)
throw new BeanDefinitionStoreException(resource.getDescription(),
"Parser configuration exception parsing XML from " + resource, ex);
catch (IOException ex)
throw new BeanDefinitionStoreException(resource.getDescription(),
"IOException parsing XML document from " + resource, ex);
catch (Throwable ex)
throw new BeanDefinitionStoreException(resource.getDescription(),
"Unexpected exception parsing XML document from " + resource, ex);
2.doLoadDocument()
将XML
文件解析成Docuemnt
private DocumentLoader documentLoader = new DefaultDocumentLoader();
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
// DefaultDocumentLoader 默认的文档加载器
@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isTraceEnabled())
logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
3.registerBeanDefinitions() 开始注册
注册BeanDefinition
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException
// <1>创建对象BeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// <2>获取已注册的BeanDefinition数量
int countBefore = getRegistry().getBeanDefinitionCount();
// <3>创建XmlReaderContext
// <4>注册新的Bean
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// <5>两个相减,返回注册成功的数量
return getRegistry().getBeanDefinitionCount() - countBefore;
-> registerBeanDefinitions()
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
-> doRegisterBeanDefinitions()
该方法特殊处理profile
属性,然后进行解析
protected void doRegisterBeanDefinitions(Element root)
// 记录老的 BeanDefinitionParserDelegate 对象
BeanDefinitionParserDelegate parent = this.delegate;
// 创建 BeanDefinitionParserDelegate 对象,并进行设置到 delegate
// *** 它负责解析Document的各种对象
this.delegate = createDelegate(getReaderContext(), root, parent);
// 检查 <beans /> 根标签的命名空间是否为空,或者是 http://www.springframework.org/schema/beans
if (this.delegate.isDefaultNamespace(root))
// 处理profile属性
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec))
// 使用分隔符切分,可能有多个 profile 。
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// 如果所有 profile 都无效,则不进行注册
// 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);
// 解析
parseBeanDefinitions(root, this.delegate);
// 解析后处理, 空实现
postProcessXml(root);
this.delegate = parent;
protected void preProcessXml(Element root)
protected void postProcessXml(Element root)
4.parseBeanDefinitions() 分类解析
下面代码中会区分2类,
- 默认命名空间,例如:
<bean id="book" class="com.h3c.ss.Book"></bean>
- 自定义注解方式,例如:
<tx:annotation-driven>
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)
// <1>如果根节点使用默认命名空间,执行默认解析
if (delegate.isDefaultNamespace(root))
// 遍历子节点,也就是XML文件中的内容
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
//<2> 根节点非默认命名空间,执行自定义解析
delegate.parseCustomElement(root);
-> parseDefaultElement() 解析默认命名空间
public static final String BEAN_ELEMENT = "bean";
public static final String IMPORT_ELEMENT = "import";
public static final String ALIAS_ATTRIBUTE = "alias";
public static final String NESTED_BEANS_ELEMENT = "beans";
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)
// 解析import标签
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT))
importBeanDefinitionResource(ele);
// 解析alias
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT))
processAliasRegistration(ele);
// 解析bean
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT))
processBeanDefinition(ele, delegate);
// 解析嵌套的bean
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT))
// recurse
doRegisterBeanDefinitions(ele);
-> parseCustomElement() 自定义解析
5.importBeanDefinitionResource() 解析import标签
6.processBeanDefinition() 解析bean标签
解析工作分为三步:
- 解析默认标签。
- 解析默认标签后下得自定义标签。
- 注册解析后的 BeanDefinition 。
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)
// <1> 如果解析成功,则返回 BeanDefinitionHolder 对象。而 BeanDefinitionHolder 为 name 和 alias 的 BeanDefinition 对象
// 如果解析失败,则返回 null 。
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null)
// <2> 解析默认标签后下得自定义标签
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try
// <3> 进行 BeanDefinition 的注册
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
catch (BeanDefinitionStoreException ex)
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
-> 第1步:解析beanName
- 解析
id
、name
属性,确定aliases
集合 - 检测
beanName
是否唯一 - 对属性进行解析并封装成
AbstractBeanDefinition
实例beanDefinition
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele)
return parseBeanDefinitionElement(ele, null);
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean)
// 获取Id 和 name
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
// 解析name
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr))
// name属性可以存在多个,可以以不同的分隔符间隔,这里就是用于拆分的工具类
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS
2、FactoryBean:是一个特殊的bean,具有工厂生成对象的能力,只能生成特定的对象。bean必须使用 FactoryBean接口,此接口提供方法 getObject() 用于获得特定bean。
<bean id="" class="FB"> 先创建FB实例,使用调用getObject()方法,并返回方法的返回值
FB fb = new FB();
return fb.getObject();
BeanFactory 和 FactoryBean 对比?
BeanFactory:工厂,用于生成任意bean。
FactoryBean:特殊bean,用于生成另一个特定的bean。例如:ProxyFactoryBean ,此工厂bean用于生产代理。
<bean id="" class="....ProxyFactoryBean"> 获得代理对象实例,AOP使用。
二、作用域
作用域:用于确定Spring创建Bean的实例个数。
取值:
singleton 单例,是默认值。
prototype 多例,每执行一次getBean将获得一个实例。例如:struts整合spring,配置action多例。
配置信息:
<bean id="" class="" scope="">
例如:
<bean id="userServiceId" class="com.zju.scope.UserServiceImpl" scope="prototype"></bean>
例1:singleton 单例
UserService.java
package com.zk.myspring;
public interface UserService {
public void addUser();
}
UserServiceImpl.java
package com.zk.myspring;
public class UserServiceImpl implements UserService{
@Override
public void addUser() {
// TODO Auto-generated method stub
System.out.println("UserService");
}
}
ApplicationContext.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"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!-- 默认scope为singleton -->
<bean id="userserviceId" class="com.zk.myspring.UserServiceImpl" scope="singleton"></bean>
</beans>
TestDemo.java
package com.zk.myspring;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestDemo {
@Test
public void demo1(){
String xmlpath="ApplicationContext.xml";
ApplicationContext ac=new ClassPathXmlApplicationContext(xmlpath);
UserService us=ac.getBean("userserviceId", UserServiceImpl.class);
UserService us2=ac.getBean("userserviceId", UserServiceImpl.class);
//us.addUser();
//us2.addUser();
System.out.println(us);
System.out.println(us2);
System.out.println(us==us2);
}
}
运行效果图:
例2:prototype 多例
UserService.java
package com.zk.myspring;
public interface UserService {
public void addUser();
}
UserServiceImpl.java
package com.zk.myspring;
public class UserServiceImpl implements UserService{
@Override
public void addUser() {
// TODO Auto-generated method stub
System.out.println("UserService");
}
}
TestDemo.java
package com.zk.myspring;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestDemo {
@Test
public void demo1(){
String xmlpath="ApplicationContext.xml";
ApplicationContext ac=new ClassPathXmlApplicationContext(xmlpath);
UserService us=ac.getBean("userserviceId", UserServiceImpl.class);
UserService us2=ac.getBean("userserviceId", UserServiceImpl.class);
//us.addUser();
//us2.addUser();
System.out.println(us);
System.out.println(us2);
System.out.println(us==us2);
}
}
ApplicationContext.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"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<bean id="userserviceId" class="com.zk.myspring.UserServiceImpl" scope="prototype"></bean>
</beans>
运行效果图:
以上是关于解析XML获取Bean的主要内容,如果未能解决你的问题,请参考以下文章