Spring中的类加载
Spring进行类加载一般是传入一个配置类或者xml文件,然后进行解析。Spring第一步就是把类封装成BeanDefinition,只有把类信息打包进BeanDefinition后,Spring才能实例化bean。接下来就来研究下Spring是如何生成BeanDefinition对象的。
使用XML加载生成BeanDefinition
spring对xml文件的解析就是对其中标签的解析,解析后生成BeanDefinition。xml的解析入口就是new ClassPathXmlApplication("spring.xml"),进去之后关注refresh方法,构建BeanDefinition的方法就是其中的obtainFreshBeanFactory,如图1:
获取完实例工厂BeanFactory后,调用的customizeFactory中可以设置是否允许循环依赖和是否允许使用相同名称注册不同的bean实现,之后关注loadBeanDefinitions。接下来开始解析,因为涉及代码比较多,会挑一些重点代码:
1、使用委托者模式把BeanFactory委托给XmlBeanDefinitionReader管理,如图2:
2、通过流的方式解析xml文件,并封装成InputSource对象,如图3:
3、通过jdk的dom4j解析文件,并封装成Document对象,如图4:
4、通过createBeanDefinitionDocumentReader方法获取到BeanDefinitionDocumentReader对象;通过createReaderContext方法获取到XmlReaderContext对象;然后调用registerBeanDefinitions方法,如图5:
5、使用parseBeanDefinition开始对传统标签和自定义标签解析。parseDefaultElement方法用于解析传统标签;parseCustomElement方法用于解析自定义标签,如图6:
传统标签解析
以bean标签为例,调用的是processBeanDefinition(从代码中也能看到传统标签其实就四种:import、bean、alias、beans),如图7:
首先通过parseBeanDefinitionElement生成BeanDefinitionHolder,生成该对象需要三个属性:一个是beanName,默认是用id属性;一个是alias别名;一个是最重要的BeanDefinition,通过parseBeanDefinitionElement构建,其中将bean标签的所有属性都注入进了BeanDefinition,如图8:
然后调用registerBeanDefinition,建立beanName和BeanDefinition之间的映射关系,其次调用registerAlias,建立beanName和alias别名之间的映射关系,如图9:
自定义标签解析
通过getNamespaceURI获取xml中自定义标签的namespace命名空间,如图10:
调用handler.parse解析,命名空间的URI和NamespaceHandler之间的映射关系,会从spring源码包里的META-INF文件夹下的spring.handlers文件中找到,比如要解析component-scan标签,parse方法会调用ComponentScanBeanDefinitionParse中的parse方法。spring-context.jar中的spring.handlers,找到ContextNamespaceHandler中的componnet-scan标签对应的实现类,会发现也是ComponentScanBeanDefinitionParse,如图11至图15:
parse方法关注doScan即可,在findCandidateComponents方法里,把类信息用MetadataReader包装后生成BeanDefinition。虽然这是自定义标签,但是processCommonDefinitionAnnotation方法点进去发现还是有对@Lazy、@Primary等注解的支持,表示注解和xml配置是可以共存的。后面就和传统标签一样生成BeanDefinitionHolder,调用registerBeanDefinition就行了,如图16:
使用配置类加载生成BeanDefinition
new AnnotationConfigApplicationConext是配置类加载的入口,虽然里面也有refresh方法,但是和xml文件解析不同,生成BeanDefinition却是在register方法中的。
关注doRegisterBean,在用构造器构建BeanDefinition的时候可以看到同样用了matadata参数把类信息包装了起来:
这边自然而然也用到了processCommonDefinitionAnnotations方法,来对相应注解的支持,后面又回到了BeanDefinitionHolder相关的代码了,如图17:
BeanDefinition的结构
用idea的diagrams工具能很清楚地给我们展示接口和类之间的关系(蓝色实现代表类继承,绿色虚线代表类实现,绿色实现代表接口继承),如图18:
从BeanDefinition的实现类图上可以看到AbstractBeanDefinition实现了BeanDefinition接口,且下面有三个子类,分别是:
ChildBeanDefinition:类似于类继承,可以从父类继承构造的参数值和属性值,并且可以重写父类方法,也可以增加新的属性或者方法。
GenericBeanDefinition:从Spring2.5开始引入的一个注册BeanDefinition更好的类,提供了setParentName来动态定义父依赖,可以代替ChildBeanDefinition绝大部分场合。
RootBeanDefinition:可作为一个通用的BeanDefinition来使用,或者是当成上述两个BeanDefinition的父类来使用。
<bean>标签中的属性值
属性值的含义和使用示例都写在了代码里,后续如果有新的Spring案例也会在这边下载,需要的可以下载:https://github.com/LuoChen1996/my_spring.git