三十而立,心中的架构师梦想依旧滚烫,开始认真研究优秀架构,写点自己的心得和感悟。废话不多说,想要调试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进行读取和管理的。解析源码前,先放一张类图,方便大家的理解。
(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()方法的解析,请阅读后续文章。