申明:本文不是講解Spring如何使用註解,本文只是經過一個簡單的實現,來理解Spring是如何注入一個對象的。html
用過Spring的同窗都知道,Spring利用註解來實現依賴注入,使得各個類之間的耦合性極大的下降了。可是僅僅是使用,並不能理解到Spring內部是怎麼實現的。筆者沒有看過Spring的源碼。只能從本身的角度來談談Spring是怎麼實現的。感興趣的同窗能夠在看過本文以後,深刻的瞭解Spring.
不少時候,咱們都有這樣的應用場景。好比DAO層,你會先申明一個接口,好比IUserDao,表示用來處理User的一個接口,而後再寫一個實現類UserDaoImpl實現了IUserDao中的方法,而後在上層service層中注入。啓動以後Spring將本身掃描自動爲咱們注入實例化的對象,使得咱們不用在乎各個對象的生命週期。接下來就來聊聊具體是怎麼注入的。
假設如今已經有如下的類:git
public interface IUserDao { public void setData(String data); public String getData(); } public class UserDaoImpl implements IUserDao{ @Override public void setData(String data) { System.out.println("data is : " + data); } @Override public String getData() { return "just test"; } }
其中FieldInject是筆者模仿寫的一個註解,具體定義以下github
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FieldInject { //假設有一些變量用於控制策略 }
具體關於註解上面的元註解的含義,能夠看另一篇博客。這裏就不展開說明了。
以上就是準備工做了,接下來就是講解真正的初始化方法了。
假設咱們如今有一個類的Class對象,那麼咱們能夠根據這個Class對象找到哪些成員變量是加了指定的註解的。代碼以下ide
//下面開始注入 for(int i=0; i<field.length; i++){ FieldInject annotation = field[i].getAnnotation(FieldInject.class); if(annotation != null){//說明這個成員變量有註解 //獲取到成員變量的全限定名 String fullClassName = field[i].getType().getName(); Class sub = findSubClass(fullClassName); if(sub == null) continue; //找不到,略過 String lowCase = "set"+field[i].getName(); //採用setter方法幅值,與下面的代碼二選一便可 // injectMethod(method,obj, sub, lowCase.toLowerCase()); //如下是直接幅值 injectField(field[i],obj,sub); } }
在這段代碼中,筆者查詢的註解是本身實現的一個FieldInject註解,註解自己並不影響代碼的執行。經過判斷是否爲空能夠得出某個成員變量是否加了指定的註解。若是發現成員變量加了註解,就能夠爲該成員變量注入實例化的對象了。
問題1:怎麼知道注入哪一個對象?
問題2:怎麼注入?
問題2很好解決,若是原來的類中帶有setter方法,那麼可使用method.invoke()方法來調用並注入。或者經過field直接注入均可以。那麼主要是問題1,怎麼找到合適的注入對象。
Spring有多種注入的策略,好比按照裝配名稱,或者是默認實現了接口或者抽象類的子類實例對象來注入。總之,不一樣的策略只是選擇的不一樣,咱們能夠假定使用找到的第一個合適子類的實例對象來注入。code
//找到某個類的子類【涉及到Spring的選擇策略】 private Class findSubClass(String fullClassName){ try { Class target = Class.forName(fullClassName); //不是抽象類,不是接口,那自身就行了。 if(!target.isInterface()){ boolean isAbs = Modifier.isAbstract(target.getModifiers()); if(!isAbs) return target; } int size = clazzList.size(); for(int i=0; i<size; i++){ Class p = clazzList.get(i); if(p.getSuperclass() != null && p.getSuperclass().getName().equals(fullClassName)) return p; Class[] inter = p.getInterfaces(); for(Class c : inter){ if(c.getName().equals(fullClassName)) return p; } } } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; }
findSubClass是用來找到某個類的合適子類,相似於Spring中根據某種策略來查找,這裏使用了比較簡單的方法。找到第一個合適的子類便可。這個方法中,作了一些簡單的判斷,若是這個類自己就不是一個抽象類或者不是一個接口,那麼這個類就是第一個合適的類。若是這個類是一個接口或者一個抽象類,那麼就在全局掃描的classList中找到合適的類。找到合適的類以後,下一步就是一個注入了,筆者採用的是給setter方法注入,若是想直接給成員變量賦值也是很是簡單的。只要替換掉方法injectMethod,換成下面兩句代碼便可。htm
field[i].setAccessible(true); field[i].set(target, obj);
injectMethod實現也是比較簡單,經過比對Method中的方法,找到合適的setter方法(這裏是經過field的名稱來判斷的),並將實例對象賦值進去便可。以上就是一個簡單的注入過程的實現。筆者寫的比較匆忙,可能有些細節上經不起推敲。可是若是能爲迷惑的初學者提供一個思路也是不錯的,這份代碼我都上傳到github上了,若是想下載進行運行的能夠移步個人github。對象