【挑战 Spring】—– Spring IOC 源码调试一

前言

使用Spring已经很久了,一直想对源码去窥探窥探,拖了又拖、等了又等。自从在Listener的第一个夜晚之后,我决定不能在等了,在等万一Spring被淘汰了呢(开个有点认真的玩笑)?所以来个挑战Spring系列,对源码细粒度调试以及解读。虽说不敢挑战全网之最细粒度跟踪,但是也是尽可能的细节了。

项目结构:

先看下调试代码,即Spring的入口,就是使用的ClassPathXmlApplicationContext类去加载配置文件

开始调试:

1.加载AbstractApplicationContext类的静态代码块

断点进到new ClassPathXmlApplicationContext(location)中后,第一步是加载AbstractApplicationContext类的静态代码块。这段代码,我也不不清楚作用是什么,官方注释的意思是先加载ContextClosedEvent类以避免在WebLogic 8.1中关闭应用程序时出现奇怪的类加载器问题。但是不影响主流程。( 这块也隐藏了一个小知识点就是父类的静态代码块执行顺序优先于子类的有参构造方法)

2.执行ClassPathXmlApplicationContext构造方法

  1. 第一步会进到ClassPathXmlApplicationContext类的有参构造方法中。其中参数就是xml文件的位置classpath*:META-INF/spring-ioc.xml
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
   this(new String[] {configLocation}, true, null);
}
复制代码
  1. 第二步进入到ClassPathXmlApplicationContext另一个构造方法中,并且层层往上调用父类构造方法**super(parent)【从第一步得parent参数为null】**,直到调用到AbstractApplicationContext 类为止。其中调用顺:ClassPathXmlApplicationContext-->AbstractXmlApplicationContext-->AbstractRefreshableConfigApplicationContext-->AbstractRefreshableApplicationContext-->AbstractApplicationContext 这也体现了ClassPathXmlApplicationContext的一个继承关系
public ClassPathXmlApplicationContext(
      String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
      throws BeansException {
   super(parent);
   setConfigLocations(configLocations);
   if (refresh) {
      // 调用父类 AbstractApplicationContext#refresh() 的方法
      refresh();
   }
}
复制代码

3.执行AbstractApplicationContext 类的构造方法

public AbstractApplicationContext() {
   this.resourcePatternResolver = getResourcePatternResolver();
}
public AbstractApplicationContext(@Nullable ApplicationContext parent) {
   this();
   setParent(parent);
}
复制代码

实例化AbstractApplicationContext对象的时候,可以看到构造方法中执行了两个操作,第一个是getResourcePatternResolver()实例化ResourcePatternResolver;第二个是setParent(parent)

1.实例化ResourcePatternResolver对象

getResourcePatternResolver()方法中可以看到返回的对象是PathMatchingResourcePatternResolver

protected ResourcePatternResolver getResourcePatternResolver() {
   return new PathMatchingResourcePatternResolver(this);
}
复制代码
2.简单说一下PathMatchingResourcePatternResolver

PathMatchingResourcePatternResolver的顶级接口是ResourceLoader简单说就是来加载我们的资源文件的(比如:classpath*:META-INF/spring-ioc.xml)

到此之后代码又会回到AbstractApplicationContext 类的构造方法中

4.回到ClassPathXmlApplicationContext构造方法

**super(parent)**方法完成后,此时父类对象实例化完成,但是ClassPathXmlApplicationContext对象还未实例化完成。下面代码中可以看到后面还有 setConfigLocations(configLocations)refresh()两个方法。

public ClassPathXmlApplicationContext(
      String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
      throws BeansException {
   super(parent);
    // 调用父类 AbstractRefreshableConfigApplicationContext 的 setConfigLocations()方法
   setConfigLocations(configLocations);
   if (refresh) {
      // 调用父类 AbstractApplicationContext#refresh() 的方法
      refresh();
   }
}
复制代码
1.执行AbstractRefreshableConfigApplicationContextsetConfigLocations(@Nullable String... locations)

因为ClassPathXmlApplicationContext中没有setConfigLocations(configLocations)方法,该方法被封装在父类AbstractRefreshableConfigApplicationContext中,代码如下:

在代码中可以看到有这样一段逻辑this.configLocations[i] = resolvePath(locations[i]).trim()意思就是把我们配置资源路径维护进一个数组中。下面我们说一下这个resolvePath(String path)方法。

2.执行AbstractRefreshableConfigApplicationContextresolvePath(String path)
protected String resolvePath(String path) {
   return getEnvironment().resolveRequiredPlaceholders(path);
}
复制代码

resolvePath(String path)方法中有两个操作,一个是getEnvironment(),一个是resolveRequiredPlaceholders(path)方法

  • getEnvironment()方法

其中getEnvironment()方法又被封装在AbstractApplicationContext类中(如果这儿继承关系有点可以乱看下前面说ClassPathXmlApplicationContext构造方法调用顺序),在该方法中实例化了一StandardEnvironment对象并且将该对象赋值给ConfigurableEnvironment environment属性,就是容器的运行环境应用上下文对象,这个对象可以读取配置文件(SpringBoot项目中我们一般会经常用这个对象)

  • resolveRequiredPlaceholders(path)方法

    其中getEnvironment()方法我们得到了一个ConfigurableEnvironment类型对象,真正的实现类是StandardEnvironment。所以会执行StandardEnvironmentresolveRequiredPlaceholders(path)方法,而该方法有封装在了父类AbstractEnvironment中,代码如下:

AbstractEnvironment中我们看到又有这么一段逻辑this.propertyResolver.resolveRequiredPlaceholders(text),返回值是一个String类型(我日,真是头大的不行!),意思就是又委托给this.propertyResolver去执行了。那我们看一下这个this.propertyResolver是什么:

可以看到是直接 new 了一个PropertySourcesPropertyResolver对象,该对象是一个资源解析器,该对象的顶级接口是PropertyResolver。我理解的就是用来解析Properties配置文件的,里面定义了很多读取配置文件的API。

那好代码接着往下走可以看this.propertyResolver.resolveRequiredPlaceholders(text)到返回值就是我们配置的资源文件路径

3.回到AbstractRefreshableConfigApplicationContextsetConfigLocations(@Nullable String... locations)方法

因为只配了一个资源文件,所以this.configLocations[i] = resolvePath(locations[i]).trim()这段代码执行完后,this.configLocations的长度为1,并且里面的值为我们资源文件的路径

4.再次回到ClassPathXmlApplicationContext构造方法

因为AbstractRefreshableConfigApplicationContextsetConfigLocations(@Nullable String... locations)方法是在ClassPathXmlApplicationContext类的构造方法调用的,所以执行完之后再次回到ClassPathXmlApplicationContext的构造方法中。代码如下:

public ClassPathXmlApplicationContext(
      String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
      throws BeansException {
   super(parent);
    // 调用父类 AbstractRefreshableConfigApplicationContext 的 setConfigLocations()方法
   setConfigLocations(configLocations);
   if (refresh) {
      // 调用父类 AbstractApplicationContext#refresh() 的方法
      refresh();
   }
}
复制代码

这时候往下执行就是大名鼎鼎的refresh()方法由于篇幅原因,refresh()方法在下一篇博客中更新。

欢迎扫码关注

如果喜欢请关注我公众号【程序倾听者】,说出你的故事!我在这里倾听!
文章原文地址

猜你喜欢

转载自juejin.im/post/5ec5455551882543464b00f3