Spring知識——IOC容器

  • 2020 年 1 月 16 日
  • 筆記

IOC概述

1、理解: (1)控制反轉。將生成對象的控制權交IOC容器,由容器生成依賴的對象。調用類只依賴介面,而不依賴具體的實現類,減少了耦合。在運行的時候,才由容器將具體的實例注入到調用類的對象中。 (2)依賴注入,就是向Ioc容器索要bean的過程。getBean是依賴注入的起點。依賴注入的過程是用戶第一次向Ioc容器索要Bean時觸發的。 (3)生成bean的兩種方式 a、通過反射調用構造函數 b、通過CGLib 2、優點: (1)依賴關係的管理被反轉並交給容器,使複雜的依賴關係管理從應用中解放出來。 (2)程式碼解耦 3、啟動過程(依賴注入的實現過程): a、Resource尋找資源(XML文件形式的beanDefinition) b、將XML文件載入記憶體中,解析成org.springframework.beans.factory.config.BeanDefinition對象 c、將org.springframework.beans.factory.config.BeanDefinition對象註冊到HashMap容器中 d、客戶想Ioc容器索要bean,觸發依賴注入

基礎使用

一、首先講解依賴注入的3種方式: 1、set方式注入: 假設有一個SpringAction,類中需要實例化一個SpringDao對象,那麼就可以定義一個private的SpringDao成員變數,然後創建SpringDao的set方法(這是ioc的注入入口):

package com.bless.springdemo.action;  public class SpringAction {          //注入對象springDao      private SpringDao springDao;          //一定要寫被注入對象的set方法          public void setSpringDao(SpringDao springDao) {          this.springDao = springDao;      }            public void ok(){          springDao.ok();      }  }  

隨後編寫spring的xml文件,<bean>中的name屬性是class屬性的一個別名,class屬性指類的全名,因為在SpringAction中有一個公共屬性Springdao,所以要在<bean>標籤中創建一個<property>標籤指定SpringDao。<property>標籤中的name就是SpringAction類中的SpringDao屬性名,ref指下面<bean name="springDao"...>,這樣其實是spring將SpringDaoImpl對象實例化並且調用SpringAction的setSpringDao方法將SpringDao注入。

<!--配置bean,配置後該類由spring管理-->  <bean name="springAction" class="com.bless.springdemo.action.SpringAction">          <!--(1)依賴注入,配置當前類中相應的屬性-->          <property name="springDao" ref="springDao"></property>  </bean>  <bean name="springDao" class="com.bless.springdemo.dao.impl.SpringDaoImpl"></bean>

2、構造器注入: 這種方式的注入是指帶有參數的構造函數注入,看下面的例子,我創建了兩個成員變數SpringDao和User,但是並未設置對象的set方法,所以就不能支援Set注入方式,這裡的注入方式是在SpringAction的構造函數中注入,也就是說在創建SpringAction對象時要將SpringDao和User兩個參數值傳進來:

public class SpringAction {      //注入對象springDao      private SpringDao springDao;      private User user;        public SpringAction(SpringDao springDao,User user){          this.springDao = springDao;          this.user = user;          System.out.println("構造方法調用springDao和user");      }            public void save(){          user.setName("卡卡");          springDao.save(user);      }  } 

在XML文件中同樣不用<property>的形式,而是使用<constructor-arg>標籤,ref屬性同樣指向其它<bean>標籤的name屬性:

<!--配置bean,配置後該類由spring管理-->      <bean name="springAction" class="com.bless.springdemo.action.SpringAction">          <!--(2)創建構造器注入,如果主類有帶參的構造方法則需添加此配置-->          <constructor-arg ref="springDao"></constructor-arg>          <constructor-arg ref="user"></constructor-arg>      </bean>      <bean name="springDao" class="com.bless.springdemo.dao.impl.SpringDaoImpl"></bean>      <bean name="user" class="com.bless.springdemo.vo.User"></bean> 

解決構造方法參數的不確定性,你可能會遇到構造方法傳入的兩參數都是同類型的,為了分清哪個該賦對應值,則需要進行一些小處理: 下面是設置index,就是參數位置:

<bean name="springAction" class="com.bless.springdemo.action.SpringAction">          <constructor-arg index="0" ref="springDao"></constructor-arg>          <constructor-arg index="1" ref="user"></constructor-arg>  </bean>  

另一種是設置參數類型:

<constructor-arg type="java.lang.String" ref=""/> 

3、介面注入: ClassA依賴於InterfaceB,在運行期, InterfaceB 實例將由容器提供。

public class ClassA {      private InterfaceB clzB;      public Object doSomething(InterfaceB b) {          clzB = b;          return clzB.doIt();      }  }  ……  }

二、Bean標籤 1、scope屬性: (1)singleton:單例模式,即該bean對應的類只有一個實例;在spring 中是scope(作用範圍)參數的默認值 ; (2)prototype:表示每次從容器中取出bean時,都會生成一個新實例;相當於new出來一個對象; (3)request:基於web,表示每次接受一個HTTP請求時,都會生成一個新實例; (4)session:表示在每一個session中只有一個該對象. (5)global session global session作用域類似於標準的HTTP Session作用域,不過它僅僅在基於portlet的web應用中才有意義。Portlet規範定義了全局Session的概念,它被所有構成某個portlet web應用的各種不同的portlet所共享。在global session作用域中定義的bean被限定於全局portlet Session的生命周期範圍內。如果你在web中使用global session作用域來標識bean,那麼web會自動當成session類型來使用。 配置實例: 和request配置實例的前提一樣,配置好web啟動文件就可以如下配置: <bean id="role" class="spring.chapter2.maryGame.Role" scope="global session"/> (6)自定義bean裝配作用域: 在spring2.0中作用域是可以任意擴展的,你可以自定義作用域,甚至你也可以重新定義已有的作用域(但是你不能覆蓋singleton和prototype),spring的作用域由介面org.springframework.beans.factory.config.Scope來定義,自定義自己的作用域只要實現該介面即可,下面給個實例: 我們建立一個執行緒的scope,該scope在表示一個執行緒中有效,程式碼如下:

publicclass MyScope implements Scope {        privatefinal ThreadLocal threadScope = new ThreadLocal() {            protected Object initialValue() {               returnnew HashMap();             }       };       public Object get(String name, ObjectFactory objectFactory) {           Map scope = (Map) threadScope.get();           Object object = scope.get(name);          if(object==null) {             object = objectFactory.getObject();             scope.put(name, object);           }          return object;        }       public Object remove(String name) {           Map scope = (Map) threadScope.get();          return scope.remove(name);        }        publicvoid registerDestructionCallback(String name, Runnable callback) {        }      public String getConversationId() {         // TODO Auto-generated method stub          returnnull;       }             }

源碼解析

一、IOC容器: 1、對於Spring的使用者而言,IOC容器實際上是什麼呢?我們可以說BeanFactory就是我們看到的IoC容器,當然了Spring為我們準備了許多種IoC容器來使用,比如說ApplicationContext。這樣可以方便我們從不同的層面,不同的資源位置,不同的形式的定義資訊來建立我們需要的IoC容器。 在Spring中,最基本的IOC容器介面是BeanFactory - 這個介面為具體的IOC容器的實現作了最基本的功能規定 - 不管怎麼著,作為IOC容器,這些介面你必須要滿足應用程式的最基本要求,查看BeanFactory的源碼:

public interface BeanFactory {        //這裡是對FactoryBean的轉義定義,因為如果使用bean的名字檢索FactoryBean得到的對象是工廠生成的對象,      //如果需要得到工廠本身,需要轉義      String FACTORY_BEAN_PREFIX = "&";          //這裡根據bean的名字,在IOC容器中得到bean實例,這個IOC容器就是一個大的抽象工廠。      Object getBean(String name) throws BeansException;        //這裡根據bean的名字和Class類型來得到bean實例,和上面的方法不同在於它會拋出異常:如果根據名字取得的bean實例的Class類型和需要的不同的話。      Object getBean(String name, Class requiredType) throws BeansException;        //這裡提供對bean的檢索,看看是否在IOC容器有這個名字的bean      boolean containsBean(String name);        //這裡根據bean名字得到bean實例,並同時判斷這個bean是不是單件      boolean isSingleton(String name) throws NoSuchBeanDefinitionException;        //這裡對得到bean實例的Class類型      Class getType(String name) throws NoSuchBeanDefinitionException;        //這裡得到bean的別名,如果根據別名檢索,那麼其原名也會被檢索出來      String[] getAliases(String name);    }  

2、容器載入流程解析: 這裡我們以ClassPathXmlApplicationContext的初始化為例 (1)首先從容器構造函數入口:

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)              throws BeansException {            super(parent);          setConfigLocations(configLocations);          if (refresh) {  //這裡是IoC容器的初始化過程,其初始化過程的大致步驟由AbstractApplicationContext來定義              refresh();          }      }

(2)再看AbstractApplicationContext中refresh函數定義,這個方法包含了整個BeanFactory初始化的過程。這裡使用到模板模式。

@Override      public void refresh() throws BeansException, IllegalStateException {          synchronized (this.startupShutdownMonitor) {              // 準備這個上下文來刷新              prepareRefresh();                // 告訴子類刷新其beanFactory              ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();                // 準備將要在上下文中使用的bean工廠              prepareBeanFactory(beanFactory);                try {                  // 允許在上下文子類中對bean工廠進行後處理                  postProcessBeanFactory(beanFactory);                    // 調用 factory processors註冊為上下文中的bean                  invokeBeanFactoryPostProcessors(beanFactory);                    // 註冊 攔截bean創建的bean處理器                  registerBeanPostProcessors(beanFactory);                    // 初始化此上下文的消息源                  initMessageSource();                    // 初始化此上下文的時間多播器                  initApplicationEventMulticaster();                    // 在特殊上下文子類中初始化其特殊的bean                  onRefresh();                    // 檢查監聽器bean,並且註冊它們                  registerListeners();                    // 初始化所有剩下的(非懶載入)單例                  finishBeanFactoryInitialization(beanFactory);                    // 發布相應的事件                  finishRefresh();              }                catch (BeansException ex) {                  if (logger.isWarnEnabled()) {                      logger.warn("Exception encountered during context initialization - " +                              "cancelling refresh attempt: " + ex);                  }                    // 銷毀已經創建的單例,以避免資源泄漏                  destroyBeans();                    // 重置active標誌位                  cancelRefresh(ex);                    // 拋出異常給調用者                  throw ex;              }                finally {                  // 在Spring的核心中重置常見的自檢快取,因為我們可能不再需要單例對象的元數據了                  resetCommonCaches();              }          }      }

(4)進入obtainFreshBeanFactory()函數,發現調用refreshBeanFactory(),而refreshBeanFactory()裡面調用了loadBeanDefinitions()函數,該函數描述了載入bean定義的過程,最終會調用」具體的解析和註冊過程「。

@Override      protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {          // 為給定的BeanFactory創建一個新的XmlBeanDefinitionReader          XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);            // 使用此上下文的資源載入環境,去配置bean定義閱讀器。          beanDefinitionReader.setEnvironment(this.getEnvironment());          beanDefinitionReader.setResourceLoader(this);          beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));            // 允許子類提供reader的自定義初始化,然後執行實際載入bean定義。          initBeanDefinitionReader(beanDefinitionReader);          loadBeanDefinitions(beanDefinitionReader);      }    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {          Resource[] configResources = getConfigResources();          if (configResources != null) {              // 調用XmlBeanDefinitionReader來載入bean定義資訊。              reader.loadBeanDefinitions(configResources);          }          String[] configLocations = getConfigLocations();          if (configLocations != null) {              reader.loadBeanDefinitions(configLocations);          }  }    // XmlBeanDefinitionReader.java  public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {          Assert.notNull(encodedResource, "EncodedResource must not be null");          if (logger.isInfoEnabled()) {              logger.info("Loading XML bean definitions from " + encodedResource.getResource());          }            Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();          if (currentResources == null) {              currentResources = new HashSet<EncodedResource>(4);              this.resourcesCurrentlyBeingLoaded.set(currentResources);          }          if (!currentResources.add(encodedResource)) {              throw new BeanDefinitionStoreException(                      "Detected cyclic loading of " + encodedResource + " - check your import definitions!");          }          try {              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();              }          }      }      protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)              throws BeanDefinitionStoreException {          try {              //通過解析得到DOM,然後完成bean在IOC容器中的註冊              Document doc = doLoadDocument(inputSource, resource);              return registerBeanDefinitions(doc, resource);          }      ....

我們看到先把定義文件解析為DOM對象,然後進行具體的註冊過程:

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {  // 具體的註冊過程,首先得到XmlBeanDefinitionReader,來處理xml的bean定義文件          BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();          int countBefore = getRegistry().getBeanDefinitionCount();          documentReader.registerBeanDefinitions(doc, createReaderContext(resource));          return getRegistry().getBeanDefinitionCount() - countBefore;      }

(5)在BeanDefinitionDocumentReader中完成bean定義文件的解析和IOC容器bean的初始化。

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {          this.readerContext = readerContext;          logger.debug("Loading bean definitions");          Element root = doc.getDocumentElement();          doRegisterBeanDefinitions(root);  }    protected void doRegisterBeanDefinitions(Element root)          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);                  if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {                      return;                  }              }          }            preProcessXml(root);          parseBeanDefinitions(root, this.delegate);          postProcessXml(root);            this.delegate = parent;  }    // 對配置文件(xml文件)進行解析  protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {          if (delegate.isDefaultNamespace(root)) {              NodeList nl = root.getChildNodes();              for (int i = 0; i < nl.getLength(); i++) {                  Node node = nl.item(i);                  if (node instanceof Element) {                      Element ele = (Element) node;                      if (delegate.isDefaultNamespace(ele)) {                      //這裡是解析過程的調用,對預設的元素進行分析比如bean元素                          parseDefaultElement(ele, delegate);                      }                      else {                          delegate.parseCustomElement(ele);                      }                  }              }          }          else {              delegate.parseCustomElement(root);          }  }    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {          if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {              importBeanDefinitionResource(ele);          }          else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {              processAliasRegistration(ele);          }          else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {              //這裡對我們最熟悉的bean元素進行處理              processBeanDefinition(ele, delegate);          }          else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {              // recurse              doRegisterBeanDefinitions(ele);          }      }    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {  //委託給BeanDefinitionParserDelegate來完成對bean元素的處理,這個類包含了具體的bean解析的過程。          // 把解析bean文件得到的資訊放到BeanDefinition里,他是bean資訊的主要載體,也是IOC容器的管理對象。          BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);          if (bdHolder != null) {              bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);              try {                  // 這裡是向IOC容器註冊,實際上是放到IOC容器的一個map里                  BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());              }              catch (BeanDefinitionStoreException ex) {                  getReaderContext().error("Failed to register bean definition with name '" +                          bdHolder.getBeanName() + "'", ele, ex);              }              // 這裡向IOC容器發送事件,表示解析和註冊完成              getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));          }      }

(6)我們看到在parseBeanDefinition中對具體bean元素的解析式交給BeanDefinitionParserDelegate來完成的,下面我們看看解析完的bean是怎樣在IOC容器中註冊的: 在BeanDefinitionReaderUtils調用的是:

public static void registerBeanDefinition(              BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)              throws BeanDefinitionStoreException {            // Register bean definition under primary name.          String beanName = definitionHolder.getBeanName();          //這是調用IOC來註冊的bean的過程,需要得到BeanDefinition          registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());            // 別名也是可以通過IOC容器和bean聯繫起來的進行註冊          String[] aliases = definitionHolder.getAliases();          if (aliases != null) {              for (String alias : aliases) {                  registry.registerAlias(beanName, alias);              }          }  }

(7)我們看看XmlBeanFactory中的註冊實現:

//---------------------------------------------------------------------  // 這裡是IOC容器對BeanDefinitionRegistry介面的實現  //---------------------------------------------------------------------    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)          throws BeanDefinitionStoreException {        .....//這裡省略了對BeanDefinition的驗證過程      //先看看在容器里是不是已經有了同名的bean,如果有拋出異常。      Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);      if (oldBeanDefinition != null) {          if (!this.allowBeanDefinitionOverriding) {          ...........      }      else {          //把bean的名字加到IOC容器中去          this.beanDefinitionNames.add(beanName);      }      //這裡把bean的名字和Bean定義聯繫起來放到一個HashMap中去,IOC容器通過這個Map來維護容器里的Bean定義資訊。      this.beanDefinitionMap.put(beanName, beanDefinition);      removeSingleton(beanName);  } 

這樣就完成了Bean定義在IOC容器中的註冊,就可被IOC容器進行管理和使用了。

總結IOC容器初始化流程:

1、初始化的入口在容器實現中的refresh()調用來完成 2、將bean定義資訊載入IOC容器。使用的方法是loadBeanDefinition,其中的大致過程如下: (1)通過ResourceLoader來完成資源文件位置的定位,DefaultResourceLoader是默認的實現,同時上下文本身就給出了ResourceLoader的實現,可以從類路徑,文件系統, URL等方式來定為資源位置。如果是XmlBeanFactory作為IOC容器,那麼需要為它指定bean定義的資源,也就是說bean定義文件時通過抽象成Resource來被IOC容器處理的 (2)容器通過BeanDefinitionReader來完成定義資訊的解析和Bean資訊的註冊,往往使用的是XmlBeanDefinitionReader來解析bean的xml定義文件 - 實際的處理過程是委託給BeanDefinitionParserDelegate來完成的,從而得到bean的定義資訊,這些資訊在Spring中使用BeanDefinition對象來表示 - 這個名字可以讓我們想到loadBeanDefinition,RegisterBeanDefinition這些相關的方法 - 他們都是為處理BeanDefinitin服務的,IoC容器解析得到BeanDefinition以後,需要把它在IOC容器中註冊,這由IOC實現 BeanDefinitionRegistry介面來實現。註冊過程就是在IOC容器內部維護的一個HashMap來保存得到的 BeanDefinition的過程。這個HashMap是IoC容器持有bean資訊的場所,以後對bean的操作都是圍繞這個HashMap來實現的。 (3)然後我們就可以通過BeanFactory和ApplicationContext來享受到Spring IOC的服務了.

Beanfactory 和Factory bean

1、BeanFactory 指的是IOC容器的編程抽象,比如ApplicationContext, XmlBeanFactory等,Bean工廠, 保存了所有的Bean並管理它們的生命周期和依賴關係。 2、Factory bean 是一個可以在IOC容器中被管理的一個bean,是對各種處理過程和資源使用的抽象,Factory bean在需要時產生另一個對象,而不返回FactoryBean本省,我們可以把它看成是一個抽象工廠,對它的調用返回的是工廠生產的產品。所有的 Factory bean都實現特殊的org.springframework.beans.factory.FactoryBean介面,當使用容器中factory bean的時候,該容器不會返回factory bean本身,而是返回其生成的對象。Spring包括了大部分的通用資源和服務訪問抽象的Factory bean的實現,其中包括: 對JNDI查詢的處理,對代理對象的處理,對事務性代理的處理,對RMI代理的處理等,這些我們都可以看成是具體的工廠,看成是SPRING為我們建立好的工廠。也就是說Spring通過使用抽象工廠模式為我們準備了一系列工廠來生產一些特定的對象,免除我們手工重複的工作,我們要使用時只需要在IOC容器里配置好就能很方便的使用了。

IOC高級特性

一、lazy-init延遲載入 1、執行原理: lazy-init屬性:為true的話,在Ioc容器初始化過程中,會對BeanDefinitionMap中所有的Bean進行依賴注入,這樣在初始化過程結束後,容器執行getBean得到的就是已經準備好的Bean,不需要進行依賴注入。 2、優點:當應用第一次向容器索取所需的Bean時,容器不再需要對Bean進行初始化,直接從已經完成實例化的Bean中取出需要的bean,這樣就提高了第一次獲取Bean的性能。

二、BeanPostProcessor後置處理器: 1、BeanPostProcessor後置處理器是Spring IoC容器經常使用到的一個特性,這個Bean後置處理器是一個監聽器,可以監聽容器觸發的Bean聲明周期事件。後置處理器想容器註冊以後,容器中管理的Bean就具備了接收IoC容器事件回調的能力。 2、BeanPostProcessor的使用非常簡單,只需要提供一個實現介面BeanPostProcessor的實現類,然後在Bean的配置文件中設置即可。 3、API解釋:

public interface BeanPostProcessor {        /**       * Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean       * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}       * or a custom init-method). The bean will already be populated with property values.       */      //實例化、依賴注入完畢,在調用顯示的初始化之前完成一些訂製的初始化任務      Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;          /**       * Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean       * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}       * or a custom init-method). The bean will already be populated with property values.       */      //實例化、依賴注入、初始化完畢時執行      Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;    }

三、自動裝配: 1、概念:無須在Spring配置文件中描述javaBean之間的依賴關係(如配置<property>、<constructor-arg>)。IOC容器會自動建立java Bean之間的關聯關係(通過autowire)。 2、在Spring中,支援 5 自動裝配模式。 (1)no – 預設情況下,自動配置是通過「ref」屬性手動設定 (2)byName – 根據屬性名稱自動裝配。如果一個bean的名稱和其他bean屬性的名稱是一樣的,將會自裝配它。 (3)byType – 按數據類型自動裝配。如果一個bean的數據類型是用其它bean屬性的數據類型,兼容並自動裝配它。 (4)constructor – 在構造函數參數的byType方式。

參考: http://www.iteye.com/topic/86339 http://blessht.iteye.com/blog/1162131 https://blog.csdn.net/mastermind/article/details/1932787 https://blog.csdn.net/sugar_rainbow/article/details/76757383

Tags: