1:前に書く
実際の作業では、次のコードを書くことがよくあります。
@Test
public void testBeanDefinitionLoad() {
// 定义资源
ClassPathResource classPathResource = new ClassPathResource("testbeandefinition.xml");
// 定义IOC容器
DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
// 定义bean定义读取器
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(defaultListableBeanFactory);
// 通过bean定义读取器从资源中读取bean定义
int i = xmlBeanDefinitionReader.loadBeanDefinitions(classPathResource);
System.out.println("bean定义的个数是:" + i);
}
使用する構成ファイルの内容は次のとおりです。
<?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.xsd">
<bean id="testBeanDefinitionBean"
class="yudaosourcecode.spring.TestBeanDefinitionBean"></bean>
<bean id="testBeanDefinitionBean1"
class="yudaosourcecode.spring.TestBeanDefinitionBean"></bean>
<!-- 这里引入自己的话会发生org.springframework.beans.factory.BeanDefinitionStoreException异常 -->
<!--<import resource="testbeandefinition.xml"/>-->
</beans>
このコードは、春にBeanDefinitionとして構成ファイルにBean情報をロードします。これは、Springで定義したBean情報を格納するデータ構造です。SpringBeanの最終生成も、このデータ構造に依存します。最終的な実行結果コードの内容は次のとおりです。
bean定义的个数是:2
返されるのは、構成ファイルで定義されているBeanの数です。
2:loadBeanDefinitions
このメソッドは、構成ファイルでカプセル化されたリソースからbeandefinitionをロードする機能を完了します。具体的な場所は次のとおりです。
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource)
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
// 其中EncodedResource是加了编码的资源,不影响主流程,可以无视
return loadBeanDefinitions(new EncodedResource(resource));
}
続ける:
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.support.EncodedResource)
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
// 断言判断传入的资源不为null
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Loading XML bean definitions from " + encodedResource);
}
// <2021-02-24 12:21>
// 从resourcesCurrentlyBeingLoaded中获取当前线程已经或者是正在加载的资源集合
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
// 如果是不存在已经加载的资源则创建,注意这里是
// 使用的HashSet,因为资源不允许重复
if (currentResources == null) {
currentResources = new HashSet<>(4);
// 存储新创建的资源到resourcesCurrentlyBeingLoaded中
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
// <2021-02-24 12:23>
// 这里如果是资源在set集合中已经存在会返回false,此时代表资源重复
// 直接抛出BeanDefinitionStoreException信息
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
// 获取对应的资源,然后从资源中获取输入流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
// 封装到InputSource中
InputSource inputSource = new InputSource(inputStream);
// 如果有编码则使用,不影响主流程,可以忽略
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// <2021-02-24 12:27>
// 实现具体加载为BeanDefinition的过程
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);
// 如果删除当前资源后,没有其他资源,则清空threadlocal
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
<2021-02-24 12:21>
変数は以下のように定義されるprivate final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded = new NamedThreadLocal<>("XML bean definition resources currently being loaded");
サブクラスNamedThreadLocal
のjava.lang.ThreadLocal
クラスが、名前の属性が追加され、以下のようにソースコードがあります:
public class NamedThreadLocal<T> extends ThreadLocal<T> {
private final String name;
public NamedThreadLocal(String name) {
Assert.hasText(name, "Name must not be empty");
this.name = name;
}
@Override
public String toString() {
return this.name;
}
}
<2021-02-24 12:23>
重要なのは、主に自己紹介の状況に対処するためにリソースが存在するかどうかを判断し、リソース導入の無限ループが発生するようにすることです。それに対処すると、次の状況は無限ループに
なります。次のテストを実行して実行します。コード:
@Test
public void testBeanDefinitionStoreException() {
// 定义资源
ClassPathResource classPathResource = new ClassPathResource("testbeandefinition.xml");
// 定义IOC容器
DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
// 定义bean定义读取器
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(defaultListableBeanFactory);
// 通过bean定义读取器从资源中读取bean定义
xmlBeanDefinitionReader.loadBeanDefinitions(classPathResource);
}
異常動作:
<2021-02-24 12:27>
ソースコードは次のとおりです。
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#doLoadBeanDefinitions
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// <2021-02-24 16:01> 获取xml文件对应的文档对象
Document doc = doLoadDocument(inputSource, resource);
// <2021-02-24 16:02> 根据文档对象注册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) {
...snip...
}
catch (SAXException ex) {
...snip...
}
catch (ParserConfigurationException ex) {
...snip...
}
catch (IOException ex) {
...snip...
}
catch (Throwable ex) {
...snip...
}
}
<2021-02-24 16:01>
ソースコードは次のとおりです。
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
コードgetValidationModeForResource(resource)
は検証モデルを取得するためのものです。詳細については、こちらを参照してください。コードthis.documentLoader.loadDocument
は、XMLファイルに対応するDocumentインスタンスを取得するためのものです。詳細については、こちらを参照してください。<2021-02-24 16:02>
、ドキュメントドキュメント情報に従ってBeanDefinition情報を登録します。詳細については、こちらを参照してください。