Spring 启动流程,基于Xml配置启动做了什么?

Spring 3.0之前主要基于xml配置,它的启动流程中,做了些什么?

这里基于Spring5.0.8版本:对ClassPathXmlApplicationContext进行讲解,同时没有集成spring-web包,所以启动过程跳过servlet实现:

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");

application.xml中的简单配置:

    
        
        
    
    
        
    
application.xml 只配置了一个Bean对象,以及它的属性name

在创建ClassPathXmlApplicationContext对象时,具体会做如下几件事情:

  1. 确定application.xml的位置,默认去项目路径下找:classpath:/applicaiton.xml
  2. 通过读取器(xxxReader)读取application.xml文件,将它读取到一个Resource对象中,Resource对象会保存application.xml文件的字节输入流(inputStream);
  3. xml文件在java中两种主要的解析方式是DOM解析,一个是SAX解析,Spring 中采用了DOM解析,来解析application.xml文件,将application.xml中的每一个解析成AbstractBeanDefinition对象,AbstractBeanDefinition对象中包括Bean的属性,作用域,是否抽象,是否为懒加载等等属性信息;最后将解析的BeanDefinition对象保存到Map对象中(DefaultListableBeanFactory#registerBeanDefinition() 在这个方法中完成)
  4. 将我们解析的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属性注入几个流程;

后面在完善!!!

你可能感兴趣的:(Spring 启动流程,基于Xml配置启动做了什么?)