Escreva na frente
Este artigo continua a análise com base neste artigo . Este artigo analisa principalmente o processo de importação de arquivos de configuração com a tag de importação.
1: Função
A dissociação dos arquivos de configuração reduz a complexidade da gravação do arquivo de configuração e manutenção posterior.
2: código de teste
Para facilitar a depuração e, em seguida, cole o código de teste:
@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:
<?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 id="testBeanDefinitionBean1" class="yudaosourcecode.spring.TestBeanDefinitionBean"/>
<import resource="needimported.xml"/>
</beans>
Introduzido no arquivo de configuração needimported.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.xsd">
<bean id="testBeanDefinitionBean2" class="yudaosourcecode.spring.TestBeanDefinitionBean"/>
</beans>
Em seguida, org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseDefaultElement
atinja os seguintes pontos de interrupção, você pode começar a depurar:
3 : importBeanDefinitionResource
Código fonte:
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#importBeanDefinitionResource
protected void importBeanDefinitionResource(Element ele) {
// 获取import标签的resource属性的值,即需要引入的配置文件的地址
String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
// 如果是resource值为空,直接error,并return
if (!StringUtils.hasText(location)) {
getReaderContext().error("Resource location must not be empty", ele);
return;
}
// <2021-03-01 10:54>
// 解析系统环境变量
location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);
// 最终需要加载的资源的集合
Set<Resource> actualResources = new LinkedHashSet<>(4);
// 是相对路径还是绝对路径?
boolean absoluteLocation = false;
try {
// 1:classpath:,classpath*:是绝对路径
// 2:能够构造url的是绝对路径,如file:/xxxx,http://,jar:// 等资源访问协议定义的路径
absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
}
catch (URISyntaxException ex) {
...snip...
}
// 如果是绝对路径,classpath:,classpath*:,file:/,http:等
// 这里需要区分是相对路径还是绝对路径的原因是,递归加载
// import资源使用的方式和API会有所不同
if (absoluteLocation) {
try {
// 递归导入,注意这里调用的是loadBeanDefinition的另一个重载版本了,不过殊途同归
int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
if (logger.isTraceEnabled()) {
logger.trace("Imported " + importCount + " bean definitions from URL location [" + location + "]");
}
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error(
"Failed to import bean definitions from URL location [" + location + "]", ele, ex);
}
}
// 如果是相对路径,非以classpath:,classpath*:,http://.file:/等开头的路径
// 如果不是绝对路径则默认就是相对于当前资源的相对路径
else {
try {
int importCount;
// 根据当前资源(当前解析的import标签所在资源)的相对路径创建需要import的资源
Resource relativeResource = getReaderContext().getResource().createRelative(location);
// 需要import的相对路径资源存在
if (relativeResource.exists()) {
// 递归调用
importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
// 添加资源
actualResources.add(relativeResource);
}
// 需要import的相对路径资源不存在,理论上相对不存在,这种方式也不会存在,不知道为啥还要执行后续操作
else {
// 获取当前资源基础路径,如file:/E:/workspace-idea/java-life/target/classes/testbeandefinition.xml
String baseLocation = getReaderContext().getResource().getURL().toString();
// <2021-03-01 11:32>
importCount = getReaderContext().getReader().loadBeanDefinitions(
StringUtils.applyRelativePath(baseLocation, location), actualResources);
}
if (logger.isTraceEnabled()) {
logger.trace("Imported " + importCount + " bean definitions from relative location [" + location + "]");
}
}
catch (IOException ex) {
getReaderContext().error("Failed to resolve current resource location", ele, ex);
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error(
"Failed to import bean definitions from relative location [" + location + "]", ele, ex);
}
}
// 加载到的资源转成数据,并触发导入已经处理事件
Resource[] actResArray = actualResources.toArray(new Resource[0]);
getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
}
<2021-03-01 10:54>
É para substituir variáveis de ambiente, como as seguintes variáveis de ambiente:
C:\Users\Administrator>set
ALLUSERSPROFILE=C:\ProgramData
...snip...
Em seguida, modifique o xml para <import resource="${ALLUSERSPROFILE}/needimported.xml"/>
depurar lá, e o valor obtido é o seguinte:
Você pode ver que o valor real da variável de ambiente foi substituído.
<2021-03-01 11:32>
O objetivo é aplicar o caminho relativo com base no caminho absoluto básico para obter o caminho absoluto do caminho relativo. Se o caminho absoluto básico for o file:/E:/workspace-idea/java-life/target/classes/testbeandefinition.xml
caminho relativo needimported1.xml
, o resultado será file:/E:/workspace-idea/java-life/target/classes/needimported1.xml
.
Então, vamos examinar o processo de análise da tag do bean por aqui , porque a tag do bean deve ser resolvida no final.