记录一下学习Spring源码 基于Spring5.1.3RELEASE
一.基础代码
1.基础的项目目录截图如下
2.bean.xml
<bean class="com.lixiao.stydy.spring.domain.Student" id="lixiao">
<property name="name" value="李校"></property>
<property name="age" value="18"></property>
<property name="father" value="爸爸"></property>
<property name="mother" value="妈妈"></property>
</bean>
3.main方法
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student lixiao = context.getBean("lixiao", Student.class);
System.out.println(lixiao.getName());
二、源码分析
1.ClassPathXmlApplicationContext 构造器
/*
参数说明:1.String[] configLocations 这个是一个配置数组 例如"beans.xml"
2.boolean refresh 是否refresh 默认是true
3.@Nullable ApplicationContext parent 可以为空的父容器
*/
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh,
@Nullable ApplicationContext parent)
throws BeansException {
/**
* 如果有父容器就先执行这个方法,核心是一个合并方法,把父类的配置全部赋值到当前容器中
* 会先放全部的配置PropertySources 再放被active的配置ActiveProfiles 再放默认配置
* DefaultProfiles
* 作用可以参考这里 https://www.cnblogs.com/jason0529/p/6567373.html
*/
super(parent);
/**1.处理占位符,例如spring${username}.xml 他会从系统环境和jvm环境中取值,如果windows
* 系统填username那最后解析出来的就是你当前计算机的用户名称,例如我的是李校 C:\Users\李校
* 2.加载当前的操作系统环境和JVM环境
* 可以参考这里https://blog.csdn.net/huzhiliayanghao/article/details/115256071
*/
setConfigLocations(configLocations);
if (refresh) {
//最核心的方法,把这个搞明白Spring应该就清楚了
refresh();
}
}
2.refresh()方法
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
/**
准备refresh工作
1.记录启动时间为当前时间,记录启动状态为true,记录关闭状态为false
2.打印日志
3.放一个初始化环境资源的空方法,留给子类覆盖
使用参考:https://blog.csdn.net/u013277209/article/details/109177452
4.创建并获取环境对象,验证需要的属性文件是否都已经放入环境中 这个就配合3使用,如果
使用了3 启动之后如果没读取到3的配置这里就会抛出异常
5.创建刷新前的监听事件集合
*/
prepareRefresh();
/*
刷新并获取beanFactory
1.如果存在beanFactory就先销毁beanFactory
2.new一个DefaultListableBeanFactory
3.设置beanFactior的序列号ID为当前容器对象的信息
例如@asdvb 就是@+16进制的hashCode
4.留一个扩展 用来设置 是否允许覆盖同名称的不同定义的对象,
是否允许bean之间的循环依赖。
参考:https://blog.csdn.net/u013277209/article/details/109183125
5.读取配置文件的bean的name,放到beanDefinitionsName这个list里面去
读取配置文件的bean完整信息放到beanDefinitionsMap 这个map里面去
*/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
/*
准备beanFactory
1.设置类加载器默认是当前容器的类加载器,根据类加载器设置表达式解析器默认是#{},
设置属性注册编辑器(读取xml里面bean的属性,当某些格式不支持读取的时候可以
自定义扩展),
可以参考
https://blog.csdn.net/cuichunchi/article/details/90407632
2.给postProcesser添加aware和广播事件
3.忽略一些自动装配接口
4.添加自动装配的类
5.如果当前BeanFactory包含loadTimeWeaver Bean,说明存在类加载期织入AspectJ,
则把当前BeanFactory交给类加载期BeanPostProcessor实现类
LoadTimeWeaverAwareProcessor来处理,从而实现类加载期织入AspectJ的目的。
6.注册系统配置bean environment,systemProperties,systemEnvironment
*/
prepareBeanFactory(beanFactory);
try {
//空方法,用来改变beanDefinitionsMap里面的bean信息,例如单例原型,属性值等
//可以参考这个链接:
//https://blog.csdn.net/caihaijiang/article/details/35552859
postProcessBeanFactory(beanFactory);
//执行实现了BeanFactoryPostProcessor接口的方法,就是上一步所添加的扩展点
invokeBeanFactoryPostProcessors(beanFactory);
//执行实现了BeanPostProcessor接口的方法
registerBeanPostProcessors(beanFactory);
//初始化国际化的东西
initMessageSource();
//初始化当前容器的事件广播器,如果用户定义了就用用户的,用户没定义就用默认的
//就是if判断当前beanFactory
//beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)
initApplicationEventMulticaster();
//扩展点 子类重写这个方法,在容器刷新的时候可以自定义逻辑;
//如创建Tomcat,Jetty等WEB服务器
//参考:https://www.cnblogs.com/grasp/p/11942735.html
onRefresh();
//注册实现了ApplicationListener接口的监听器bean到
//ApplicationEventMulticaster里面
registerListeners();
// 初始化所有剩下的非懒加载的单例bean
finishBeanFactoryInitialization(beanFactory);
//完成context的刷新。主要是调用LifecycleProcessor的onRefresh()方法,
//并且发布事件(ContextRefreshedEvent)
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();
}
}
}