Spring源码分析第一弹 - IOC控制反转分析

127 阅读7分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

经过前面的手写,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流程图: 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 依赖注入分析

黑发不知勤学早,白发方悔读书迟