<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                依賴注入(DI)是一個處理對象的依賴的過程,換句話說就是處理與某個對象一起工作的其它對象。所依賴的對象可以通過構造參數、工廠方法參數以及設置對象實例的屬性來定義。當一個對象被創建時,IOC 容器會注入它的依賴,這個過程與傳統創建對象的方式是相反的故稱為控制反轉(IOC)。 &emsp; 使用DI后代碼十分的簡潔,并且在定義依賴時也能有效的解耦。對象不需要過多的去關注它依賴的對象,所以類更容易測試,特別是依賴于接口或者是抽象類時,可以在單元測試中使用stub或者mock實現。 &emsp; DI存在兩種主要的形式:基于構造器注入和Setter方法注入。 * * * * * &emsp; ### **基于構造器注入** 構造注入通過容器提供指定的參數調用構造方法來完成,每個參數都稱為依賴。這和通過提供給靜態的工廠方法的參數來創建對象幾乎是等價的。下面的樣例展示了一個類通過構造方法來完成依賴注入的過程。 ~~~ public class SimpleMovieLister { // SimpleMovieLister依賴于MovieFinder private MovieFinder movieFinder; // Spring通過構造器注入MovieFinder實例 public SimpleMovieLister(MovieFinder movieFinder) { this.movieFinder = movieFinder; } //使用被注入MovieFinder實例來完成的業務邏輯已被忽略 } ~~~ 注意:這個類是普通的Java對象,不依賴于容器相關的接口、基類或者注解。 ### **構造參數的識別** 構造參數的識別是通過類型匹配來完成的,如果沒有模糊不清的構造參數,那么構造參數的定義順序就是容器實例化Bean時提供參數給構造器的順序。參考如下代碼: ~~~ package x.y; public class Foo { public Foo(Bar bar, Baz baz) { // ... } } ~~~ 現在,參數都是明確的。假定`Bar`和`Baz`沒有繼承關系,因此下面的配置沒有任何問題,你也不需要在`<constructor-arg/>`元素中指定參數的索引和類型。 ~~~ <beans> <bean id="foo" class="x.y.Foo"> <constructor-arg ref="bar"/> <constructor-arg ref="baz"/> </bean> <bean id="bar" class="x.y.Bar"/> <bean id="baz" class="x.y.Baz"/> </beans> ~~~ 當引用的Bean的類型是確定的,這能很好的進行參數匹配。如果使用的是簡單類型,比如:`<value>true</value>`,Spring就不能判斷其類型了,所以不能獨自完成類型匹配。參考如下代碼: ~~~ package examples; public class ExampleBean { //計算最終答案的年份 private int years; //生活、宇宙、萬物的答案 private String ultimateAnswer; public ExampleBean(int years, String ultimateAnswer) { this.years = years; this.ultimateAnswer = ultimateAnswer; } } ~~~ ##### 1.構造參數類型匹配 在上面的例子中,如果你通過`type`屬性指定參數的類型,那么容器可以很好的進行簡單參數類型的類型匹配。參考如下代碼: ~~~ <bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg type="int" value="7500000"/> <constructor-arg type="java.lang.String" value="42"/> </bean> ~~~ ##### 2.構造參數索引 使用`index`屬性指定參數在構造器中的索引,參考如下代碼: ~~~ <bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg index="0" value="7500000"/> <constructor-arg index="1" value="42"/> </bean> ~~~ > 如果不指定index的值,則默認以<constructor-arg/>出現的順序為參數索引。 為了解決多個簡單類型間的歧義性,可以通過指定值在構造參數中的索引。注意:索引從0開始。 ##### *3.構造參數名* 你也可以指定值所對應構造器中的參數名來消除歧義: ~~~ <bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg name="years" value="7500000"/> <constructor-arg name="ultimateAnswer" value="42"/> </bean> ~~~ 注意:要使上述樣例正常工作,必須啟用調試標記來編譯代碼,這樣Spring才能從構造器中查找到參數名。否則你必須使用`@ConstructorProperties` JDK注解來顯式指定參數名,參考如下代碼: ~~~ package examples; public class ExampleBean { // 忽略屬性 @ConstructorProperties({"years", "ultimateAnswer"}) public ExampleBean(int years, String ultimateAnswer) { this.years = years; this.ultimateAnswer = ultimateAnswer; } } ~~~ ***** &emsp; ### **基于Setter方法注入** 容器通過無參構造方法或者無參靜態工廠方法實例化Bean之后,再通過調用Setter方法來完成依賴注入。 下面的樣例展示了一個類通過Setter方法來完成依賴注入。注意:這個類是普通的Java對象,不依賴于容器相關的接口、基類或者注解。 ~~~ public class SimpleMovieLister { // SimpleMovieLister依賴于MovieFinder private MovieFinder movieFinder; // Spring通過Setter方法注入MovieFinder實例 public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } //使用被注入MovieFinder實例來完成的業務邏輯已被忽略 } ~~~ `ApplicationContext`支持構造注入和Setter方法注入,也支持通過構造參數注入某些依賴后再通過Setter注入其它依賴。配置的依賴存在于`BeanDefinition`中,可以結合`PropertyEditor`完成屬性的轉換。然而,大多開發者并不會直接使用這些類(即以編程的方式)而是使用XML、被注解的組件(即被`@Component`、 `@Controller`注解的類)以及帶有`@Configuration`的類中`@Bean`注解的方法來定義Bean,這些配置在內部被轉換為`BeanDefinition`實例并被用來加載整個Spring IOC容器。 <p style="margin-bottom:0px">&emsp;</p> >### :-: **使用構造注入還是Setter方法注入?** > > 盡管可以使用混合注入,但是通過**構造方法完成必要依賴的注入**,通過**Setter方法完成可選依賴的注入**是一個好的編程方式。注意:在Setter方法上標注`@Required`([1.9.1.@Required](1.9.1.Required.md))注解使得屬性為必需依賴。不過,使用構造注入(帶有編程式參數驗證)更可取。 > **Spring團隊提倡使用構造注入**,原因如下: > 1.該方式可以使得依賴為不可變對象并且不會為空; > 2.通過構造注入的依賴的對象被客戶端調用時都是完全初始化好的; > 3.它還可以作為啟示:過多的構造參數是一種不好的編程方式,意味著此類擁有過多的職責,最好重構代碼,將多余的職責分離開來。 > &emsp; > Setter注入主要用于可選依賴,但是在類中也應該分配合理的默認值,否則在客戶端使用此依賴時需要做非空檢查。使用Setter注入有一個好處:可以重新配置或者重新注入依賴。通過JMX mbean進行管理是Setter注入的一個引人注目的用例。 > DI只在大多場景下是有效的,當處理沒有源碼的第三方類時,可以選擇其它注入方式。比如:第三方類沒有暴露任何Setter方法,那只能通過構造注入了。 ***** &emsp; ### **依賴處理過程** 容器處理依賴的的細節如下: * ` ApplicationContext`通過配置元數據來創建和初始化,配置元數據可以通過XML、Java代碼、注解來指定。 * 每個Bean的依賴都表示為:屬性、構造參數、靜態工廠方法參數。當Bean創建后容器會注入其依賴。 * 每個屬性、構造參數都是需要設置的值或者需要引用容器中的其它Bean。 * 每個屬性、構造參數的值都被從指定的類型轉換成實際類型。默認情況下,Spring可以將字符串類型的值轉換為`int`,`long`,`String`,`boolean`等。 Spring容器被創建時會驗證每個Bean的配置,然而Bean的屬性在Bean沒有創建之前不會設置。Bean默認是單例的并且會在容器創建時初始化(關于Bean的作用域將在[1.5.Bean的范圍](1.5.Bean的范圍.md)詳細解釋),非單例的Bean將會在被客戶請求獲取時創建。Bean的創建可能會一系列Bean(Bean的依賴的依賴......的依賴)被創建。注意,這些依賴項之間的不匹配解析可能在較晚的時候才出現,例如在第一次創建受影響的Bean時。 <p style="margin-bottom:0px">&emsp;</p> >### :-: **循環依賴** >如果通過構造器完成依賴注入,這可能會出現循環依賴的情況。 > >比如:類A通過構造注入類B的實例,而類B也通過構造注入類A的實例。容器在運行時會檢查出循環引用并拋出`BeanCurrentlyInCreationException`。 >&emsp; >一個可行的解決方案:編輯源碼將一些類的構造注入替換成Setter注入。此外,還可以避免使用構造注入而只使用Setter注入。換而言之,可以通過Setter注入配置循環依賴,雖然這是不推薦的。 >&emsp; >和典型的場景(沒有循環依賴)不同,Bean A和Bean B之間的循環依賴關系迫使在完全初始化之前將其中一個Bean注入到另一個Bean中,這是一個矛盾的問題(這就像一個經典的問題:先有雞還是先有蛋)。 > 開發者可以信任Spring做的事都是正確的。在容器加載時會檢查不存在依賴和循環依賴等問題。當一個Bean被創建時,Spring會盡可能晚的設置屬性和解析依賴。這意味著Spring容器被正確的加載后,當你請求一個對象時,如果創建該對象或者它的一項依賴出現問題,生成異常也會更晚。例如:當Bean的屬性缺失或者無效時會拋出異常。所以一些配置問題可能延遲可見,這也就是為什么`ApplicationContext`的實現默認提前實例化單例Bean的原因。在容器創建之時花費一些時間和內存創建Bean可以提前發現問題。你仍然可以覆蓋提前初始化的行為,將單例Bean的提前加載轉變為延遲加載。 &emsp; 如果沒有循環依賴存在,一個Bean在注入到依賴Bean之前就已經完成初始化好了。這意味著,如果Bean A依賴Bean B,在調用Bean A 的Setter方法之前,Bean B已是完全可用的。換而言之,當一個Bean初始化完成時,它的依賴已經設置好了,相關的生命周期方法(比如:初始化回調方法,詳情參見:[1.6.1.生命周期回調函數](1.6.1.生命周期回調函數.md))也調用完畢。 ***** &emsp; ### **依賴注入的例子** 下面的樣例展示了基于XML配置的Setter注入。 ~~~ <bean id="exampleBean" class="examples.ExampleBean"> <!-- 使用ref元素完成Setter注入--> <property name="beanOne"> <ref bean="anotherExampleBean"/> </property> <!-- 使用更簡潔的ref屬性完成Setter注入 --> <property name="beanTwo" ref="yetAnotherBean"/> <property name="integerProperty" value="1"/> </bean> <bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/> ~~~ ~~~ public class ExampleBean { private AnotherBean beanOne; private YetAnotherBean beanTwo; private int i; public void setBeanOne(AnotherBean beanOne) { this.beanOne = beanOne; } public void setBeanTwo(YetAnotherBean beanTwo) { this.beanTwo = beanTwo; } public void setIntegerProperty(int i) { this.i = i; } } ~~~ 在上面的樣例中,Setter方法和和`<property/>`對應。下面的樣例采用構造注入。 ~~~ <bean id="exampleBean" class="examples.ExampleBean"> <!-- 使用ref元素完成Setter注入--> <constructor-arg> <ref bean="anotherExampleBean"/> </constructor-arg> <!-- 使用更簡潔的ref屬性完成Setter注入 --> <constructor-arg ref="yetAnotherBean"/> <constructor-arg type="int" value="1"/> </bean> <bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/> ~~~ ~~~ public class ExampleBean { private AnotherBean beanOne; private YetAnotherBean beanTwo; private int i; public ExampleBean( AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) { this.beanOne = anotherBean; this.beanTwo = yetAnotherBean; this.i = i; } } ~~~ Bean定義指定的構造器參數會作為ExampleBean構造器中的參數。 現在請參考另一個例子,Spring通過靜態工廠來代替構造器創建實例。 ~~~ <bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance"> <constructor-arg ref="anotherExampleBean"/> <constructor-arg ref="yetAnotherBean"/> <constructor-arg value="1"/> </bean> <bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/> ~~~ ~~~ public class ExampleBean { // 私有構造器 private ExampleBean(...) { ... } /** * 靜態工廠方法; * 這個方法的參數可以被認為是待創建Bean的依賴項, * 不管這些參數實際是如何使用的的。 */ public static ExampleBean createInstance (AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) { ExampleBean eb = new ExampleBean (...); // 其它操作...... return eb; } } ~~~ `<constructor-arg/>`提供給靜態工廠方法的參數和構造器實際使用的參數是一致的。從工廠方法返回的對象類型可以和包含靜態工廠方法的類不一致,盡管在本例子中是一致的。實例(非靜態)工廠方法的用法和靜態工廠方法本質上是一樣(使用`factory-bean` 代替`class`屬性),故不再此詳述。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看