spring的ioc容器的實現原理(附測試代碼)

 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 的數組。 數組


返回:
該類所實現的接口的一個數組。
網絡

測試效果圖 架構

淡淡spring的ioc容器的實現原理(附測試代碼) - zhang8mss - zhang8mss的博客


下面附上完整代碼 框架

測試類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();  } }

相關文章
相關標籤/搜索