本文已参与「新人创作礼」活动,一起开启掘金创作之路。
经过前面的手写,IOC的思想大概能明白了吧,今天就从头开始分析源码,看看有哪些出入。
IOC控制反转流程
1.预准备
- spring包spring-framework
- 有两种方式引入
- 直接maven引入spring-framework系列jar包(spring-context,spring-beans。。。)
- 直接引用springboot包的封装,为了方便直接采用这种了
//父包
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.6</version>
</parent>
//web包 后面分析mvc用
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2.项目入口
还是采用XML注入bean的方式为入口,方便和以前文章相通。
- 还记得以前的
applicationContext.xml想要初始化某个类吗,项目和class类就不贴了,主要是分析源码。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
>
<!--项目想要加载的类-->
<bean class="com.example.demo.action.HelloAction" />
</beans>
- main()方法,也就是源码分析的入口
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
}
3.IOC加载时序图
- 简单划分,分为三步,即定位、加载、注册
- IOC流程图:
4.源码分析
4.1 配置阶段
//通过ClassPathXmlApplicationContext的构造方法一直到此方法
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
//存储我们传入的配置文件路径
setConfigLocations(configLocations);
if (refresh) {
//整个IOC容器的入口
refresh();
}
}
4.2 定位
- 核心方法
ClassPathXmlApplicationContext#refresh()
//核心方法 源码里面复制出来的,防止内容过长,简化了
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Tell the subclass to refresh the internal bean factory.
//通过委派模式告诉子类刷新容器 重点去看
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//中间的暂不关注 省略了
......
//实例化所有剩余的(非延迟初始化)单例。依赖注入阶段,下篇文章分析
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
}
}
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//刷新BeanFactory,完成一些东西,让getBeanFactory拿到 接下来走这里
refreshBeanFactory();
return getBeanFactory();
}
- 接下来到
AbstractRefreshableApplicationContext#refreshBeanFactory()
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) { //检查是否存在容器了 有就干掉 spring不能同时存在多个容器
destroyBeans();
closeBeanFactory();
}
try {
//创建容器BeanFactory 并保持一些信息
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
//加载Bean配置信息 重点看
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
//这里详细说一下 createBeanFactory,因为后面会有用到,不然容易晕车
protected DefaultListableBeanFactory createBeanFactory() {
return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}
//实际上是DefaultListableBeanFactory封装了一层 BeanFactory
//后面走BeanFactory时 就应该到DefaultListableBeanFactory
protected BeanFactory getInternalParentBeanFactory() {
return (getParent() instanceof ConfigurableApplicationContext) ?
((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent();
}
- 因为前面传入的是xml文件,接下来到
AbstractXmlApplicationContext#loadBeanDefinitions()
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//创建配置文件读取器,通过构造方法把beanFactory 存进去
//DefaultListableBeanFactory beanFactory是 BeanDefinitionRegistry的实现
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
//设置一系列的配置
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
//保存到上下文中
initBeanDefinitionReader(beanDefinitionReader);
//接下来往这走 重点去看
loadBeanDefinitions(beanDefinitionReader);
}
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
//获取refresh入口setConfigLocations(configLocations);存入的
//获取配置文件的位置
String[] configLocations = getConfigLocations();
if (configLocations != null) {
//接下来往这
reader.loadBeanDefinitions(configLocations);
}
}
- 一直
loadBeanDefinitions往下到XmlBeanDefinitionReader#loadBeanDefinitions,因为前面的Reader是XmlBeanDefinitionReader
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Loading XML bean definitions from " + encodedResource);
}
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
//终于看到读文件了 通过读流的方式,将xml资源文件转为InputStream的IO流
//如果是包路径方式 也是一样的 把对应的包名下的资源文件加载到IO流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//真正的开始进入加载阶段 重点去看
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
4.3 加载
- 从
XmlBeanDefinitionReader#doLoadBeanDefinitions->registerBeanDefinitions->doRegisterBeanDefinitions-> 一直往下到DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions
protected void doRegisterBeanDefinitions(Element root) {
//委派模式 具体解析过程委派给 BeanDefinitionParserDelegate去实现
//BeanDefinitionParserDelegate这里面存了一些默认的key值(bean name id...)
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
//解析xml之前 增强
preProcessXml(root);
//真正的解析过程 重点去看
parseBeanDefinitions(root, this.delegate);
//解析xml之后 增强
postProcessXml(root);
this.delegate = parent;
}
//根据命名空间解析xml元素节点
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//使用了spring默认的命名空间
if (delegate.isDefaultNamespace(root)) {
//获取根元素下的所有子节点
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//获取XML元素节点
if (node instanceof Element) {
Element ele = (Element) node;
//Bean定义的Document的元素节点使用的是Spring默认的XML命名空间
if (delegate.isDefaultNamespace(ele)) {
//使用Spring的Bean规则解析元素节点 重点看这个
parseDefaultElement(ele, delegate);
}
else {
//没有使用Spring默认的XML命名空间,
//则使用用户自定义的解析规则解析元素节点
delegate.parseCustomElement(ele);
}
}
}
}
else {
//Document的根节点没有使用Spring默认的命名空间,则使用用户自定义的
//解析规则解析Document根节点
delegate.parseCustomElement(root);
}
}
//使用Spring的Bean规则解析Document元素节点
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//如果元素节点是<Import>导入元素,进行导入解析 IMPORT_ELEMENT = import
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//如果元素节点是<Alias>别名元素,进行别名解析 ALIAS_ELEMENT = alias
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//元素节点既不是导入元素,也不是别名元素,即普通的<Bean>元素,
//按照Spring的Bean规则解析元素 BEAN_ELEMENT = bean
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
//文章开头是以xml bean形式初始化 重点看这个
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// BeanDefinitionHolder实际上就是是对BeanDefinition的封装
//对Document对象中<Bean>元素的解析由BeanDefinitionParserDelegate实现,上一步传来的
//解析完成并封装成BeanDefinitionHolder
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
//开始走向注册了 接下来看这个
//向Spring IOC容器注册解析得到的Bean定义,这是Bean定义向IOC容器注册的入口
//这个getReaderContext().getRegistry() 就是 new XmlBeanDefinitionReader(beanFactory); 保存的beanFactory
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
//在完成向Spring IOC容器注册解析得到的Bean定义之后,发送注册事件
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
4.4 注册
- 经过前面的读流加载xml,然后解析对应的标签并封装成
BeanDefinition,接下来开始注册BeanDefinitionReaderUtils#registerBeanDefinition
//将解析的BeanDefinitionHolder注册到容器中
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
//获取解析的beanName
String beanName = definitionHolder.getBeanName();
//向IOC容器注册BeanDefinition 接下来走这里
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
//如果解析的BeanDefinition有别名,向容器为其注册别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
- 前面说明过
getReaderContext().getRegistry()就是保存的beanFactory,回顾下
//AbstractRefreshableApplicationContext 下的 refreshBeanFactory
//创建容器BeanFactory 并保持一些信息,然后一直把beanFactory往下传
DefaultListableBeanFactory beanFactory = createBeanFactory();
//然后到这个 AbstractXmlApplicationContext.loadBeanDefinitions
//创建配置文件读取器,并把beanFactory 设置进去
//DefaultListableBeanFactory beanFactory是 BeanDefinitionRegistry的实现
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
//接下来到 DefaultBeanDefinitionDocumentReader.processBeanDefinition
//这个getReaderContext().getRegistry() 就是 new XmlBeanDefinitionReader(beanFactory); 保存的beanFactory
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
- 通过回顾知晓接下来到
DefaultListableBeanFactory#registerBeanDefinition
//中间的一些判断逻辑我们不用关心的就干掉了,简化代码,看起来更直观
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
BeanDefinition oldBeanDefinition;
//获取老的ben注册信息
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
if (oldBeanDefinition != null) {
//如果存在就直接更新
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
//不存在就注册
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
//注册的过程中需要线程同步,以保证数据的一致性
synchronized (this.beanDefinitionMap) {
//添加到保存注册信息的beanDefinitionMap 注册流程到此完结,后面的流程看依赖注入
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
//然后把注册信息的beanName 保存到beanDefinitionNames里面
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
//检查是否有同名的BeanDefinition已经在注册信息的容器中注册
if (oldBeanDefinition != null || containsSingleton(beanName)) {
//重置所有已经注册过的BeanDefinition的缓存
resetBeanDefinition(beanName);
}
}
- 保存bean信息的beanDefinitionMap
/** Map of bean definition objects, keyed by bean name. */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
5.简化流程
5.1 读流找到对应的配置文件
//XmlBeanDefinitionReader.loadBeanDefinitions 下面
//将资源文件转为InputStream的IO流
InputStream inputStream = encodedResource.getResource().getInputStream();
5.2 将XML的<bean>解析成封装类BeanDefinitionHolder
//DefaultBeanDefinitionDocumentReader.processBeanDefinition 下面
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
//BeanDefinitionHolder里面实际是就是BeanDefinition的封装
//beanDefinition 实际是一个接口
//其中的class beanName信息 都在继承BeanDefinition的抽象实现类 AbstractBeanDefinition这里面
private final BeanDefinition beanDefinition;
private final String beanName;
@Nullable
private final String[] aliases;
//接下来向容器注册,调用的是工具类方法
BeanDefinitionReaderUtils.
registerBeanDefinition(bdHolder, getReaderContext().getRegistry())
5.3 将解析得到的封装类BeanDefinitionHold注册到IOC容器
//BeanDefinitionReaderUtils.registerBeanDefinition 下面
// Register bean definition under primary name.
//获取解析的BeanDefinition的beanName
String beanName = definitionHolder.getBeanName();
//向IOC容器注册BeanDefinition
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
//最后到 DefaultListableBeanFactory.registerBeanDefinition 下面
//保存到beanDefinitionMap
this.beanDefinitionMap.put(beanName, beanDefinition);
//beanName 保存到list
this.beanDefinitionNames.add(beanName);
6 本文比较关注的成员变量
//beanName -> class类信息的封装BeanDefinition
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
//beanName的list 实例化用
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
本篇的内容到这里就截止了,到这里可以看到已经将所有的bean标签解析好之后封装成BeanDefinition注册到了IOC容器中,以beanName做为key。但是到目前为止IOC容器并没有将这些解析好的类实例化,我们仍不能直接使用。下一篇我们接着分析
以上就是本章的全部内容了。
上一篇:最详细Java中动态代理分析-- Proxy 下一篇:Spring源码分析第二弹 - DI 依赖注入分析
黑发不知勤学早,白发方悔读书迟