接觸過spring 的同學應該都知道依賴注入,依賴注入又稱控制反轉,其內涵就是,將創建某個bean的控制權力,由原來需要引用這個bean的bean轉移(反轉)到外部的spring IOC容器,由IOC容器統一創建,並且注入到需要引用的bean中去。 那么spring是怎么做到依賴注入的,我們來看看spring是怎么做的吧!
其實,spring的本質是一個工廠(beanFactory)或者說bean容器,它按照我們的要求,生產我們需要的各種各樣的bean,提供給我們使用。只是在生產bean的過程中,需要解決bean之間的依賴問題,才引入了依賴注入(DI)這種技術。也就是說依賴注入是beanFactory生產bean時為了解決bean之間的依賴的一種技術而已。但是,我們一般都不直接用BeanFactory,而是用它的實現類ApplicationContext,這個類會自動解析我們配置的applicationContext.xml,然后根據我們配置的bean來new對象,將new好的對象放進一個Map中,鍵就是我們bean的id,值就是new的對象。
我們來看一下beanFactory的代碼:
1 package org.springframework.beans.factory; 2 3 public interface BeanFactory { 4 String FACTORY_BEAN_PREFIX = "&"; 5 6 Object getBean(String var1) throws BeansException; 7 8 <T> T getBean(String var1, Class<T> var2) throws BeansException; 9 10 <T> T getBean(Class<T> var1) throws BeansException; 11 12 Object getBean(String var1, Object... var2) throws BeansException; 13 14 <T> T getBean(Class<T> var1, Object... var2) throws BeansException; 15 16 ... 17 }
beanFactory是一個接口,其中定義了各種getBean()方法,我們可以看到返回的是一個object對象,因此通過上下文對象ApplicationContext對象拿到的對象都必須通過強轉能變成指定的類型的對象。
我們再來模擬ClassPathXmlApplicationContext的是怎么拿到bean的:
1 package com.spring.Context; 2 3 import java.util.HashMap; 4 import java.util.List; 5 import java.util.Map; 6 7 import org.dom4j.Document; 8 import org.dom4j.DocumentException; 9 import org.dom4j.Element; 10 import org.dom4j.io.SAXReader; 11 12 13 public class ClassPathXmlApplicationContext implements BeanFactory { 14 private Map<String, Object> beanMap = new HashMap<String, Object>(); //map的value是object 15 public ClassPathXmlApplicationContext(String fileName) throws Exception{ 16 SAXReader reader = new SAXReader(); 17 Document document = reader.read(this.getClass().getClassLoader().getResourceAsStream(fileName)); //SAX解析對應的xml配置文件 18 List<Element> elements = document.selectNodes("/beans/bean"); 19 for (Element e : elements) { 20 String id = e.attributeValue("id"); 21 String value = e.attributeValue("class"); 22 Object o = Class.forName(value).newInstance(); //利用反射通過類名拿到的類的一個實例對象 23 beanMap.put(id, o); 24 } 25 } 26 27 public Object getBean(String id) { 28 return beanMap.get(id); 29 } 30 31 }
我們可以發現,通過spring getBean()拿到的對象實例,都是通過讀取applicationContext.xml文件,再通過反射拿到的類的實例對象。
因此,spring注入的對象必須可以實例化,也就是說,接口和抽象類是不能通過spring實現注入的,因為兩者都不能實例化。
這就讓人很不能理解了,因為在做spring-mybatis整合的時候,我們有一個常見的做法就是在service層通過注解注入dao層接口,在這里不是可以注入接口嗎?為什么又說spring不能注入接口呢?
其實,這里注入的接口,並不是真正的接口,我們不妨吧這個注入的“接口”打印出來看看,到底是個什么東西:
1 the dao is: org.apache.ibatis.binding.MapperProxy@33e4ae3b 2 the dao class is : class com.sun.proxy.$Proxy21
輸出顯示,其實注入的並不是接口,而是mybatis中的mapper對象的代理類。
至於這個里面的玄機,還是等下次去探討吧!