我們回顧一下計算機的發展史,從最初第一台計算機的占地面積達170平方米,重達30噸,到現如今的個人筆記本,事物更加輕量功能卻更加豐富,這是事物發展過程中的一個趨勢,在技術領域中同樣也是如此,企業級JavaBean(Enterprise JavaBean ,EJB)在創建之初是非常成功,但是時間一久人們便開始追逐更加方便更加簡易和輕量級的技術框架實現,於是Spring就應運而生,並且Spring一直開始不斷地涉及到其他領域(這里就不再多詳談了),而Spring的精髓當中就包括控制反轉和依賴注入。
淺談控制反轉(Inversion of Control,IOC)
我們首先先來了解一下控制二字,也就是在控制“正”轉的情況下,在任何一個有請求作用的系統當中,至少需要有兩個類互相配合工作,在一個入口類下使用new關鍵字創建另一個類的對象實例,這就好比在面向對象編程的思想下,“我“充當一個入口類,在這個入口類中,我每次吃飯的時候都要買一雙一次性筷子(每一次使用都要new一次),在這樣的關系下,是”我“(即調用者)每次都要”主動“去買一次性筷子(另一個類),我對筷子說你老老實實的過來我的手上,是我控制了筷子,那好,在這種控制正轉的關系下,放在現實生活當中,肯定是不現實的,而且人是懶惰的,他總會去創造出更加方便自己生活的想法,更確切的做法是,買一雙普通的筷子(非一次性),把他放在一個容器當中(在Spring中叫做IOC容器),你需要使用的時候就對容器說:IOC我想要用筷子(向容器發出請求),接着筷子就會”注入“到的手上,而在這個過程當中,你不再是控制方,反而演變成一名請求者(雖然本身還是調用者),依賴於容器給予你資源,控制權坐落到了容器身上,於是這就是人們俗稱的控制反轉。
初涉依賴注入(Dependency Injection)
同樣接着上面的例子,在控制反轉的統一下,筷子是怎么來到我的手上(即我們是如何獲得請求的類),這就是一個依賴注入的過程。
再談IOC與DI
設計原則中好萊塢原則描述到,“別找我們,我們找你”,百度百科上對這點描述是“不要給我們打電話,我們會給你打電話(don‘t call us, we‘ll call you)”這是著名的好萊塢原則。在好萊塢,把簡歷遞交給演藝公司后就只有回家等待。由演藝公司對整個娛樂項的完全控制,演員只能被動式的接受公司的差使,在需要的環節中,完成自己的演出。這一點完美的提現了在IOC身上,IOC所注重的是設計思想上,從一個常規的創建對象的做法,即new一個對象,轉變成向IOC容器遞交”簡歷“,被動的等待IOC容器返回資源給你。控制反轉即指的是”演藝公司控制演員“,而說到依賴,則是“演員需要公司混飯”,我們所需求的對象,需要依賴容器來獲得,這個過程即是依賴注入。本質上IOC和DI是同一思想下不同維度的表現。
代碼實現
既然說在控制反轉中獲取資源的過程叫做依賴注入,那么這里代碼實現也是專注於依賴注入。依賴注入有3種方式,分別為構造注入,設置注入,接口注入。
1.接口注入:在接口中定義要注入的信息,並通過接口來完成注入。(Spring不支持這種注入方式--不支持的原因是--Spring聲稱其是非入侵式的《離開這個框架也能活》,如果使用接口注入的話,就違背了這一原則),這里不做代碼實現講解。
2.setter注入
我們先脫離Spring來實現setter注入,分兩步,第一步我們先看看在常規的做法下類之間的關系,第二步使用IOC來進行設計類,對比一下之間的差別。
在常規的做法下
public class UserDao{ public void addUser(String username) { System.out.println("添加用戶:"+username); } }
public class UserMessage { String username="demo"; UserDao userDao; public UserMessage(){ userDao=new UserDao(); } public void addUser(){ userDao.addUser(username); } }
public class test{ UserMessage userMessage=new UserMessage(); userMessage.addUser(); }
這上面的代碼存在一個缺陷,由於在UserMessage內部創建了UseDao對象,這就造成了兩個類之間的耦合度較高,當項目經理想要用另外一個Dao類的方法來實現addUser的時候,這個時候我就需要修改User Message的代碼,重新new另一個類,如果系統中有一百個地方用到這個類,那我們這些地方都要全部修改,出現Bug的概率將大大增加,然后過了一陣子,項目經理又說我想換回以前的Dao類了……
下面我們看看IOC設計的實現
public interface UserDao{ void 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(UserDao userDao) { this.userDao = userDao; } public void addUser(String userName, String password) { userDao.addUser(userName); } }
public class test{ public static void main(String[] args) { UserDao userDao =new UserDaoImpl(); UserMessage userMessage=new UserMessage(); userMessage.setUserDao(userDao); } }
我們仔細觀察這里的做法, UserDao userDao=new UserDaoImpl()做法,這里就不得不提到了多態,即父類可以引用子類的方法,在這里形成的一個效果就是降低了User Message和UserDao的耦合度。因為UserMessage和UserDao之間僅存在做什么事(addUser方法)的聯系,User Message並不了解UserDaoImpl的實現方式。當我們需要換另一種UserDao的實現類的話,我們只需要修改test類下的第一行的代碼就可以了,UserMessage內部並不需要修改。
再想想,讀者可能會說不對啊,你說的控制反轉和依賴注入需要向容器請求資源,這個容器並沒有在上面提現出來啊,下面我們就講解一下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》中進行參考。
轉載請說明原文鏈接:http://www.cnblogs.com/xxzhuang/p/5948902.html 多謝合作。