Spring XML 解析要完成的任务是将 XML 中的标签解析成 BeanDefinition 注册到 BeanFactory 中。
以 ClassPathXmlApplicationContext 为例,上述工作在 obtainFreshBeanFactory() 中完成,分为:
- 创建 BeanFactory
- SAX 解析 XML
- 标签解析,向容器注册 Bean
- 默认标签解析
- 自定义标签解析
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
1. 创建 BeanFactory
创建 DefaulListableBeanFactory,并设置 allowBeanDefinitionOverriding、allowCircularRefrences
2. SAX 解析 XML
以下面这种方式加载配置文件时,涉及到的对象、类包括:BeanDefinitionReader、configLocations、Resource、InputSource、DocumentBuilderFactory、DocumentBuilder、Document。
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
- 创建 BeanDefinitionReader
- 获取 XML 配置文件
- 封装成 Resource
- 获取流,并封装成 InputSource
- SAX 解析
3. 标签解析
涉及到的对象、类包括:BeanDefinitionDocumentReader。
- 创建 BeanDefinitionDocumentReader
- 分类解析:默认标签、自定义标签。判断标准是:默认标签命名空间是 http://www.springframework.org/schema/beans
3.1 默认标签解析
默认标签包括 import、alias、bean、beans,如下
各标签使用示例:
<import resource="customerContext.xml"/>
<alias name="testBean" alias="testBean,testBean2"/>
<!-- 给 bean 添加别名还有一种方式,使用 bean 标签 -->
<bean id="testBean" name="beanName1,beanName2" class="TestBean"/>
<beans>
<beans> </beans>
</beans>
最重要的是 bean 标签,包括:
- 属性:id、name、scope、abstract、lazy-init、autowire、depends-on、autowire-candidate、init-method、destory-method、factory-method、factory-bean
- 子标签:lookup-method、replaced-method、constructor-arg、property
下面分析 bean 标签解析步骤:
- 创建 GenericBeanDefinition
- 解析属性
- 解析子标签
子标签使用示例:
<!-- lookup-method -->
<bean id="aPeople" class="com.xmustang.bean.APeople" lazy-init="false"></bean>
<bean id="showLookUp" class="com.xmustang.bean.ShowLookUp">
<lookup-method name="getPeople" bean="aPeople"></lookup-method>
</bean>
public abstract class ShowLookUp {
public void showName() {
getPeople().getName();
}
public abstract IPeople getPeople();
}
<!-- replaced-method -->
<bean id="replaceClass" class="com.mustang.bean.ReplaceClass" lazy-init="false"/>
<bean id="originClass" class="com.mustang.bean.OriginClass">
<replaced-method name="say" replacer="replaceClass">
<!--方法可能出现重载的情况,要根据类型和方法名找方法-->
<arg-type match="java.lang.String"/>
</replaced-method>
</bean>
public class ReplaceClass implements MethodReplacer {
@Override
public Object reimplement(Object obj, Method method, Object[] args) {
System.out.println("I am replace method-->reimplement!");
return null;
}
}
<!-- constructor-arg -->
<bean class="com.mustang.bean.ConstructorArgBean" id="constructorArgBean">
<constructor-arg name="username" value="Tom" index="0" type="java.lang.String"/>
<constructor-arg name="password" value="123" index="1" type="java.lang.String"/>
</bean>
<!-- property -->
<bean class="com.mustang.bean.PropertyBean" id="propertyBean">
<property name="username" value="Jerry"/>
<property name="password" value="123"/>
</bean>
- BeanDefinition 封装成 BeanDefinitionHolder
- 注册 bean
注册 bean、别名
相关的容器有:DefaultListableBeanFactory.beanDefinitionMap、DefaultListableBeanFactory.beanDefinitionNames;SimpleAliasRegistry.aliasMap
3.2 默认标签解析
- 加载 .handlers 文件,获取所有 NamespaceHandler
- 根据自定义标签 namespaceUri 获取对应的 NamespaceHandler
- 调用 NamespaceHandler.init() 注册解析器
- 调用解析器的 parse() 解析标签
3.2.1 context:component-scan 标签解析
context:component-scan 是自定义标签,它的 NamespaceHandler、BeanDefinitionParser 是 ContextNamespaceHandler、ComponentScanBeanDefinitionParser。
直接从 parse() 开始看:
- 扫描包
- 过滤包含 @Component
- 填充 BeanDefinition
- 封装成 BeanDefinitionHolder,注册
注册与默认标签表示的 bean 注册相同。