我們回顧一下計算機的發展史,從最初第一台計算機的占地面積達170平方米,重達30噸,到現如今的個人筆記本,事物更加輕量功能卻更加豐富,這是事物發展過程中的一個趨勢,在技術領域中同樣也是如此,企業級JavaBean(Enterprise JavaBean ,EJB)在創建之初是非常成功,但是時間一久人們便開始追逐更加方便更加簡易和輕量級的技術框架實現,於是Spring就應運而生,並且Sring一直開始不斷地涉及到其他領域(這裡就不再多詳談了),而Spring的精髓當中就包括控制反轉和依賴注入。
我們首先先來了解一下控制二字,也就是在控制“正”轉的情況下,在任何一個有請求作用的系統當中,至少需要有兩個類互相配合工作,在一個入口類下使用new關鍵字創建另一個類的對象實例,這就好比在面向對象編程的思想下,“我“充當一個入口類,在這個入口類中,我每次吃飯的時候都要買一雙一次性筷子(每一次使用都要new一次),在這樣的關系下,是”我“(即調用者)每次都要”主動“去買一次性筷子(另一個類),我對筷子說你老老實實的過來我的手上,是我控制了筷子,那好,在這種控制正轉的關系下,放在現實生活當中,肯定是不現實的,而且人是懶惰的,他總會去創造出更加方便自己生活的想法,更確切的做法是,買一雙普通的筷子(非一次性),把他放在一個容器當中(在Spring中叫做IOC容器),你需要使用的時候就對容器說:IOC我想要用筷子(向容器發出請求),接著筷子就會”注入“到的手上,而在這個過程當中,你不再是控制方,反而演變成一名請求者(雖然本身還是調用者),依賴於容器給予你資源,控制權坐落到了容器身上,於是這就是人們俗稱的控制反轉。
同樣接著上面的例子,在控制反轉的統一下,筷子是怎麼來到我的手上(即我們是如何獲得請求的類),這就是一個依賴注入的過程。
設計原則中好萊塢原則描述到,“別找我們,我們找你”,百度百科上對這點描述是“不要給我們打電話,我們會給你打電話(don‘t call us, we‘ll call you)”這是著名的好萊塢原則。在好萊塢,把簡歷遞交給演藝公司後就只有回家等待。由演藝公司對整個娛樂項的完全控制,演員只能被動式的接受公司的差使,在需要的環節中,完成自己的演出。這一點完美的提現了在IOC身上,IOC所注重的是設計思想上,從一個常規的創建對象的做法,即new一個對象,轉變成向IOC容器遞交”簡歷“,被動的等待IOC容器返回資源給你。控制反轉即指的是”演藝公司控制演員“,而說到依賴,則是“演員需要公司混飯”,我們所需求的對象,需要依賴容器來獲得,這個過程即是依賴注入。本質上IOC和DI是同一思想下不同維度的表現。
既然說在控制反轉中獲取資源的過程叫做依賴注入,那麼這裡代碼實現也是專注於依賴注入。依賴注入有3種方式,分別為構造注入,設置注入,接口注入。
1.接口注入:在接口中定義要注入的信息,並通過接口來完成注入。(Spring不支持這種注入方式--不支持的原因是--Spring聲稱其是非入侵式的《離開這個框架也能活》,如果使用接口注入的話,就違背了這一原則),這裡不做代碼實現講解。
2.setter注入
我們先脫離Spring來實現setter注入
public interface UserDao{ addUser(String username); }
public class UserDaoImpl implements UserDao{ @Override public void addUser(String username) { System.out.println("添加用戶:"+username); } }
public class UserMessage{ private UserDao userDao; //使用設值方式賦值 public void setUserDao(UserDaoImpl userDao) { this.userDao = userDao; } @Override public void addUser(String userName, String password) { userDao.addUser(userName, password); } }
我們仔細觀察,其實這裡的做法跟 UserDao userDao=new UserDaoImpl()做法本質是一樣的,這裡就不得不提到了多態,即父類可以引用子類的方法,在這裡形成的一個效果就是降低了User Message和UserDao的耦合度。再想想,讀者可能會說不對啊,你說的控制反轉和依賴注入需要向容器請求資源,這個容器並沒有在上面提現出來啊,下面我們就講解一下Spring 中是如何做到注入的。
<!-- 使用spring管理對象的創建,還有對象的依賴關系 --> <bean id="userManager" class="scau.zzf.service.UserMessage"> <!-- (1)UserMessageImpl使用了userDao,Ioc是自動創建相應的UserDao實現,都是由容器管理--> <!-- (2)在UserMessageImpl中提供構造函數,讓spring將UserDao實現注入(DI)過來 --> <!-- (3)讓spring管理我們對象的創建和依賴關系,必須將依賴關系配置到spring的核心配置文件中 --> <property name="userDao" ref="UserDao"></property> <!-- 構造注入 --> <!-- <constructor-arg ref="userDao"/> --> </bean> <bean id="UserDao" class="scau.zzf.Dao.UserDao"> </bean>
首先我們需要裝配Bean,即在Spring容器中將Bean進行配置後,然後返回Bean對象實例。我們可以通過XmlBeanFactory讀取我們xml文件,從而獲取相關的Bean信息。
public class test { public static void main(String[] args) throws Exception { BeanFactory factory=new XmlBeanFactory(new FileSystemResource("src/appllication.xml")); UserMessage userMessage=(UserMessage)factory.getBean("UserMessage"); userMessage.add("德瑪西亞"); } }
在實際應用當中,我們並不會手動去讀取Xml中的信息或者加載配置文件,Spring底層已經幫我做好了這些,也就是在實際應用當中,我們就只是需要發送一個請求而已,當然了解這麼一個過程還是很有必要的。
下面再簡單講解一下如何通過注解來實現注入。
@Configuration public class UserConfig { @Bean public UserDao getUserDao(){ return new UserDao(); } @Bean public UserMessage getUserMessage(){ return new UserMesssgae(getUserDao); } }
@Configuration的作用是使整個類成為一個配置類,@Bean注解會告訴Spring這個注解下的方法將會返回一個對象,這個對象要注冊維Spring應用上下文的Bean。在默認情況下,Spring的Bean都是單例的,也就是再上面的例子當中,無論我們使用多少次getUserDao(),結果返回的對象至始至終都是相同的。關於JavaConfig的配置進一步相關說明,讀者可以前往筆者的另一篇文章《更加優雅的配置SSH》中進行參考。