likes
comments
collection
share

从简单的配置文件开始,重新审视Spring的上下文环境

作者站长头像
站长
· 阅读数 79

思考,输出,沉淀。用通俗的语言陈述技术,让自己和他人都有所收获。 作者:毅航😜


在接下来很长的一段时间内笔者将开始对Spring源码进行深入分析并根据源码仿写一个简化版的Spring,在这一过程中笔者将以理论+实践的方式来讲述Spring源码,即先分析框架的实现,再自己模拟实现,双管齐下真正意义上攻克Spring框架源码。感兴趣的话不妨点个关注,现在上车,以后就是老司机啦~~~

前言

相信对于Java开发者而言,日常开发接触最多的框架一定是Spring。就是这样一个每天都接触的框架你有深入研究它过吗?或许,你对于Spring框架源码的深入研究都集中在求职面试期间,每次都在快面试的时候临时抱一抱佛脚,面试完就开始将相关内容束之高阁。然后,每次面试都不断重复这一过程。

所以,为了避免每次都进行这样无效的学习,不妨跟着笔者的思路来对重新对Spring框架源码进行一次重新梳理,坚持跟完笔者本专栏的内容,相信你一定会对Spring框架源码有一个更深层的认识!

从配置文件开始重新认识Spring

SpringBoot的出现,极大的简化了Spring上手难度,日常开发中通过几个简单的注解就能将类信息注入到容器中。或许,你已经忘却原生的Spring该如何使用。接下来,不妨跟着笔者思路来对Spring的原生用法进行一个简单的回顾。

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans">   
    
    <bean id="car" class="com.spring.bean.Car"></bean>
</beans>    

(注:为了节省篇幅,上述内容中的xmlns内容进行了省略~)

正如上述applicationContext.xml配置文件所示,在Spring中你可以使用XML配置文件来定义Bean。进一步,你可以在应用程序中通过如下代码来加载配置文件,并获取相关的bean

public class MyApplicationContext {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        // 从容器中获取Bean
        Car car = context.getBean("car", Car.class);

        // 使用car对象的相关方法
        car.doSomething();
    }
}

看到上述代码,是不是会有种恍若隔世的感觉?毫无疑问,每个学习Spring的初学者一定写过上述代码,其就如学习编程语言时写的Hello Word一样,这是Spring界的Hello Word

走进ClassPathXmlApplicationContext

不知你是否考过这样一个问题,上述简单的三行代码到底做了哪些工作呢?初学者的角度来看,其无非做了如下两件事

  1. 通过new关键字构建了一个类型ClassPathXmlApplicationContextcontext变量;
  2. 调用ClassPathXmlApplicationContext相关API,获取一个类型为Car.class的变量,然后,访问其内部方法。

进一步,那如果我要你ClassPathXmlApplicationContext内部完成了那些工作你能回答上来吗?回答上来也不要着急,不妨跟着笔者思路来进行分析!

首先,我们来看ClassPathXmlApplicationContext的构造方法。此时,你可能会疑惑,为啥一上来要看ClassPathXmlApplicationContext的构造方法?答案其实很简单,因为我们会通过new关键字来构造一个ClassPathXmlApplicationContext的实例对象。


public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
   this(new String[] {configLocation}, true, null);
}

// <2>
public ClassPathXmlApplicationContext(
      String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
      throws BeansException {
   // 调用父类构造
   super(parent);
   // 设定配置文件路径信息
   setConfigLocations(configLocations);
   // 刷新容器操作
   refresh();
   
}

可以看到,在其内部最终会调用<2>处所示的构造方法,那不妨来看其会完成哪些工作:

  1. 首先,不断向上调用父类的构造器。具体来看,此处最终会初始化一个ResourcePatternResolver的成员变量,该类提供了一种查找和加载资源的强大机制,主要用于处理类路径下的资源和基于通配符的资源查找。你可以简单理解为此处通过构造器来初始化一个资源加载器,进而方便后续配置文件的加载。而有关资源加载的内容,笔者会在后续会进行分析!

  2. 设定配置文件路径信息,此处也很好理解,既然配置了一个资源加载器,那必然需要知道要到何处去加载配置信息,这也是<2>处代码主要完成的工作。

  3. 容器刷新

点进refresh方法后,你会看到如下内容:

public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
     
      prepareRefresh();
      // 构架容器
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

     // .... 省略其他无关代码
     
  
}

出于篇幅考虑,此处笔者略去了refresh中调用的十多个方法。本次我们先简单看一下obtainFreshBeanFactory的逻辑。这部分调用逻辑如下:

从简单的配置文件开始,重新审视Spring的上下文环境

(注:重点关注调用逻辑!请忽略掉相关容器名称,这个后期笔者会单独分析)

不难发现,其最终会调用AbstractRefreshableApplicationContext中的refreshBeanFactory方法

protected final void refreshBeanFactory() throws BeansException {
   
      // 构建一个beanFactory
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      // 加载bean定义
      loadBeanDefinitions(beanFactory);
     
}

可以看到refreshBeanFactory主要完成如下两点工作:

  1. 创建Bean工厂:首先,refreshBeanFactory方法会创建一个DefaultListableBeanFactory实例,它是Spring容器的核心部分。DefaultListableBeanFactory用于注册Bean定义、解析依赖关系以及管理Bean的生命周期。
  2. 加载XML配置文件loadBeanDefinitions方法接受一个BeanFactory然后根据之前配置路径信息加载配置文件并进行解析,进一步,解析出的bean都将存放于传入的BeanFactory之中。

此处,笔者就不展开分析loadBeanDefinitions的内部逻辑了,其内部逻辑一言以概之无非就是 解析配置文件,加载配置bean相关信息。

至此,我们来对ClassPathXmlApplicationContext进行一个简单总结,其无非就是Spring框架中用于从类路径(Classpath) 加载XML配置文件并初始化bean的一个上下文。

总结

经过上述分析,那如果这时我这样问你,你能通过自己编码的方式来实现一个ClassPathXmlApplicationContext吗?

你心里肯定会想这有啥难的呢!通过文章之前的分析,利用xml解析结合HashMap就能实现。具体来看,将配置文件中的bean信息进行解析,解析完毕后通过反射构建一个bean,然后将bean存放到一个HashMap到时候用的时候去Map结构中去取不就完了吗?

通过上述的分析,你觉得Spring还难吗?我想答案肯定是否定的!那能独立实现上述需求吗?我想肯定可以啦!没有思路也别着急,笔者将在下一遍亲自操刀来实现,到时候不妨跟着笔者的思路来仿写一个!

转载自:https://juejin.cn/post/7288964017350672425
评论
请登录