Spring源码系列-2-整体debug

本文深入剖析Spring源码,从创建ClassPathXmlApplicationContext开始,讲解refresh函数的10个核心步骤,包括bean定义信息的加载、bean工厂的准备、bean的实例化和初始化等关键流程,旨在帮助读者理解Spring框架的工作原理。

上一篇我们整体概述Spring的源码,这一篇我们来整体debug过一遍Spring的源码。

Spring源码前置知识

  1. Java基础,反射、代理等
  2. 熟练使用Spring
  3. 熟悉常见的设计模式,包括但不限于工厂、代理、适配、观察者模式、责任链等
  4. 熟悉常见的数据结构
  5. 懂多线程与JVM更好

如何读源码

  1. 优先把握整体脉络,不要太专注细节,先整体后部分
  2. 必看注释(类、接口、方法)
  3. 学会见名知意,源码的类、方法命名比较规范
  4. 大胆猜测,小心验证
  5. 必须画图,时序图、结构图、总结图
  6. 坚持!坚持!坚持!

搭建源码调试环境

估计搭建Spring源码的调试环境会难住部分人。这里我收集几个成功搭建的案例供大家参考。

  1. 使用 IDEA 快速搭建 Spring 的源码环境

  2. Spring源码分析环境搭建

整体debug

spring源码预览.png

简单的demo

使用上一篇提供的简单例子进行调试。

// Bean类
public class Student {
   
   
    private int id;
    private String name;
    private static String gender = "男";
	//  省略getter/setter
}

// xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean class="com.mashibing.Student" id="student">
        <property name="id" value="1"></property>
        <property name="name" value="zhangsan"></property>
    </bean>
</beans>

// 启动类        
public class MyTest {
   
   
    public static void main(String[] args) {
   
   
        ApplicationContext context = new ClassPathXmlApplicationContext("my-test.xml");
        Student student = context.getBean(Student.class);
        System.out.println("姓名 " + student.getName());
    }
}        

spring启动入口

  1. 创建一个新的ClassPathXmlApplicationContext,从xml文件中加载给定的bean定义信息,并且刷新上下文。

    image-20210527133630620

    执行的ClassPathXmlApplicationContext构造函数,尤其注意注意里面的一个函数refresh(),这个函数是通过启动spring的所有核心流程的入口。

    image-20210527091451620

    为什么说这个refresh函数是spring启动的核心入口?

    • refresh定义在AbstractApplicationContext类中

    • 查看一下这个抽象类的子类,注意2个比较典型的ClassPathXmlApplicationContextAnnotationConfigApplicationContext

      image-20210527093336425

    • 无论是通过xml配置文件启动启动,还是通过注解扫描启动,都会由这个抽象类的refresh()方法进入spring的核心流程。

  2. 继续调试进入比较核心的入口方法AbstractApplicationContext#refresh(),里面有10几个方法,夸张点说掌握了里面10个方法,大体就弄懂了spring的源码。每个方法大体作用,在下面的代码做了注释,可以参照看,我们在如何读源码也强调了一定要学会看注释。后续我们进入每个方法看一下大体作用。

    	@Override
    	public void refresh() throws BeansException, IllegalStateException {
         
         
    		synchronized (this.startupShutdownMonitor) {
         
         
    			// Prepare this context for refreshing.
    			/**
    			 * 1 做容器刷新前的准备工作
    			 */
    			prepareRefresh();
    
    			// Tell the subclass to refresh the internal bean factory.
    			//2 创建容器对象:DefaultListableBeanFactory
    			// 加载xml配置文件的属性值到当前工厂中,最重要的就是BeanDefinition
    			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
    			// Prepare the bean factory for use in this context.
    			//3 beanFactory的准备工作,对各种属性进行填充
    			prepareBeanFactory(beanFactory);
    
    			try {
         
         
    				// Allows post-processing of the bean factory in context subclasses.
    				//4 子类覆盖方法做额外的处理,此处我们自己一般不做任何扩展工作,但是可以查看web中的代码,是有具体实现的
    				postProcessBeanFactory(beanFactory);
    
    				// Invoke factory processors registered as beans in the context.
    				// 5 调用各种beanFactory处理器
    				invokeBeanFactoryPostProcessors(beanFactory);
    
    				// Register bean processors that intercept bean creation.
    				// 6 注册bean处理器,这里只是注册功能,真正调用的是getBean方法
    				registerBeanPostProcessors(beanFactory);
    
    				// Initialize message source for this context.
    				// 7 为上下文初始化message源,即不同语言的消息体,国际化处理,在springmvc的时候通过国际化的代码重点讲
    				initMessageSource();
    
    				// Initialize event multicaster for this context.
    				// 8 初始化事件监听多路广播器
    				initApplicationEventMulticaster();
    
    				// Initialize other special beans in specific context subclasses.
    				// 9 留给子类来初始化其他的bean
    				onRefresh();
    
    				// Check for listener beans and register them.
    				// 10 在所有注册的bean中查找listener bean,注册到消息广播器中
    				registerListeners();
    
    				// Instantiate all remaining (non-lazy-init) singletons.
    				// 11 初始化剩下的单实例(非懒加载的)
    				finishBeanFactoryInitialization(beanFactory);
    
    				// Last step: publish corresponding event.
    				// 12 完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人
    				finishRefresh();
    			}
    
    			catch (BeansException ex) {
         
         
    				if (logger.isWarnEnabled()) {
         
         
    					logger.warn("Exception encountered during context initialization - " +
    							"cancelling refresh attempt: " + ex);
    				}
    
    				// Destroy already created singletons to avoid dangling resources.
    				// 为防止bean资源占用,在异常处理中,销毁已经在前面过程中生成的单件bean
    				destroyBeans();
    
    				// Reset 'active' flag.
    				// 重置active标志
    				cancelRefresh(ex);
    
    				// Propagate exception to caller.
    				throw ex;
    			}
    
    			finally {
         
         
    				// Reset common introspection caches in Spring's core, since we
    				// might not ever need metadata for singleton beans anymore...
    				resetCommonCaches();
    			}
    		}
    	}
    

refresh函数详解

prepareRefresh()

做容器刷新前的准备工作

  • 1、设置容器的启动时间
  • 2、设置活跃状态为true
  • 3、设置关闭状态为false
  • 4、获取Environment对象,并加载当前系统的属性值到Environment对象中
  • 5、准备监听器和事件的集合对象,默认为空的集合

第4,5点你会发现用到上一篇提到的2个重要的接口Environment和监听器ApplicationListener

protected void prepareRefresh() {
   
   
   // Switch to active.
   // 设置容器启动的时间
   this.startupDate = System.currentTimeMillis();
   // 容器的关闭标志位
   this.closed.set(false);
   // 容器的激活标志位
   this.active.set(true);

   // 留给子类覆盖,初始化属性资源
   initPropertySources();

   // 创建并获取环境对象,验证需要的属性文件是否都已经放入环境中
   getEnvironment().validateRequiredProperties();

   // Store pre-refresh ApplicationListeners...
   // 判断刷新前的应用程序监听器集合是否为空,如果为空,则将监听器添加到此集合中
   if (this.earlyApplicationListeners == null) {
   
   
      this.earlyApplicationListeners <
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值