spring現在在java開源框架中大行其道,很受歡迎,是輕量級JAVA EE中的核心框架,企業級應用信息系統開發的首選框架,它不愧是是JAVA中重量級框架EJB強大挑戰對手。由於其靈活的擴展性和伸縮性,靈活簡單的配置(採用xml文件和Annotation註解等),能夠和衆多的開源框架進行有效的整合資源,讓它既能夠和struts,jsf這些表現層框架,也能和hibernate,ibatis這些中間件框架能夠無縫連接,還有dwr這些ajax框架!總之,spring真是個神奇而又偉大的東西!而其中,它的IOC和AOP則是spring中的核心應用.阿堂一直對spring中的IOC功能的實現,有些好奇,想探個究竟,看看這些世界頂級高手是如何實現的.阿堂在幸讀到了瘋狂java書籍李剛先生的《輕量級JAVA EE企業應用實戰》一書中,講架構模式時,給出sping 的ioc容器相關原理的簡單代碼實現後,讓阿堂看了後,豁然開朗,原來spring的底層的實現原理是這樣的,以爲頗有收穫,以下,就有以下這遍文章的出現了。看高手寫的東西,會讓你之後開發時,會從另外一個新的高度上來看問題!多看看開源世界的成熟框架的代碼,是很是重要的,這也許是高手學習必須經歷的過程吧!
下面,我就來和朋友一塊兒來分享一下了!(在本篇文章,阿堂會附上詳細的測試代碼)
一般狀況下,咱們使用spring的時候,會將spring配置成Listener寫在web.xml中,讓web容器初始化的時候,也同時加載了spring了容器,sping容器在加載的時候,實際上,它也是要進行初始化,數據源的初始化,完成bean類的實例化,bean類的依賴注入的屬性的初始化等工做,下面我要分享的代碼,正是基於這樣的思路來進行的
在附上以下測試代碼以前,我先對李剛先生的spring的ioc代碼的實現原理的代碼功能,做一下簡單的說明,要否則,估計會有一些朋友可能不必定能看懂這些代碼的執行,由於裏面有不少java反射方面的知識!
主要的幾點,阿堂說明以下
(1)這些代碼中,關鍵就是YeekuXmlApplicationContext類,它裏面有兩個重要的初始化方法,一個是initPool(),它主要是初始化容器中全部singleton Bean,另外一個是initProp(),它主要是初始化容器中singleton Bean的屬性
(2)以下代碼中的ioc容器,也一樣採用bean.xml配置文件的方式來管理bean,其中,主要講了兩種常見的bean類,其做用域是singleton和prototype類型
(3)在加載的時候,首先會用一個Map來封裝bean對應的id號和Class類的實例(對象)
(4)其中,有以下一段代碼,須要認真體會,剛開始,阿堂還真沒看懂,後來,我查了jdk的api才真正弄懂(api說明我附在後面)
Method setter = null;
//遍歷target對象所所實現的全部接口
for (Class superInterface : target.getClass().getInterfaces())
{
try
{
//獲取設值注入所需的setter方法
setter = bean.getClass().getMethod(
"set" + propNameCamelize , superInterface);
//若是成功取得該接口對應的方法,直接跳出循環
break;
}
catch (NoSuchMethodException ex)
{
//若是沒有找到對應的setter方法,繼續下次循環
continue;
}
}
java
實際上,對於此例中,上面的break語句,能夠註釋掉,由於BetterPrinter implements Output原本就只實現了一個接口,若是是多個接口,此處的break是不能省掉的 web
在jdk的api中,有以下說明(我就不解釋了)
public Class[] getInterfaces()肯定此對象所表示的類或接口實現的接口。
若是此對象表示一個類,則返回值是一個數組,它包含了表示該類所實現的全部接口的對象。數組中接口對象的順序與此對象所表示的類的聲明的 implements 子句中的接口名順序一致。例如,給定聲明: ajax
class Shimmer implements FloorWax, DessertTopping { ... }
設 s 的值爲 Shimmer 的一個實例;表達式:
s.getClass().getInterfaces()[0]
的值爲表示FloorWax 接口的 Class 對象;
s.getClass().getInterfaces()[1]
的值爲表示 DessertTopping 接口的 Class 對象。
若是此對象表示一個接口,則該數組包含表示該接口擴展的全部接口的對象。數組中接口對象的順序與此對象所表示的接口的聲明的 extends 子句中的接口名順序一致。 spring
若是此對象表示一個不實現任何接口的類或接口,則此方法返回一個長度爲 0 的數組。 api
若是此對象表示一個基本類型或 void,則此方法返回一個長度爲 0 的數組。 數組
返回:
該類所實現的接口的一個數組。 網絡
測試效果圖 架構
測試類IoCTest 學習
public class IoCTest
{
public static void main(String[] args)
throws Exception
{
//建立IoC容器
ApplicationContext ctx =
new YeekuXmlApplicationContext("bean.xml");
//從IoC容器中取出computer Bean
Computer c = (Computer)ctx.getBean("computer");
//測試Computer對象
c.keyIn("瘋狂Java講義");
c.keyIn("Struts2權威指南");
c.print();
System.out.println(ctx.getBean("now"));
}
}
關鍵的實現類YeekuXmlApplicationContext
public class YeekuXmlApplicationContext
implements ApplicationContext
{
//保存容器中全部單例模式的Bean實例
private Map<String , Object> objPool
= Collections.synchronizedMap(new HashMap<String , Object>());
//保存配置文件對應的Document對象
private Document doc;
//保存配置文件裏的根元素
private Element root;
public YeekuXmlApplicationContext(String filePath)
throws Exception
{
SAXReader reader = new SAXReader();
String path=System.getProperties().getProperty("user.dir")+"\\src\\";
doc = reader.read(path+filePath);
root = doc.getRootElement();
initPool();
initProp();
}
public Object getBean(String name)
throws Exception
{
Object target = objPool.get(name);
//對於singleton Bean,容器已經初始化了全部Bean實例
if (target.getClass() != String.class)
{
return target;
}
else //這種狀況對於非singlton Bean
{
String clazz = (String)target;
//對於prototype並未注入屬性值
return Class.forName(clazz).newInstance();
}
}
//初始化容器中全部singleton Bean
private void initPool()
throws Exception
{
//遍歷配置文件裏的每一個<bean.../>元素
for (Object obj : root.elements())
{
Element beanEle = (Element)obj;
//取得<bean.../>元素的id屬性
String beanId = beanEle.attributeValue("id");
//取得<bean.../>元素的class屬性
String beanClazz = beanEle.attributeValue("class");
//取得<bean.../>元素的scope屬性
String beanScope = beanEle.attributeValue("scope");
//若是<bean.../>元素的scope屬性不存在,或爲singleton
if (beanScope == null ||
beanScope.equals("singleton"))
{
//以默認構造器建立Bean實例,並將其放入objPool中
objPool.put(beanId , Class.forName(beanClazz).newInstance());
}
else
{
//對於非singlton Bean,存放該Bean實現類的類名。
objPool.put(beanId , beanClazz);
}
}
}
//初始化容器中singleton Bean的屬性
private void initProp()
throws Exception
{
//遍歷配置文件裏的每一個<bean.../>元素
for (Object obj : root.elements())
{
Element beanEle = (Element)obj;
//取得<bean.../>元素的id屬性
String beanId = beanEle.attributeValue("id");
//取得<bean.../>元素的scope屬性
String beanScope = beanEle.attributeValue("scope");
//若是<bean.../>元素的scope屬性不存在,或爲singleton
if (beanScope == null ||
beanScope.equals("singleton"))
{
//取出objPool的指定的Bean實例
Object bean = objPool.get(beanId);
//遍歷<bean.../>元素的每一個<property.../>子元素
for (Object prop : beanEle.elements())
{
Element propEle = (Element)prop;
//取得<property.../>元素的name屬性
String propName = propEle.attributeValue("name");
//取得<property.../>元素的value屬性
String propValue = propEle.attributeValue("value");
//取得<property.../>元素的ref屬性
String propRef = propEle.attributeValue("ref");
//將屬性名的首字母大寫
String propNameCamelize = propName.substring(0 , 1).toUpperCase()
+ propName.substring(1 , propName.length());
//若是<property.../>元素的value屬性值存在
if (propValue != null && propValue.length() > 0)
{
//獲取設值注入所需的setter方法
Method setter = bean.getClass().getMethod(
"set" + propNameCamelize , String.class);
//執行setter注入
setter.invoke(bean , propValue);
}
if (propRef != null && propRef.length() > 0)
{
//取得須要被依賴注入的Bean實例
Object target = objPool.get(propRef);
//objPool池中不存在指定Bean實例
if (target == null)
{
//此處還應處理Singleton Bean依賴prototype Bean的情形
}
//定義設值注入所需的setter方法
Method setter = null;
//遍歷target對象所所實現的全部接口
for (Class superInterface : target.getClass().getInterfaces())
{
try
{
//獲取設值注入所需的setter方法
setter = bean.getClass().getMethod(
"set" + propNameCamelize , superInterface);
//若是成功取得該接口對應的方法,直接跳出循環
break;
}
catch (NoSuchMethodException ex)
{
//若是沒有找到對應的setter方法,繼續下次循環
continue;
}
}
//若是setter方法依然爲null,
//則直接取得target實現類對應的setter方法
if (setter == null)
{
setter = bean.getClass().getMethod(
"set" + propNameCamelize , target.getClass());
}
//執行setter注入
setter.invoke(bean , target);
}
}
}
}
}
}
spring的ioc容器的配置文件bean.xml
<?xml version="1.0" encoding="GBK"?>
<beans>
<bean id="computer" class="lee.Computer">
<!-- 爲name屬性注入普通屬性值 -->
<property name="name" value="網絡時空的電腦"/>
<!-- 爲out屬性注入普通屬性值 -->
<property name="out" ref="betterPrinter"/>
</bean>
<!-- 配置兩個Bean實例 -->
<bean id="printer" class="lee.Printer"/>
<bean id="betterPrinter" class="lee.BetterPrinter"/>
<!-- 配置一個prototype行爲的Bean實例 -->
<bean id="now" class="java.util.Date" scope="prototype"/>
</beans>
接口類Output
public interface Output
{
//接口裏定義的屬性只能是常量
int MAX_CACHE_LINE = 50;
//接口裏定義的只能是public的抽象實例方法
void out();
void getData(String msg);
}
接口類
public interface ApplicationContext
{
//獲取指定Bean實例的方法
Object getBean(String name)
throws Exception;
}
實現類BetterPrinter
public class BetterPrinter implements Output
{
private String[] printData = new String[MAX_CACHE_LINE * 2];
//用以記錄當前需打印的做業數
private int dataNum = 0;
public void out()
{
//只要還有做業,繼續打印
while(dataNum > 0)
{
System.out.println("高速打印機正在打印:" + printData[0]);
//把做業隊列總體前移一位,並將剩下的做業數減1
System.arraycopy(printData , 1, printData, 0, --dataNum);
}
}
public void getData(String msg)
{
if (dataNum >= MAX_CACHE_LINE * 2)
{
System.out.println("輸出隊列已滿,添加失敗");
}
else
{
//把打印數據添加到隊列裏,已保存數據的數量加1。
printData[dataNum++] = msg;
}
}
}
實現類Printer
public class Printer implements Output
{
private String[] printData = new String[MAX_CACHE_LINE];
//用以記錄當前需打印的做業數
private int dataNum = 0;
public void out()
{
//只要還有做業,繼續打印
while(dataNum > 0)
{
System.out.println("打印機打印:" + printData[0]);
//把做業隊列總體前移一位,並將剩下的做業數減1
System.arraycopy(printData , 1, printData, 0, --dataNum);
}
}
public void getData(String msg)
{
if (dataNum >= MAX_CACHE_LINE)
{
System.out.println("輸出隊列已滿,添加失敗");
}
else
{
//把打印數據添加到隊列裏,已保存數據的數量加1。
printData[dataNum++] = msg;
}
}
}
普通類Computer
public class Computer
{
private Output out;
private String name;
public Computer(){}
//out屬性的setter和getter方法 public void setOut(Output out) { this.out = out; } //name屬性的setter和getter方法 public void setName(String name) { this.name = name; } //定義一個模擬獲取字符串輸入的方法 public void keyIn(String msg) { out.getData(msg); } //定義一個模擬打印的方法 public void print() { System.out.println(name + "開始打印..."); out.out(); } }