前言:對於spring IOC概念不是很了解的朋友可以閱讀我上一篇博客——輕松理解spring IOC(這兩篇博客也是由於我的個人原因導致現在才發布,慚愧啊)。通過這篇博客的理解之后,相信大家會對spring的IOC概念會有進一步的理解。接下來我先預覽一下本例中java的類圖關系。
解析:我們有一個Master接口,接口中定義了一個WalkDog()遛狗的方法,Hostess是對這個接口的具體實現。然后我們有一個Dog接口,接口中有一個bark()方法,Labuladuo和Taidi是對其的實現。最后我們的程序入口Client類調用Hostess對象的WalkDog方法。
需求:Hostess對象遛狗需要一個狗對象,目前我們的類中有兩個符合需求的對象,我們只要在配置文件中進行相關配置便可以指定我們的Hostess對象調用的是哪一個具體的Dog對象。
1 public static void main(String[] args) { 2 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); 3 Master master = (Master)context.getBean("hostess"); 4 5 System.out.println(); 6 System.out.println(); 7 System.out.println(); 8 System.out.println("***********************************"); 9 master.WalkDog(); 10 }
解析:從main方法的前兩句原spring的代碼中我們可以猜想,spring框架中一定是定義了ApplicationContext這個接口,並且接口中定義了一個getBean()的方法,而ClassPathXmlApplicationContext類肯定是對其的實現。既然是我們自己動手寫spring框架,我們把這個接口和類實現了也就可以了。
接口 ApplicationContext
1 public interface ApplicationContext { 2 public Object getBean(String beanid); 3 }
實現類 ClassPathXmlApplicationContext
1 package com; 2 3 import java.io.File; 4 import java.lang.reflect.Method; 5 6 import org.dom4j.Document; 7 import org.dom4j.DocumentException; 8 import org.dom4j.Element; 9 import org.dom4j.Node; 10 import org.dom4j.io.SAXReader; 11 12 public class ClassPathXmlApplicationContext implements ApplicationContext { 13 14 private String fileName; 15 16 public ClassPathXmlApplicationContext(String fileName){ 17 this.fileName = fileName; 18 } 19 20 @Override 21 public Object getBean(String beanid) { 22 //獲取本類的當前目錄 23 String currentPath = this.getClass().getResource("").getPath().toString(); 24 25 SAXReader reader = new SAXReader();//DOM4J解釋器 26 Document doc = null;//xml文檔本身 27 Object obj = null;//目標表創建出來的實例 28 try { 29 doc = reader.read( new File(currentPath+fileName) ); 30 String xpath = "/beans/bean[@id='"+beanid+"']"; 31 Element beanNode = (Element) doc.selectSingleNode(xpath); 32 String className = beanNode.attributeValue("class"); 33 obj = Class.forName(className).newInstance(); 34 35 Element propertyNode = (Element) beanNode.selectSingleNode("property"); 36 37 if(propertyNode!=null){ 38 System.out.println("當前bean有屬性需要注入"); 39 40 String propertyName = propertyNode.attributeValue("name"); 41 System.out.println("當前bean需要注入的屬性為"+propertyName); 42 43 //拼接出注入方法 44 String setMethod = "set"+(propertyName.substring(0, 1)).toUpperCase()+propertyName.substring(1,propertyName.length()); 45 System.out.println("自動調用注入方法"+setMethod); 46 47 String set_object_name = propertyNode.attributeValue("ref"); 48 System.out.println("需要注入的對象名"+set_object_name); 49 50 Object di_object = getBean(set_object_name); 51 System.out.println("注入的對象實例"+di_object); 52 53 Method []methods = obj.getClass().getMethods(); 54 55 for (Method m : methods) { 56 if(setMethod.equals(m.getName()) ) { 57 m.invoke(obj, di_object); 58 break; 59 } 60 } 61 62 }else{ 63 System.out.println("當前bean沒有屬性,無需注入直接結束"); 64 } 65 66 } catch (Exception e) { 67 e.printStackTrace(); 68 } 69 70 71 return obj; 72 } 73 74 }
配置文件 applicationContext.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans> 3 <bean id="hostess" class="com.Hostess"> 4 <property name="dog" ref="Taidi_dog"></property> 5 </bean> 6 7 <bean id="Taidi_dog" class="com.Taidi"></bean> 8 9 <bean id="Labuladuo_dog" class="com.Labuladuo"></bean> 10 </beans>
解析:① 我們的applicationContext.xml文件主要是配置我們的java bean。這里我們自己寫一份這樣的文件通知我們自己的框架有哪些對象需要注入。
② 接口 ApplicationContext 這里我只是定義了一個方法就不多解釋了。
③ 實現類 ClassPathXmlApplicationContext 主要是解析我們的xml文件然后構造實例的一個類。解析xml文件我們主要使用的是dom4j,獲取各個節點和節點屬性與屬性值。創建對象則是通過反射的方式構造對象 [obj = Class.forName(className).newInstance();]。 在判斷一個對象是否有屬性需要注入則是使用遞歸算法對其一一注入。
最后: 我們來看一下運行結果
小結:我們自己手寫的框架自然沒有spring框架嚴謹,安全(不然它早倒閉了),不過spring的原理我們自己的也是大同小異的。通過源碼級別的解讀,相信大家已經可以熟練掌握IOC原理。