面试官:讲讲Spring IOC!

共 21957字,需浏览 44分钟

 ·

2024-04-10 22:39

来源:juejin.cn/post/7327447632111779891

推荐:https://t.zsxq.com/17Qr4cXwo

IOC(Inversion of Control),即控制反转,它是一种设计思想

  • 控制(谁控制谁)

之前通过new()进行创建对象,主动去创建依赖对象,而现在通过IOC容器负责实例化、配置和组装 bean。

在Spring IOC中,控制反转指的是将对象的创建和管理交给Spring容器来完成,而不是由程序员手动管理。因此,在Spring IOC中,Spring容器控制对象的创建和管理,而不是对象控制容器。因此,可以说在Spring IOC中,Spring容器控制对象。

  • 反转(反转什么)

之前对象主动直接去获取依赖对象,而现在通过Ioc容器查找及注入(DI) 依赖对象。

在Spring IOC中,控制反转(Inversion of Control,IoC)指的是将对象的创建和管理交给Spring容器来完成,而不是由应用程序自己来创建和管理对象。控制反转反转的是控制权,即原本由应用程序控制对象的创建和生命周期,变成了由Spring容器控制对象的创建和生命周期。这种反转的控制权可以带来更好的松耦合、更高的可维护性和可测试性。

  • 依赖注入

DI(Dependency Injection),即依赖注入,是IOC具体的实现,IOC容器动态的将某个依赖注入到对象之中

4bcee39d9af315a08cd9ceda4603131f.webp

Spring Bean 在Spring中,Spring IoC 容器管理的对象称为 bean。

Spring IOC

Spring提供了两种容器:BeanFactoryApplicationContext

BeanFactory 接口提供配置框架和基本功能,能够管理任何类型的对象。

ApplicationContextBeanFactory的子接口

通过上图我们可以看到ApplicationContext还继承了其他接口,增加了消息资源处理(用于国际化)、事件发布、资源访问等功能。

初始化流程

通过前面的内容,我们可以了解到Spring IOC 容器,主要负责管理 Bean 的实例化、配置和组装,接下来我们看看Spring IOC的初始化过程(以xml配置为例)

      
      <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.xsd">


    <bean id="clientMessageReq" class="com.tx.general.rtnp.ws.req.ClientMessageReq" scope="prototype">
    </bean>
</beans>
      
      public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext("clientMessageReq.xml");
    ClientMessageReq clientMessageReq = context.getBean("clientMessageReq", ClientMessageReq.class);
    clientMessageReq.setParams("text");
    System.out.println(JSON.toJSONString(clientMessageReq));
}

通过new ClassPathXmlApplicationContext()方法创建了一个IOC容器,我们主要这个构造方法开始,分析一下IOC容器的初始化过程。

      
      /**
 * 构造一个ClassPathXmlApplicationContext对象。
 *
 * @param configLocations 配置文件路径数组
 * @param refresh 是否刷新上下文
 * @param parent 父上下文对象,可能为空
 * @throws BeansException 如果在创建对象时发生错误,抛出BeansException异常
 */

public ClassPathXmlApplicationContext(
  String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)

  throws BeansException 
{
                
  // 设置Bean资源加载器
 super(parent);
        
  // 设置Xml配置路径
 setConfigLocations(configLocations);
 if (refresh) {
  refresh();
 }
}

重点就是refresh()方法,它是IOC容器初始化的核心

      
      public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

      // ▲1.调用容器准备刷新的方法,获取容器的当时时间,同时给容器设置同步标识 
      prepareRefresh();

      // ▲2.初始化BeanFactory
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // ▲3.准备Bean工厂以在上下文中使用
      prepareBeanFactory(beanFactory);

      try {
         // ▲4.为子类设置BeanFactory的后置处理器
         postProcessBeanFactory(beanFactory);

         StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
         
         // ▲5.调用实现了BeanFactoryPostProcessor的类
         invokeBeanFactoryPostProcessors(beanFactory);

         // ▲6.注册bean初始化时候的processor
         registerBeanPostProcessors(beanFactory);
         beanPostProcess.end();

         // ▲7.对上下文的消息源进行初始化
         initMessageSource();

         // ▲8.初始化上下文的事件机制
         initApplicationEventMulticaster();

         // ▲9.初始化其他特殊的Bean
         onRefresh();

         // ▲10.注册实现了ApplicationListener的listener
         registerListeners();

         // ▲11.初始化配置为lazy-init=false的bean
         finishBeanFactoryInitialization(beanFactory);

         // ▲12.触发所有监听ContextRefreshedEvent事件的listener
         finishRefresh();
      }

      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         destroyBeans();

         cancelRefresh(ex);

         throw ex;
      }

      finally {
        
         resetCommonCaches();
         contextRefresh.end();
      }
   }
}

可以看到容器的创建和初始化就在obtainFreshBeanFactory()方法,重点来看一下

  • obtainFreshBeanFactory()初始化BeanFactory
      
         protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
      refreshBeanFactory();
      return getBeanFactory();
   }

 /**
  * 刷新Bean工厂 委托模式
  */

 @Override
 protected final void refreshBeanFactory() throws BeansException {
           
       // 如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在refresh之后使用的是新建立起来的IoC容器
     if (hasBeanFactory()) {
         destroyBeans();
         closeBeanFactory();
     }
     try {
         // 创建DefaultListableBeanFactory对象
         DefaultListableBeanFactory beanFactory = createBeanFactory();
         // 设置序列化ID
         beanFactory.setSerializationId(getId());
         // 自定义Bean工厂
         customizeBeanFactory(beanFactory);
         // 加载定义的Bean
         loadBeanDefinitions(beanFactory);
         // 将beanFactory赋值给类成员变量
         this.beanFactory = beanFactory;
     }
     catch (IOException ex) {
         // 解析bean定义源时发生I/O错误抛出ApplicationContextException异常
         throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
     }
 }

定位Resource(bean的Xml)

  • AbstractXmlApplicationContext#loadBeanDefinitions() 加载BeanDefinition
      
      protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {

   // 创建XmlBeanDefinitionReader读取器
   XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
   
   beanDefinitionReader.setEnvironment(this.getEnvironment());
   
   // 设置Spring资源加载器
   beanDefinitionReader.setResourceLoader(this);
   beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

   // 读取器读取Bean定义的Xml资源文件时,启用Xml的校验机制
   initBeanDefinitionReader(beanDefinitionReader);

   // 通过beanDefinitionReader加载BeanDefinitions 
   loadBeanDefinitions(beanDefinitionReader);
}
  • AbstractXmlApplicationContext#loadBeanDefinitions() 定位资源,以Resource的形式去加载资源
      
      protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
    
    // 获得Bean配置文件的资源位置
    Resource[] configResources = getConfigResources();    
    if (configResources != null) {
        // 读取AbstractBeanDefinitionReader中定位的资源
        reader.loadBeanDefinitions(configResources);
    }
    // 获取ClassPathXmlApplicationContext构造方法中setConfigLocations方法设置的资源  
    String[] configLocations = getConfigLocations();    
    if (configLocations != null) {
        //通过AbstractBeanDefinitionReader#loadBeanDefinitions()方法,以Resource的形式去加载资源。
        reader.loadBeanDefinitions(configLocations);
    }
}

载入BeanDefinition

经过上面的步骤我们定义的资源以及容器本身需要的资源全部加载到reader中,通过XmlBeanDefinitionReader#loadBeanDefinitions()方法会得到一个XML文件的InputStream,然后 将Bean定义资源转换成Document对象。最后在registerBeanDefinitions()方法中完成对解析

      
      public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
   // 创建DefaultBeanDefinitionDocumentReader对象
   BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
   int countBefore = getRegistry().getBeanDefinitionCount();
   // 通过DefaultBeanDefinitionDocumentReader对象的registerBeanDefinitions()方法解析Document
   documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
   return getRegistry().getBeanDefinitionCount() - countBefore;
}

而具体的解析过程由BeanDefinitionParserDelegate()来实现

      
      protected void doRegisterBeanDefinitions(Element root) {
    // ... 代码部分省略
    
    // 执行XML预处理操作
    preProcessXml(root);

    // 解析Bean定义
    parseBeanDefinitions(root, this.delegate);

    // 执行XML后处理操作
    postProcessXml(root);
    
    // ... 代码部分省略
}

通过BeanDefinitionParserDelegate#parseBeanDefinitionElement()方法,得到结果BeanDefinitionHolderBeanDefinitionHolder是BeanDefinition的封装类,封装了BeanDefinition,Bean的名字、别名)

      
      protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
   // 对给定的<bean>标签进行解析,得到BeanDefinitionHolder对象
   BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
   if (bdHolder != null) {
      bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
      try {
         // 向IOC容器注册解析到BeanDefinition
         BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
      }
      catch (BeanDefinitionStoreException ex) {
         getReaderContext().error("Failed to register bean definition with name '" +
               bdHolder.getBeanName() + "'", ele, ex);
      }
      
     // 在BeanDefinition像IOC容器注册完以后,发送事件消息
      getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
   }
}

BeanDefiniton注册

最终Xml配置被解析成了BeanDefinitionHolder类,通过DefaultListableBeanFactory#registerBeanDefinition()方法,将BeanDefinition注册到beanDefinitionsMap

      
      public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
      throws BeanDefinitionStoreException 
{

   Assert.hasText(beanName, "Bean name must not be empty");
   Assert.notNull(beanDefinition, "BeanDefinition must not be null");

   if (beanDefinition instanceof AbstractBeanDefinition) {
      try {
         ((AbstractBeanDefinition) beanDefinition).validate();
      }
      catch (BeanDefinitionValidationException ex) {
         throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
               "Validation of bean definition failed", ex);
      }
   }

   BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
   if (existingDefinition != null) {
      if (!isAllowBeanDefinitionOverriding()) {
         throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
      }
      else if (existingDefinition.getRole() < beanDefinition.getRole()) {
         // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
         if (logger.isInfoEnabled()) {
            logger.info("Overriding user-defined bean definition for bean '" + beanName +
                  "' with a framework-generated bean definition: replacing [" +
                  existingDefinition + "] with [" + beanDefinition + "]");
         }
      }
      else if (!beanDefinition.equals(existingDefinition)) {
         if (logger.isDebugEnabled()) {
            logger.debug("Overriding bean definition for bean '" + beanName +
                  "' with a different definition: replacing [" + existingDefinition +
                  "] with [" + beanDefinition + "]");
         }
      }
      else {
         if (logger.isTraceEnabled()) {
            logger.trace("Overriding bean definition for bean '" + beanName +
                  "' with an equivalent definition: replacing [" + existingDefinition +
                  "] with [" + beanDefinition + "]");
         }
      }
      this.beanDefinitionMap.put(beanName, beanDefinition);
   }
   else {
      if (hasBeanCreationStarted()) {
         // Cannot modify startup-time collection elements anymore (for stable iteration)
         synchronized (this.beanDefinitionMap) {
            this.beanDefinitionMap.put(beanName, beanDefinition);
            List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
            updatedDefinitions.addAll(this.beanDefinitionNames);
            updatedDefinitions.add(beanName);
            this.beanDefinitionNames = updatedDefinitions;
            removeManualSingletonName(beanName);
         }
      }
      else {
         // Still in startup registration phase
         this.beanDefinitionMap.put(beanName, beanDefinition);
         this.beanDefinitionNames.add(beanName);
         removeManualSingletonName(beanName);
      }
      this.frozenBeanDefinitionNames = null;
   }

   if (existingDefinition != null || containsSingleton(beanName)) {
      resetBeanDefinition(beanName);
   }
   else if (isConfigurationFrozen()) {
      clearByTypeCache();
   }
}

至此,IOC容器完成了初始化,其内部使用ConcurrentHashMap(Spring IOC容器本质)保存了BeanDefinition信息

总结

Spring IOC初始化可以分为三个步骤

  • 通过 ResourceLoader 来完成资源文件位置的定位(Resource)
  • 将Resource定位好的资源载入到BeanDefinition(通过 BeanDefinitionReader来完成定义信息的解析)
  • 将BeanDefiniton注册到容器中(通过BeanDefinitionRegistry接口,将BeanDefinition保存到ConcurrentHashMap中)

Spring IOC容器管理了定义的各种Bean对象及其相互的关系,降低了耦合度

浏览 21
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报