三十而立,心中的架构师梦想依旧滚烫,开始认真研究优秀架构,写点自己的心得和感悟。废话不多说,想要调试Spring源码,首先得搭建环境,使用IDEA和Gradle搭建Spring5源码环境可以参考:使用IDEA+Gradle构建Spring5源码并调试(手把手教程全图解)。

        直接给出Spring5框架的最基础demo示例:

1.新建一个Bean的示例(Student.java),代码如下所示:

@Service
@Component
public class Student {

	private String username = "zhangsan";

	private String password;

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
}

2.在resources目录下,新增spring配置文件(spring.xml),指定Spring扫描的类包。

<?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"
	   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">

	<!--配置扫描文件-->
	<context:component-scan base-package="com.zlp"></context:component-scan>
</beans>

3.新建一个Mytest.java类,来完成逻辑代码的编写。

public class Mytest {
	@Test
	public void test1() {
		ClassPathXmlApplicationContext  applicationContext = new ClassPathXmlApplicationContext("spring.xml");
		Student student = (Student) applicationContext.getBean("student");
		System.out.println(student.getUsername());
	}
}

        以上就是最简单的Spring代码,我们尝试运行一下,会打印以下结果:

>zhangsan

        ☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆

        接下来我们来分析一下,Spring是如何运行的,如何对Bean进行读取和管理的。解析源码前,先放一张类图,方便大家的理解。

Spring源代码 spring5源码_Spring源代码

(1)先看代码第一行。

ClassPathXmlApplicationContext  applicationContext = new ClassPathXmlApplicationContext("spring.xml");

(2)调用ClassPathXmlApplicationContext类中ClassPathXmlApplicationContext(String configLocation)的构造方法

//创建一个新的 ClassPathXmlApplicationContext,从给定的 XML 文件加载定义并自动刷新上下文
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
		this(new String[] {configLocation}, true, null);
	}

(3)实际调用ClassPathXmlApplicationContext类中ClassPathXmlApplicationContext( String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)构造方法

/**
	 * 使用给定的父级创建一个新的 ClassPathXmlApplicationContext,从给定的 XML 文件加载定义。
	 *
	 * 参数:
	 * configLocations – 资源位置数组
	 * refresh – 是否自动刷新上下文,加载所有 bean 定义并创建所有单例。 或者,在进一步配置上下文后手动调用 refresh。
	 * parent – 父上下文
	 */
public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {

		super(parent);
		setConfigLocations(configLocations);
		if (refresh) {
        //-----------------------------------------------------------------
        //注:重中之重,spring IOC的核心方法(涉及到Bean的全生命周期管理)
        //-----------------------------------------------------------------
			refresh();
		}
	}

(4)在(3)中super(parent)方法,调用AbstractApplicationContext类的构造方法

public AbstractApplicationContext(@Nullable ApplicationContext parent) {
		this();
		setParent(parent);
	}

    //this()方法调用此方法
    public AbstractApplicationContext() {
		this.resourcePatternResolver = getResourcePatternResolver();//此处继续调用下面方法
	}
    
    //获取资源模式解析器
	protected ResourcePatternResolver getResourcePatternResolver() {
		return new PathMatchingResourcePatternResolver(this);
	}

(5)在(4)中的PathMatchingResourcePatternResolver(this)方法,调用PathMatchingResourcePatternResolver的构造方法。

public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
        //判断资源加载器是否为空
		Assert.notNull(resourceLoader, "ResourceLoader must not be null");
		this.resourceLoader = resourceLoader;
	}

(6)在(4)中setParent(parent)方法,调用自身(AbstractApplicationContext类)的setParent(@Nullable ApplicationContext parent)方法。

//设置此应用程序上下文的父级。
	//如果父环境为非null且其环境是ConfigurableEnvironment的实例,则父环境将与此(子)应用程序上下文环境合并
	@Override
	public void setParent(@Nullable ApplicationContext parent) {
		this.parent = parent;
		if (parent != null) {
			Environment parentEnvironment = parent.getEnvironment();
			if (parentEnvironment instanceof ConfigurableEnvironment) {
				getEnvironment().merge((ConfigurableEnvironment) parentEnvironment);
			}
		}
	}

(7)在(3)中setConfigLocations(configLocations)方法,调用AbstractRefreshableConfigApplicationContext的setConfigLocations(@Nullable String... locations)方法。

//将配置文件(xml、yaml、properties等)加载到configLocations中,便于后续文件读取和解析
    public void setConfigLocations(@Nullable String... locations) {
		if (locations != null) {
			Assert.noNullElements(locations, "Config locations must not be null");
			this.configLocations = new String[locations.length];
			for (int i = 0; i < locations.length; i++) {
				this.configLocations[i] = resolvePath(locations[i]).trim();
			}
		}
		else {
			this.configLocations = null;
		}
	}

(8)在(3)中refresh()方法,调用自身(AbstractApplicationContext类)的refresh()方法。

@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				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.
				destroyBeans();

				// Reset 'active' flag.
				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()方法的解析,请阅读后续文章。