Spring 3.0之前主要基于xml配置,它的启动流程中,做了些什么?
这里基于Spring5.0.8版本:对ClassPathXmlApplicationContext进行讲解,同时没有集成spring-web包,所以启动过程跳过servlet实现:
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
application.xml中的简单配置:
<bean id="user" class="com.ytq.cloud.source.entity.User">
<property name="name" value="zhangsan" />
<property name="person" ref="person" />
</bean>
<bean id="person" class="com.ytq.cloud.source.entity.Person">
<property name="name" value="lisi"/>
</bean>
application.xml 只配置了一个Bean对象,以及它的属性name
在创建ClassPathXmlApplicationContext对象时,具体会做如下几件事情:
- 确定application.xml的位置,默认去项目路径下找:classpath:/applicaiton.xml
- 通过读取器(xxxReader)读取application.xml文件,将它读取到一个Resource对象中,Resource对象会保存application.xml文件的字节输入流(inputStream);
- xml文件在java中两种主要的解析方式是DOM解析,一个是SAX解析,Spring 中采用了DOM解析,来解析application.xml文件,将application.xml中的每一个解析成AbstractBeanDefinition对象,AbstractBeanDefinition对象中包括Bean的属性,作用域,是否抽象,是否为懒加载等等属性信息;最后将解析的BeanDefinition对象保存到Map对象中(DefaultListableBeanFactory#registerBeanDefinition() 在这个方法中完成)
- 将我们解析的BeanDefinition对象取出,通过反射的方式对单例模式的BeanDefinition进行实例化,并保存在DefaultSingletonBeanRegistry对象的Map容易中(singletonObjects);
这里是启动的主线,中间Spring也做了大量的处理如:BeanFactory的创建于修饰,国际化处理,事件处理(ApplicationEvent),清除缓存等等;
1:资源文件读取与解析
- 从ClassPathXmlApplicationContext对象的构造方法进入:
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
// 会在父类中创建一个资源解析器ResourcePatternResolver(可以理解为先买把刀,为劈柴做准备),它的实现类new PathMatchingResourcePatternResolver(this);
super(parent);
// 解析路径并保存到数组中
setConfigLocations(configLocations);
if (refresh) {
// 启动过程的工作基本在refresh()方法中完成;
refresh();
}}
- 进入refresh()方法,他的实现在AbstractApplicationContext类中,
// 读取application.xml文件,并把它解析成BeanDefinition对象,然后注册到Map容器中;
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 实例化剩余单例模式(非懒加载的)Bean对象
finishBeanFactoryInitialization(beanFactory);
- 进入obtainFreshBeanFactory()->refreshBeanFactory()
// 创建BeanFactory子类实现DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
//加载BeanDefinition
loadBeanDefinitions(beanFactory);进入方法->AbstractXmlApplicationContext#loadBeanDefinitions
方法中代码实现:
reader.loadBeanDefinitions(configLocations); reader就是读取器,configxxx就是配置文件路径;
转换为:XmlBeanDefinitionReader.loadBeanDefinitions(new String[]{"application.xml"});
XmlBeanDefinitionReader是Spring读取配置文件的一个工具类;
进行进入方法,这里有好几层方法,还是重载的loadBeanDefinitions
{
这个方法就是讲配置文件读取到Resource资源对象中,并保存着配置文件字节输入流inputstream
其中resourceLoader资源加载器,就是ClassPathXmlApplicationContext构造方法中创建的PathMatchingResourcePatternResolver对象;然后在调用DefaultResourceLoader的getResource方法;
Resource resource = resourceLoader.getResource(location);
// XmlBeanDefinitionReader对Resource的读取与解析
int count = loadBeanDefinitions(resource);
}
- 进入XmlBeanDefinitionReader对象的loadBeanDefinitions方法,
// 对流进行编码与解码处理
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// 读取流文件;
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
进入doLoadBeanDefinitions方法,这里就是通过DOM方法,对xml流进行解析的过程
Document doc = doLoadDocument(inputSource, resource);
// 通过doc读取里面的每一个element元素,继续进入方法;
int count = registerBeanDefinitions(doc, resource);
进入到->DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions
进入方法->parseBeanDefinitions(root, this.delegate);
- 在parseBeanDefinitions解析方法中
这里的root就是document根节点,获取它所有node节点进行遍历,
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
// 解析element元素节点,如果他是bean开头,就解析他的id,class,name等属性,封装到AbstractBeanDefinition对象中;
parseDefaultElement(ele, delegate);
}}}
- BeanDefinition的解析构成就此完成,最后将解析生成的AbstractBeanDefinition注册到DefaultListableBeanFactory(registerBeanDefinition方法实现注册过程)对象的Map容器中,key就是id定义名称,value就是AbstractBeanDefinition对象,注册的过程中,会判断beanName在同一个上下文中的唯一性,如果beanName重复,注册失败;
2.单例Bean的实例化
- 进入AbstractApplicationContext的refresh()方法的finishBeanFactoryInitialization(beanFactory)方法:
// 初始化所有非延迟加载的Bean实例,这里的BeanFactory就是上传创建的DefaultListableBeanFactory对象;
进入方法->beanFactory.preInstantiateSingletons();
{
//这里就是取上面解析到的所有BeanDefinition的name名称,进行遍历
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
...
// 我们自己定义的普通bean,走这里实例化过程
getBean(beanName);
}}}
进入方法-->AbstractBeanFacotory.doGetBean()方法
{
// 如果是单例模式就创建单例,如果是原型模式就创建原型模式实例,
这里只复制了创建单例代码,
if (mbd.isSingleton()) {
return createBean(beanName, mbd, args);
}}
进入createBean方法
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
- 进入到AbstractAutowireCapableBeanFactory的doCreateBean方法,执行对象的创建于属性依赖注入
// Bean对象的创建,根据beanName获取Class对象,通过Clazz.newInstance(args)创建bean对象;
BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
try {
// 真正的依赖注入处理方法,在依赖注入时,同时会建立对象之间的依赖关系如:(A依赖B,C等),将这种依赖关系保存在Map容器中,key是beanName,Value是set集合依赖的所有对象;
populateBean(beanName, mbd, instanceWrapper);
// Bean初始化方法处理(@PostConstruct注解或者@Bean(init-method="xxx")),同时在初始化之前与之后做些功能扩展,初始化方法会在这里处理;
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
- populateBean(beanName, mbd, instanceWrapper);依赖注入构成实现:
获取bean的所有属性id与配置的属性值value,同时会解析value值的类型,
BeanDefinitionValueResolver对象的resolveValueIfNecessary(pv, originalValue)方法就是判断value值是什么类型的值,value可能set集合,list,map,string,具体对象;
spring提供了强大的类型转换器,将value转换成具体的类型,如果value引用了一个person对象
1. 会先到Map容器中去获取person对象BeanFactory.getBean("person")如果person存在,通过反射调用User的setPerson(Person person)方法,进行设值。setter方法注入
ReflectionUtils.makeAccessible(writeMethod);
writeMethod.invoke(getWrappedInstance(), value);
2. 如果person在IoC容器中不存在,会调用BeanFactory.createBean("person")方法去创建Person对象,然后通过反射调用setPerson()方法,进行注入;
具体实现在BeanWrapperImpl的setValue()方法;
Bean创建完成之后,DefaultSingletonBeanRegistry对象getSingleton方法中,把创建的Bean添加到Map容器中(singletonObjects),这里就是IOC容器管理所有单例Bean的地方;
- 属性注入完成之后,调用initializeBean()初始化方法:
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
...
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// 初始之前通过BeanPostProcessor对Bean做修饰处理
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
// 初始化方法执行,@PostConstruct注解方法或者@Bean(init-method="initxxx")方法
invokeInitMethods(beanName, wrappedBean, mbd);
if (mbd == null || !mbd.isSynthetic()) {
// 初始之后通过BeanPostProcessor对Bean做修饰处理,这里在Spring注解Annotation启动过程中起了很重要的作用,后面将Spring Annotation启动会说
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
Spring IoC容器的初始化过程基本就完成了;
它包括配置文件读取载入,解析,Bean创建,Bean属性注入几个流程;
后面在完善!!!