深入解析Java的Spring框架中bean的依賴(lài)注入
每一個(gè)基于java的應(yīng)用程序都有一個(gè)共同工作來(lái)展示給用戶(hù)看到的內(nèi)容作為工作的應(yīng)用幾個(gè)對(duì)象。當(dāng)編寫(xiě)一個(gè)復(fù)雜的Java應(yīng)用程序,應(yīng)用程序類(lèi)應(yīng)該盡可能獨(dú)立其他Java類(lèi)來(lái)增加重復(fù)使用這些類(lèi),并獨(dú)立于其他類(lèi)別的測(cè)試它們,而這樣做單元測(cè)試的可能性。依賴(lài)注入(或有時(shí)稱(chēng)為布線(xiàn))有助于粘合這些類(lèi)在一起,同時(shí)保持他們的獨(dú)立。
考慮有其中有一個(gè)文本編輯器組件的應(yīng)用程序,要提供拼寫(xiě)檢查。標(biāo)準(zhǔn)的代碼將看起來(lái)像這樣:
public class TextEditor {
private SpellChecker spellChecker;
public TextEditor() {
spellChecker = new SpellChecker();
}
}
我們?cè)谶@里所做的就是創(chuàng)建文本編輯和拼寫(xiě)檢查之間的依賴(lài)性。在控制方案中的反轉(zhuǎn),我們反而會(huì)做這樣的事情:
public class TextEditor {
private SpellChecker spellChecker;
public TextEditor(SpellChecker spellChecker) {
this.spellChecker = spellChecker;
}
}
在這里,文本編輯不應(yīng)該擔(dān)心拼寫(xiě)檢查落實(shí)。拼寫(xiě)檢查器將獨(dú)立實(shí)施,將提供給文本編輯在文本編輯實(shí)例化的時(shí)候,這整個(gè)過(guò)程是由Spring框架的控制。
在這里,我們已經(jīng)刪除從文本編輯的全面控制,并保持它在其他地方(即XML配置文件)和依賴(lài)性(即類(lèi)拼寫(xiě)檢查)被注入到類(lèi)文本編輯通過(guò)類(lèi)構(gòu)造函數(shù)。因此,流程控制已經(jīng)“倒”通過(guò)依賴(lài)注入(DI),因?yàn)橐呀?jīng)有效地委派依賴(lài)一些外部系統(tǒng)。
依賴(lài)注入的第二種方法是通過(guò)文本編輯類(lèi),我們將創(chuàng)建拼寫(xiě)檢查實(shí)例的setter方法,該實(shí)例將被用來(lái)調(diào)用setter方法來(lái)初始化文本編輯的屬性。
因此,DI主要有兩種變體和下面的兩個(gè)子章將涵蓋兩者結(jié)合實(shí)例:
基于構(gòu)造函數(shù)的依賴(lài)注入
當(dāng)容器調(diào)用類(lèi)的構(gòu)造函數(shù)有多個(gè)參數(shù),每個(gè)代表在其他類(lèi)中的構(gòu)造函數(shù)依賴(lài)關(guān)系為基礎(chǔ)的DI來(lái)完成。
例子:
下面的例子顯示了一個(gè)類(lèi)文本編輯TextEditor 只能是依賴(lài)注入與構(gòu)造函數(shù)注入。
我們使用Eclipse IDE,然后按照下面的步驟來(lái)創(chuàng)建一個(gè)Spring應(yīng)用程序:

這里是TextEditor.java文件的內(nèi)容:
package com.yiibai;
public class TextEditor {
private SpellChecker spellChecker;
public TextEditor(SpellChecker spellChecker) {
System.out.println("Inside TextEditor constructor." );
this.spellChecker = spellChecker;
}
public void spellCheck() {
spellChecker.checkSpelling();
}
}
下面是另外一個(gè)相關(guān)的類(lèi)文件SpellChecker.java內(nèi)容:
package com.yiibai;
public class SpellChecker {
public SpellChecker(){
System.out.println("Inside SpellChecker constructor." );
}
public void checkSpelling() {
System.out.println("Inside checkSpelling." );
}
}
以下是MainApp.java文件的內(nèi)容:
package com.yiibai;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("Beans.xml");
TextEditor te = (TextEditor) context.getBean("textEditor");
te.spellCheck();
}
}
以下是配置文件beans.xml文件里面有配置為基于構(gòu)造函數(shù)的注入:
<?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"> <!-- Definition for textEditor bean --> <bean id="textEditor" class="com.yiibai.TextEditor"> <constructor-arg ref="spellChecker"/> </bean> <!-- Definition for spellChecker bean --> <bean id="spellChecker" class="com.yiibai.SpellChecker"> </bean> </beans>
創(chuàng)建源代碼和bean配置文件完成后,讓我們運(yùn)行應(yīng)用程序。如果一切順利將打印以下信息:
Inside SpellChecker constructor. Inside TextEditor constructor. Inside checkSpelling.
構(gòu)造函數(shù)的參數(shù)解析:
可能有歧義存在,而將參數(shù)傳遞給構(gòu)造函數(shù)的情況下有一個(gè)以上的參數(shù)。要解決這種不確定性,其中的構(gòu)造器參數(shù)在一個(gè)bean定義中定義的順序就是這些參數(shù)提供給適當(dāng)?shù)臉?gòu)造函數(shù)的順序。請(qǐng)考慮下面的類(lèi):
package x.y;
public class Foo {
public Foo(Bar bar, Baz baz) {
// ...
}
}
下面的配置工作正常:
<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>
讓我們檢查一個(gè)更多情況下我們通過(guò)不同類(lèi)型的構(gòu)造函數(shù)。請(qǐng)考慮下面的類(lèi):
package x.y;
public class Foo {
public Foo(int year, String name) {
// ...
}
}
容器也可以使用類(lèi)型匹配與簡(jiǎn)單類(lèi)型,如果你明確地指定使用type屬性的構(gòu)造函數(shù)的參數(shù)類(lèi)型。例如:
<beans> <bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg type="int" value="2001"/> <constructor-arg type="java.lang.String" value="Zara"/> </bean> </beans>
最后,并通過(guò)構(gòu)造函數(shù)參數(shù)的最佳方法,使用索引屬性來(lái)顯式地指定一個(gè)構(gòu)造器參數(shù)的索引。這里的索引是從0開(kāi)始。例如:
<beans> <bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg index="0" value="2001"/> <constructor-arg index="1" value="Zara"/> </bean> </beans>
最后需要說(shuō)明的,如果你傳遞一個(gè)引用到一個(gè)對(duì)象,需要使用<constructor-arg>標(biāo)簽的ref屬性,如果是直接傳遞一個(gè)值,那么應(yīng)該使用value屬性。
基于setter方法的依賴(lài)注入
基于setter DI由容器調(diào)用setter方法對(duì)bean調(diào)用無(wú)參構(gòu)造器或無(wú)參static工廠(chǎng)方法實(shí)例化bean之后完成。
這里是TextEditor.java文件的內(nèi)容:
package com.yiibai;
public class TextEditor {
private SpellChecker spellChecker;
// a setter method to inject the dependency.
public void setSpellChecker(SpellChecker spellChecker) {
System.out.println("Inside setSpellChecker." );
this.spellChecker = spellChecker;
}
// a getter method to return spellChecker
public SpellChecker getSpellChecker() {
return spellChecker;
}
public void spellCheck() {
spellChecker.checkSpelling();
}
}
在這里,需要檢查setter方法的命名約定。設(shè)置我們使用setSpellChecker()方法,這是非常類(lèi)似于Java POJO類(lèi)的變量的拼寫(xiě)檢查器。讓我們創(chuàng)造另一個(gè)相關(guān)的類(lèi)文件SpellChecker.java,內(nèi)容如下:
package com.yiibai;
public class SpellChecker {
public SpellChecker(){
System.out.println("Inside SpellChecker constructor." );
}
public void checkSpelling() {
System.out.println("Inside checkSpelling." );
}
}
以下是MainApp.java文件的內(nèi)容:
package com.yiibai;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("Beans.xml");
TextEditor te = (TextEditor) context.getBean("textEditor");
te.spellCheck();
}
}
以下是配置文件beans.xml文件里面有配置為基于setter方法注入:
<?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"> <!-- Definition for textEditor bean --> <bean id="textEditor" class="com.yiibai.TextEditor"> <property name="spellChecker" ref="spellChecker"/> </bean> <!-- Definition for spellChecker bean --> <bean id="spellChecker" class="com.yiibai.SpellChecker"> </bean> </beans>
應(yīng)該注意在基于構(gòu)造函數(shù)注入和setter注入定義beans.xml文件的差異。唯一的區(qū)別是,我們已經(jīng)使用<constructor-arg>標(biāo)簽為基于構(gòu)造函數(shù)的注入和的<property>標(biāo)簽為基于setter注入的<bean>元素內(nèi)。
需要注意的第二個(gè)重要的一點(diǎn)是,如果傳遞一個(gè)引用到一個(gè)對(duì)象,需要使用<property>標(biāo)簽的ref屬性,如果是直接傳遞一個(gè)值,那么應(yīng)該使用value屬性。
創(chuàng)建源代碼和bean配置文件完成后,讓我們運(yùn)行應(yīng)用程序。如果一切順利,這將打印以下信息:
Inside SpellChecker constructor. Inside setSpellChecker. Inside checkSpelling.
采用p名稱(chēng)空間的XML配置:
如果你有很多的setter方法則可以很方便地使用p名稱(chēng)空間的XML配置文件中。讓我們查看他們的區(qū)別:
讓我們來(lái)用的<property>標(biāo)簽標(biāo)準(zhǔn)的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-3.0.xsd"> <bean id="john-classic" class="com.example.Person"> <property name="name" value="John Doe"/> <property name="spouse" ref="jane"/> </bean> <bean name="jane" class="com.example.Person"> <property name="name" value="John Doe"/> </bean> </beans>
上面的XML配置可重寫(xiě)使用 p-namespace如下一個(gè)簡(jiǎn)潔的方法:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="john-classic" class="com.example.Person" p:name="John Doe" p:spouse-ref="jane"/> </bean> <bean name="jane" class="com.example.Person" p:name="John Doe"/> </bean> </beans>
在這里,不應(yīng)該在指定原始值和對(duì)空間對(duì)象引用的區(qū)別。-ref部分表示,這不是直鏈的值,而是一個(gè)引用到另一個(gè)bean中。
相關(guān)文章
java.lang.UnsupportedOperationException分析及解決辦法
日常開(kāi)發(fā)中我遇到j(luò)ava.lang.UnsupportedOperationException:異常兩次了,下面這篇文章主要給對(duì)大家介紹了關(guān)于java.lang.UnsupportedOperationException分析及解決辦法,需要的朋友可以參考下2024-03-03
spring security的BCryptPasswordEncoder加密和對(duì)密碼驗(yàn)證的原理分析
文章介紹了加密算法和hash算法的基本概念,以及BCryptPasswordEncoder加密和解密的原理,加密算法是可逆的,需要加鹽以保證安全性,BCryptPasswordEncoder通過(guò)生成鹽值并在加密和解密過(guò)程中使用,確保相同的明文每次加密結(jié)果不同,從而提高安全性2024-11-11
SpringCloud微服務(wù)熔斷器Hystrix使用詳解
這篇文章主要介紹了Spring Cloud Hyxtrix的基本使用,它是Spring Cloud中集成的一個(gè)組件,在整個(gè)生態(tài)中主要為我們提供服務(wù)隔離,服務(wù)熔斷,服務(wù)降級(jí)功能,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-07-07
解析阿里GTS開(kāi)源版本fescar分布式事務(wù)
這篇文章主要為大家介紹解析阿里GTS開(kāi)源版本fescar分布式事務(wù)的原理及使用說(shuō)明,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多進(jìn)步2022-02-02
java使用RSA加密方式實(shí)現(xiàn)數(shù)據(jù)加密解密的代碼
這篇文章給大家分享java使用RSA加密方式實(shí)現(xiàn)數(shù)據(jù)加密解密,通過(guò)實(shí)例代碼文字相結(jié)合給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友參考下2019-11-11

