關於Spring框架中的IOC的模擬實現
* 本博文內容是基於本人的理解與實踐論證所作
* 如有錯誤請積極指出,不勝感激
* 轉載請註明出處
所用註解及其作用:
註解型別有三種:‘@Component’、‘@Bean’、‘@Autowired’
作用:
——‘@Component’註解的作用物件是類,‘@Bean’註解的作用物件是類中的方法,‘@Autowired’註解的作用物件是類中的需要進行自動注入的成員。
——給類加 ‘@Component’註解是為了實現此類的自動注入,目的是在想使用這些類的物件的時不再去親自new,既直接從beanFactory裡直接取出使用。
——給方法加‘@Bean’註解的作用是為了在掃描到一個自動注入類之後,用該類的物件作為字首執行其加中加了‘@Bean’註解的方法,並將返回值加入到bean工廠中進行處理。
——給類中的欄位加‘@Autowired’註解是為了宣告該類中需要進行自動注入的欄位。
其中bean註解存在的意義:
——bean註解是作用於方法的。對於方法來說,方法存在無參與非無參。這裡是不考慮方法返回值的問題的。我們的目的是幫框架使用者生成他們想要的類的例項,並按照其意願對這些例項進行一些注入操作。從框架使用者的角度來看:如果想得到或者使用某些類的例項,而這些類使用者本身是沒有他的原始碼的(比如一些jar包、class檔案等),所以就無法通過給類新增component註解或者Autowired註解去實現。於是使用者可以在相關注解了component的類中新增一些get方法(無參),並給它們加上bean註解。在這些方法中直接new出並返回此種類的例項。在處理bean註解時先執行無參的方法,將這些方法返回的例項一個一個加入到bean工廠中。之後再執行那些帶參的註解了bean的方法也就不會再擔心某些引數無法初始化而無法執行的問題了。
處理過程分析:
——在包掃描時篩選出註解了Component的類,在每個被篩選出的類中再次篩選出註解了bean註解的方法,將無參的方法先執行,需要引數的方法儲存起來,待無參方法執行完後再將帶參方法執行完。此時的bean工廠已經被豐富填充了,可以進行真正的注入操作了。
遞迴注入法及其問題:
我們需要被將注入的物件的Autowired成員豐富起來,並返回注入物件的代理物件。也就是AOP了。事實上一開始代理物件和原始物件就一起生成了。在這裡先不討論AOP,後續博文會補充。
此時bean工廠中所有需要注入的物件就已經存在了,現在需要做的就是注入了。
掃描需要注入的類,篩選出註解了Autowired的欄位,再在bean工廠中進行查詢,如果此欄位中又有需要注入的成員,那就可以通過遞迴的方式進行注入了。 * 仔細分析存在一個問題,若A類有一個Autowired成員是B類型別的,而這個B類型別又有一個Autowired成員是C類型別的, 這個C類型別的成員又有一個Autowired成員是A類型別的,這種情況如果不加以控制,這個“迴圈依賴注入”的問題就會產生無限遞迴, 將沒有辦法再繼續注入。
* 解決方法是給這個類的物件在準備注入前就加上一個已經注入的標記,這個標記預設在物件產生的時候為False,之後再進行遞迴注入,將此物件的標記值作為遞迴是否進行下去的控制條件就可以完美解決。以下貼出遞迴注入部分的程式碼。
private static void injectBean
(ProxyBeanFactory proxyBeanFactory, Object object, Class<?> klass) {
MecProxy orgMecProxy = proxyBeanFactory.getMecProxy(klass);
orgMecProxy.setInjection(true);//立即標記本次注入
Field[] fields = klass.getDeclaredFields();
for(Field field : fields) {
if(!field.isAnnotationPresent(Autowired.class)) {
continue;
}
Class<?> fieldClass = field.getType();
MecProxy mecProxy = proxyBeanFactory.getMecProxy(fieldClass);
//如果本次注入物件中的Autowired欄位沒有被注入過,先遞迴此欄位,再注入
if(!mecProxy.isInjection()) {
injectBean(proxyBeanFactory, mecProxy.getObject(), fieldClass);
}
field.setAccessible(true);
try {
field.set(object, mecProxy.getObject());
System.out.println("注入了:"
+ mecProxy.getObject() + "到" + object + "的" + field + "中");
} catch (Exception e) {
e.printStackTrace();
}
}
}
其中的proxyBeanFactory中儲存著所有掃描過的bean,可用型別取到,所以是單立的。而取到的MecProxy就是代理、原物件與是否注入過標記的集合類了。
實踐驗證:【(@Component)StudentAction類中有(@Autowired)studentDao欄位,以此依賴關係,最後的StudentModel中有(@Autowired)studentAction欄位】,以下貼出輸出結果:
(輸出結果中的類名字由於存在包名所以很長,在此用‘*’代替一部分)
注入了:*[email protected]到*.student.model.StudentMode[email protected]中
注入了:*[email protected]到*[email protected]中
注入了:*[email protected]到*[email protected]中
注入了:*[email protected]到*[email protected]中
studentAction中的studentdao:*.student.dao.StudentDao:@8c97a5
studentDao 中的 teacherDao:*.student.dao.TeacherDao:@2e4553
teacherDao 中的 studentModel:*.student.model.StudentModel:@59eb14
sudentModel 中的 studentAction:*.student.action.StudentAction:@137e0d2