1. 程式人生 > >spring通過註解方式依賴注入原理 (私有成員屬性如何注入)

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管理也是就建立順序的,那就是優先建立無任何依賴、有無參建構函式的物件,只有這樣才能確保建立有依賴物件時可以將需要的成員物件注入。