<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          面試官:講講Spring IOC!

          共 21957字,需瀏覽 44分鐘

           ·

          2024-04-10 22:39

          來源:juejin.cn/post/7327447632111779891

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

          IOC(Inversion of Control),即控制反轉(zhuǎn),它是一種設計思想

          • 控制(誰控制誰)

          之前通過new()進行創(chuàng)建對象,主動去創(chuàng)建依賴對象,而現(xiàn)在通過IOC容器負責實例化、配置和組裝 bean。

          ?

          在Spring IOC中,控制反轉(zhuǎn)指的是將對象的創(chuàng)建和管理交給Spring容器來完成,而不是由程序員手動管理。因此,在Spring IOC中,Spring容器控制對象的創(chuàng)建和管理,而不是對象控制容器。因此,可以說在Spring IOC中,Spring容器控制對象。

          • 反轉(zhuǎn)(反轉(zhuǎn)什么)

          之前對象主動直接去獲取依賴對象,而現(xiàn)在通過Ioc容器查找及注入(DI) 依賴對象。

          ?

          在Spring IOC中,控制反轉(zhuǎn)(Inversion of Control,IoC)指的是將對象的創(chuàng)建和管理交給Spring容器來完成,而不是由應用程序自己來創(chuàng)建和管理對象。控制反轉(zhuǎn)反轉(zhuǎn)的是控制權,即原本由應用程序控制對象的創(chuàng)建和生命周期,變成了由Spring容器控制對象的創(chuàng)建和生命周期。這種反轉(zhuǎn)的控制權可以帶來更好的松耦合、更高的可維護性和可測試性。

          • 依賴注入

          DI(Dependency Injection),即依賴注入,是IOC具體的實現(xiàn),IOC容器動態(tài)的將某個依賴注入到對象之中

          4bcee39d9af315a08cd9ceda4603131f.webp

          Spring Bean 在Spring中,Spring IoC 容器管理的對象稱為 bean。

          Spring IOC

          Spring提供了兩種容器:BeanFactoryApplicationContext

          BeanFactory 接口提供配置框架和基本功能,能夠管理任何類型的對象。

          ApplicationContextBeanFactory的子接口

          通過上圖我們可以看到ApplicationContext還繼承了其他接口,增加了消息資源處理(用于國際化)、事件發(fā)布、資源訪問等功能。

          初始化流程

          通過前面的內(nèi)容,我們可以了解到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()方法創(chuàng)建了一個IOC容器,我們主要這個構造方法開始,分析一下IOC容器的初始化過程。

                
                /**
           * 構造一個ClassPathXmlApplicationContext對象。
           *
           * @param configLocations 配置文件路徑數(shù)組
           * @param refresh 是否刷新上下文
           * @param parent 父上下文對象,可能為空
           * @throws BeansException 如果在創(chuàng)建對象時發(fā)生錯誤,拋出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.調(diào)用容器準備刷新的方法,獲取容器的當時時間,同時給容器設置同步標識 
                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.調(diào)用實現(xiàn)了BeanFactoryPostProcessor的類
                   invokeBeanFactoryPostProcessors(beanFactory);

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

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

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

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

                   // ▲10.注冊實現(xiàn)了ApplicationListener的listener
                   registerListeners();

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

                   // ▲12.觸發(fā)所有監(jiān)聽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();
                }
             }
          }

          可以看到容器的創(chuàng)建和初始化就在obtainFreshBeanFactory()方法,重點來看一下

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

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

           @Override
           protected final void refreshBeanFactory() throws BeansException {
                     
                 // 如果已經(jīng)有容器存在,則需要把已有的容器銷毀和關閉,以保證在refresh之后使用的是新建立起來的IoC容器
               if (hasBeanFactory()) {
                   destroyBeans();
                   closeBeanFactory();
               }
               try {
                   // 創(chuàng)建DefaultListableBeanFactory對象
                   DefaultListableBeanFactory beanFactory = createBeanFactory();
                   // 設置序列化ID
                   beanFactory.setSerializationId(getId());
                   // 自定義Bean工廠
                   customizeBeanFactory(beanFactory);
                   // 加載定義的Bean
                   loadBeanDefinitions(beanFactory);
                   // 將beanFactory賦值給類成員變量
                   this.beanFactory = beanFactory;
               }
               catch (IOException ex) {
                   // 解析bean定義源時發(fā)生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 {

             // 創(chuàng)建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

          經(jīng)過上面的步驟我們定義的資源以及容器本身需要的資源全部加載到reader中,通過XmlBeanDefinitionReader#loadBeanDefinitions()方法會得到一個XML文件的InputStream,然后 將Bean定義資源轉(zhuǎn)換成Document對象。最后在registerBeanDefinitions()方法中完成對解析

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

          而具體的解析過程由BeanDefinitionParserDelegate()來實現(xiàn)

                
                protected void doRegisterBeanDefinitions(Element root) {
              // ... 代碼部分省略
              
              // 執(zhí)行XML預處理操作
              preProcessXml(root);

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

              // 執(zhí)行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容器注冊完以后,發(fā)送事件消息
                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容器完成了初始化,其內(nèi)部使用ConcurrentHashMap(Spring IOC容器本質(zhì))保存了BeanDefinition信息

          總結

          Spring IOC初始化可以分為三個步驟

          • 通過 ResourceLoader 來完成資源文件位置的定位(Resource)
          • 將Resource定位好的資源載入到BeanDefinition(通過 BeanDefinitionReader來完成定義信息的解析)
          • 將BeanDefiniton注冊到容器中(通過BeanDefinitionRegistry接口,將BeanDefinition保存到ConcurrentHashMap中)

          Spring IOC容器管理了定義的各種Bean對象及其相互的關系,降低了耦合度

          瀏覽 52
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  精品一区二区三区鸳鸯网 | 久久久久亚洲AV成人片乱码 | 亚洲第一成人网站 | 无码在线免费视频 | 欧美福利视频导航 |