Spring 3.0之前主要基于xml配置,它的启动流程中,做了些什么?
这里基于Spring5.0.8版本:对ClassPathXmlApplicationContext进行讲解,同时没有集成spring-web包,所以启动过程跳过servlet实现:
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
application.xml中的简单配置:
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
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属性注入几个流程;
后面在完善!!!