之前說過,依賴注入就是為了解決依賴的問題的,在Spring中,本來應該自己入new的對象自己不來new了,交給bean去new。其實使用反射可以實現依賴注入。
下面就是簡單實現的方式:
使用反射可以new出新的實例,我們可以這么做:
1 public Object getInstance(String ClassName){ 2 private Object result=null; 3 try { 4 result=Class.forName(ClassName).newInstance(); 5 } catch (ClassNotFoundException e) { 6 e.printStackTrace(); 7 } catch (InstantiationException e) { 8 e.printStackTrace(); 9 } catch (IllegalAccessException e) { 10 e.printStackTrace(); 11 } 12 return result; 13 }
為了保證通用性,這里使用了返回Object類型。之后可以直接在代碼中調用這個方法,在反射中實現依賴注入(假設有一個User接口和他的實現類):
1 TestIOC testIOC=new TestIOC(); 2 User user=(User)testIOC.getInstance("com.smile.UserImpl"); 3 user.sayHey();
第一行為剛剛封裝反射方法的類。好像還是沒有達到預知的效果,這里開始修改:
將實現類放到外面的配置文件中,到使用的時候載注入到其中。
那么我們加入了以下配置文件:
1 <?xml version="1.0" encoding="utf-8"?> 2 <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> 3 <properties> 4 <entry key="className">com.smile.User</entry> 5 </properties>
然后在讀取配置文件:
1 InputStream inputStream=ClassLoader.getSystemResourceAsStream("testxml.xml"); 2 Properties properties=new Properties(); 3 properties.loadFromXML(inputStream); 4 String ClassName=properties.getProperty("className"); 5 System.out.println(ClassName);
這里好像有點似曾相識了,之前使用Spring的時候,就有類似的代碼:
1 public void UseTest(){ 2 ApplicationContext context=new ClassPathXmlApplicationContext("SpringConfig.xml"); 3 Hello hello=(Hello)context.getBean("hello"); 4 hello.sayHello(); 5 }
讀取玩配置文件還會進行下一步的操作,也會有類似的代碼:
1 TestIOC testIOC=new TestIOC(); 2 User user=(User)testIOC.getInstance(ClassName); 3 user.sayHey();
是不是和Spring的代碼中驚人的相似了,我們在程序中沒有自己去new對象,而是交給了我們自己寫出來的方法去new,我們的代碼會根據我們自己寫的配置文件得到配置文件的信息,然后去new配置文件中寫好的對象,將依賴注入到這里,如果我們要使用的實現類出現了變化,那么只需要修改xml配置文件中的信息就好了,大大減少了修改代碼的成本。通過以上就簡單的實現了依賴注入。
這種實現方式實際上使用了一種設計模式——抽象工廠。
那么抽象工廠是一種什么樣的設計模式呢?提供一個創建一系列相關或相互依賴的接口對象。而無需指定具體的類。
在剛剛的代碼中User就是抽象,之所以是抽象,是因為User可能會有不同的實現,而我們利用反射而得到的對象的方法——getInstance()就是工廠。之所以沒有看到抽象工廠的影子是因為使用反射的時候不需要再代碼中顯示的new出對象,而類名是字符串,可以隨意修改,反射取代了抽象工廠,完成了依賴注入。
如果這里使用純粹的抽象工廠的話會出現大量的類來new我們需要的實例,反射只需要一個方法可以new出來很多我們需要的實例。
當然因為類過多的時候我們也可以使用簡單工廠來改進抽象工廠,就是講new的過程放到switch中new,在new的時候根據輸入的字符來判斷使用哪一種方式,但是這里如果我們增加一個實現類的時候,就要再次修改代碼,在switch中加入case,這樣還是會很麻煩,而利用反射也改進了這里的缺點。