ClassPathXmlApplicationContext ctx1 = new ClassPathXmlApplicationContext("spring.xml");
今天看下最原始spring解析spring.xml的流程,虽然现在已经不再使用xml去配置bean,而是使用注解类似@Component,@Service等去处理,但是注解的处理与xml解析有很多相似之处,对xml解析流程清楚之后,那么对注解的解析也就很清楚了,所以今天从最原始的ClassPathXmlApplicationContext流程开始看起。
首先会进入到refresh()方法的obtainFreshBeanFactory方法中,在这个方法中可以看到loadBeanDefinitions方法,这里会将spring.xml中配置的所有标签进行解析成beanDefinition。
创建xmlBeanDefinitionReader对象,并通过reader对象加载配置文件
根据加载的配置文件把配置文件封装成document 对象
创建BeanDefinitionDocumentReader 对象,DocumentReader 负责对document 对象解析
parseDefaultElement(ele, delegate);负责常规标签解析
delegate.parseCustomElement(ele);负责自定义标签解析
最终解析的标签封装成BeanDefinition 并缓存到容器中
先看parseDefaultElement方法,这里会去解析<import>,<bean>,<alias>,<beans>标签,我们点进处理<bean>标签的逻辑
可以看到这里主要是做了两件事情,先是将bean标签解析为beanDefinition,然后将beanDefinition注册到BeanDefinitionRegistry中,后面实例化要用到BeanDefinitionRegistry中的beanDefinition
1、创建GenericBeanDefinition对象
2、解析bean标签的属性,并把解析出来的属性设置到BeanDefinition中
3、解析bean中的meta标签
4、解析中的lookup-method标签
5、解析bean中的replaced-method标签
6、解析bean中的construstor-arg
7、解析bean中的property标签
然后再看下parseCustomElement方法
1、获取自定义标签的namespace 命令空间,例如:
http\://www.springframework.org/schema/context
String namespaceUri = getNamespaceURI(ele);
2、根据命令空间获取NamespaceHandler 对象。
NamespaceUri 和NamespaceHandler 之间会建立一个映射,spring 会从所有的spring
jar 包中扫描spring.handlers 文件,建立映射关系。
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
反射获取NamespaceHandler 实例,并调用init()方法
init()方法中会调用registerBeanDefinitionParser方法,可以看到解析component-scan的是ComponentScanBeanDefinitionParser方法
然后再回到最外面的方法,
然后进入到ComponentScanBeanDefinitionParser的parse方法可以看到,这里先是取出<componet-scan>标签的包路径,然后新创建一个ClassPathBeanDefinitionScanner,执行doScan(basePackages)方法去将bean解析为beanDefinition,然后注册到BeanDefinitionRegistry中
我们继续往doScan方法里面看,可以看到findCadidateComponets方法就是扫描到的所有的包路径下的beanDefinition,最终调用registerBeanDefinition注入到容器中
findCandidateComponets处理逻辑如下
- 首先,通过ResourcePatternResolver获得指定包路径下的所有.class 文件(Spring源码中将
此文件包装成了Resource对象)
-
遍历每个Resource对象
-
利用MetadataReaderFactory解析Resource对象得到MetadataReader(在Spring源码中
MetadataReaderFactory具体的实现类为CachingMetadataReaderFactory,
MetadataReader的具体实现类为SimpleMetadataReader)
- 利用MetadataReader进行excludeFilters和includeFilters,以及条件注解@Conditional的筛选
(条件注解并不能理解:某个类上是否存在@Conditional注解,如果存在则调用注解中所指定
的类的match方法进行匹配,匹配成功则通过筛选,匹配失败则pass掉。)
-
筛选通过后,基于metadataReader生成ScannedGenericBeanDefinition
-
再基于metadataReader判断是不是对应的类是不是接口或抽象类
-
如果筛选通过,那么就表示扫描到了一个Bean,将ScannedGenericBeanDefinition加入结果集
MetadataReader表示类的元数据读取器,主要包含了一个AnnotationMetadata,功能有
-
获取类的名字、
-
获取父类的名字
-
获取所实现的所有接口名
-
获取所有内部类的名字
-
判断是不是抽象类
-
判断是不是接口
-
判断是不是一个注解
-
获取拥有某个注解的方法集合
-
获取类上添加的所有注解信息
-
获取类上添加的所有注解类型集合
值得注意的是,CachingMetadataReaderFactory解析某个.class文件得到MetadataReader对象是
利用的ASM技术,并没有加载这个类到JVM。并且,最终得到的ScannedGenericBeanDefinition对
象,beanClass属性存储的是当前类的名字,而不是class对象。(beanClass属性的类型是Object,
它即可以存储类的名字,也可以存储class对象)
最后,上面是说的通过扫描得到BeanDefinition对象,我们还可以通过直接定义BeanDefinition,或
解析spring.xml文件的<bean/>,或者@Bean注解得到BeanDefinition对象。
以上就是<componet-scan>的处理逻辑。
每一个标签会对应不同的handler,具体要看哪个标签的解析,只需要看对应的handler即可
以上就是spring的xml的解析流程,下个章节继续讲解注解形式的解析