前言:對於spring IOC概念不是很瞭解的朋友可以閱讀我上一篇部落格——輕鬆理解spring IOC(這兩篇部落格也是由於我的個人原因導致現在才釋出,慚愧啊)。通過這篇部落格的理解之後,相信大家會對spring的IOC概念會有進一步的理解。接下來我先預覽一下本例中java的類圖關係。
解析:我們有一個Master介面,介面中定義了一個WalkDog()遛狗的方法,Hostess是對這個介面的具體實現。然後我們有一個Dog介面,介面中有一個bark()方法,Labuladuo和Taidi是對其的實現。最後我們的程式入口Client類呼叫Hostess物件的WalkDog方法。
需求:Hostess物件遛狗需要一個狗物件,目前我們的類中有兩個符合需求的物件,我們只要在配置檔案中進行相關配置便可以指定我們的Hostess物件呼叫的是哪一個具體的Dog物件。
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Master master = (Master)context.getBean("hostess"); System.out.println();
System.out.println();
System.out.println();
System.out.println("***********************************");
master.WalkDog();
}
解析:從main方法的前兩句原spring的程式碼中我們可以猜想,spring框架中一定是定義了ApplicationContext這個介面,並且介面中定義了一個getBean()的方法,而ClassPathXmlApplicationContext類肯定是對其的實現。既然是我們自己動手寫spring框架,我們把這個介面和類實現了也就可以了。
介面 ApplicationContext
public interface ApplicationContext {
public Object getBean(String beanid);
}
實現類 ClassPathXmlApplicationContext
package com; import java.io.File;
import java.lang.reflect.Method; import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader; public class ClassPathXmlApplicationContext implements ApplicationContext { private String fileName; public ClassPathXmlApplicationContext(String fileName){
this.fileName = fileName;
} @Override
public Object getBean(String beanid) {
//獲取本類的當前目錄
String currentPath = this.getClass().getResource("").getPath().toString(); SAXReader reader = new SAXReader();//DOM4J直譯器
Document doc = null;//xml文件本身
Object obj = null;//目標表創建出來的例項
try {
doc = reader.read( new File(currentPath+fileName) );
String xpath = "/beans/bean[@id='"+beanid+"']";
Element beanNode = (Element) doc.selectSingleNode(xpath);
String className = beanNode.attributeValue("class");
obj = Class.forName(className).newInstance(); Element propertyNode = (Element) beanNode.selectSingleNode("property"); if(propertyNode!=null){
System.out.println("當前bean有屬性需要注入"); String propertyName = propertyNode.attributeValue("name");
System.out.println("當前bean需要注入的屬性為"+propertyName); //拼接出注入方法
String setMethod = "set"+(propertyName.substring(0, 1)).toUpperCase()+propertyName.substring(1,propertyName.length());
System.out.println("自動呼叫注入方法"+setMethod); String set_object_name = propertyNode.attributeValue("ref");
System.out.println("需要注入的物件名"+set_object_name); Object di_object = getBean(set_object_name);
System.out.println("注入的物件例項"+di_object); Method []methods = obj.getClass().getMethods(); for (Method m : methods) {
if(setMethod.equals(m.getName()) ) {
m.invoke(obj, di_object);
break;
}
} }else{
System.out.println("當前bean沒有屬性,無需注入直接結束");
} } catch (Exception e) {
e.printStackTrace();
} return obj;
} }
配置檔案 applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="hostess" class="com.Hostess">
<property name="dog" ref="Taidi_dog"></property>
</bean> <bean id="Taidi_dog" class="com.Taidi"></bean> <bean id="Labuladuo_dog" class="com.Labuladuo"></bean>
</beans>
解析:① 我們的applicationContext.xml檔案主要是配置我們的java bean。這裡我們自己寫一份這樣的檔案通知我們自己的框架有哪些物件需要注入。
② 介面 ApplicationContext 這裡我只是定義了一個方法就不多解釋了。
③ 實現類 ClassPathXmlApplicationContext 主要是解析我們的xml檔案然後構造例項的一個類。解析xml檔案我們主要使用的是dom4j,獲取各個節點和節點屬性與屬性值。建立物件則是通過反射的方式構造物件 [obj = Class.forName(className).newInstance();]。 在判斷一個物件是否有屬性需要注入則是使用遞迴演算法對其一一注入。
最後: 我們來看一下執行結果
小結:我們自己手寫的框架自然沒有spring框架嚴謹,安全(不然它早倒閉了),不過spring的原理我們自己的也是大同小異的。通過原始碼級別的解讀,相信大家已經可以熟練掌握IOC原理。