spring通過註解方式依賴注入原理 (私有成員屬性如何注入)
一、spring如何建立依賴的物件
用過spring的都知道我們在dao、service層加上@repository、@Service就能將這兩個物件交給spring管理,在下次使用的時候使用@resource 或者@Autowired 就可以拿到而不需要自己再去new了,那麼它是如何建立那些加上註解的物件的呢?
通過JAVA反射拿到無參建構函式
以下只是舉例:
Constructor<?> constructor = Teacher.class.getConstructor(null);
可以看到class.getConstructor 可以拿到建構函式,然後再通過constructor.newInstance(null);例項化該物件,如此一來物件便建立好了,有參建構函式也是同理,區別是你要獲得它的建構函式引數,然後在getConstructor 中傳入引數的型別即可獲取對應的構造函數了。
二、spring建立物件時如何注入依賴的物件
現在我們知道spring建立物件是通過反射,那麼如何該物件依賴其他物件時是如何操作的呢?舉例:
@Service public class Teacher { @Resource private Student student; public void print(){ if(student!=null){ System.out.println("student name:"+student.getName()); }else{ System.out.println("student is null"); } } }
我們將Teacher物件交給spring管理了,但是需要在建立Teacher的同時將建立好的Student也賦值進去,可以看到Student物件是private私有的,而且沒加get set,也就沒法通過反射拿到get set方法並注入它的依賴物件了,這幾天一直在查詢相關資料終於弄明白了,其實還是反射。。。。。。
首先通過返回獲取成員屬性的註解,然後判斷註解型別是根據物件型別還是名稱注入,到這裡都很好理解,關鍵在於私有物件如何注入,請看以下程式碼:
Field[] fields = Teacher.class.getDeclaredFields(); Student student = new Student(); student.setName("zhangsan"); Teacher teacher = new Teacher(); for (Field field : fields) { if(field.getType().getName().equals(Student.class.getName())){ //關鍵點!設定私有成員屬性為可訪問! field.setAccessible(true); //將已建立的物件賦值 field.set(teacher, student); } } teacher.print();
我們假設Student 和Teacher物件都已經由spring建立好了,那麼現在Teacher裡有Student物件需要依賴注入,於是以上程式碼使我弄清楚了spring如果注入私有成員屬性物件,其重點在於如何不通過get set方法把私有的成員物件賦值進去,關鍵程式碼:
field.setAccessible(true);
設定私有成員屬性可以通過反射獲取 ,然後通過field.set(teacher, student);
獎Teacher物件裡的成員屬性student賦值進去,大功告成!具體的細節實現比如如何判斷註解、如何儲存SPRING建立的類等等相信大家不用看原始碼也知道大概是怎麼做的了。
下面附上一段spring 裝配物件的原始碼:
/**
* Either this or {@link #getResourceToInject} needs to be overridden.
*/
protected void inject(Object target, String requestingBeanName, PropertyValues pvs) throws Throwable {
if (this.isField) {
Field field = (Field) this.member;
ReflectionUtils.makeAccessible(field);
field.set(target, getResourceToInject(target, requestingBeanName));
}
else {
if (checkPropertySkipping(pvs)) {
return;
}
try {
Method method = (Method) this.member;
ReflectionUtils.makeAccessible(method);
method.invoke(target, getResourceToInject(target, requestingBeanName));
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
其中的ReflectionUtils.makeAccessible(field);
正是設定私有成員屬性可以通過反射訪問!
spring建立物件的順序
將物件交給spring管理也是就建立順序的,那就是優先建立無任何依賴、有無參建構函式的物件,只有這樣才能確保建立有依賴物件時可以將需要的成員物件注入。