模擬spring IOC 實現

利用java的反射和動態代理實現IOC java

       Java中,其反射和動態代理機制極其強大,咱們能夠經過其反射機制在運行時獲取信息。而代理是一種基本的設計模式,它是一種爲了提供額外的或不一樣的操做而插入到真實對象中的某個對象。而Java的動態代理在代理上更進一步,既能動態的建立代理對象,又能動態的調用代理方法。Java的反射和動態代理機制,使Java變得更增強大。 node

       Spring框架這幾年風頭正勁,雖然使用者衆多,但真正瞭解其內部實現原理的朋友卻並非不少。其實,瞭解它的內部實現機制和設計思想是頗有必要的你們都知道,Spring框架的IOCAOP部分功能強大,很值得咱們學習。那麼讓咱們在這兩篇文章中分別詳細的學習IOCAOP的實現吧。 spring

在本文中,主要講述的是用Java的反射機制實現IOC。下面,讓咱們開始IOC之旅吧! 設計模式

一.             Java反射機制概述與初探 框架

Java的反射機制是Java語言的一個重要特性,Java具備的比較突出的動態機制就是反射(reflection)。經過它,咱們能夠獲取以下信息: dom

1) 在運行時判斷任意一個對象所屬的類; 學習

2) 在運行時獲取類的對象; 測試

3) 在運行時得到類所具備的成員變量和方法等。 this

下面讓咱們經過調用一個Java Reflection API的演示實例來見識一下反射機制的強大。 spa

首先在IDE中創建名爲reflection_proxyJava工程,並創建存放源文件的目錄src,並在src目錄下分別創建org.amigo. reflection目錄和org.amigo.proxy目錄來分別存放代理和反射的實例。咱們在reflection目錄下創建ReflectionTest.java文件,在該文件中編寫代碼來演示Java Reflection API的使用。該類的代碼以下所示:

package  org.amigo.reflection;

import  java.awt.Button;
import  java.lang.reflect.Method;
import  java.util.Hashtable;


publicclass ReflectionTest 
{

    

    publicstaticvoid main(String[] args) 
throws Exception {
        ReflectionTest reflection 
= new ReflectionTest();
        reflection.getNameTest();
        System.out.println(
"");
        reflection.getMethodTest();
     }


    

    publicvoid getNameTest() 
throws Exception {
        System.out.println(
"===========begin getNameTest============");
        String name 
= "阿蜜果";
        Class cls 
= name.getClass();
        System.out.println(
"String類名: " + cls.getName());
        Button btn 
= new Button();
        Class btnClass 
= btn.getClass();
        System.out.println(
"Button類名: " + btnClass.getName());
        Class superBtnClass 
= btnClass.getSuperclass();
        System.out.println(
"Button的父類名: " + superBtnClass.getName());
        Class clsTest 
= Class.forName("java.awt.Button");
        System.out.println(
"clsTest name: " + clsTest.getName());
        System.out.println(
"===========end getNameTest============");
     }

    
    

    publicvoid getMethodTest() 
throws Exception {
        System.out.println(
"===========begin getMethodTest==========");
        Class cls 
= Class.forName("org.amigo.reflection.ReflectionTest");
        Class ptypes[] 
= new Class[2];
        ptypes[
0= Class.forName("java.lang.String");
        ptypes[
1= Class.forName("java.util.Hashtable");
        Method method 
= cls.getMethod("testMethod", ptypes);
        Object args[] 
= new Object[2];
        args[
0= "hello, my dear!";
        Hashtable
<String, String> ht = new Hashtable<String, String>();
        ht.put(
"name""阿蜜果");
        args[
1= ht;

        String returnStr 
= (String) method.invoke(new ReflectionTest(), args);
        System.out.println(
"returnStr= " + returnStr);
        System.out.println(
"===========end getMethodTest==========");
     }


    
public String testMethod(String str, Hashtable ht) throws Exception {
        String returnStr 
= "返回值";
        System.out.println(
"測試testMethod()方法調用");
        System.out.println(
"str= " + str);
        System.out.println(
"名字= " + (String) ht.get("name"));
        System.out.println(
"結束testMethod()方法調用");
        
return returnStr;
 }

 }

     運行該例,可在控制檯看到以下內容:

===========begin getNameTest============

String類名: java.lang.String

Button類名: java.awt.Button

Button的父類名: java.awt.Component

clsTest name: java.awt.Button

===========end getNameTest============

===========begin getMethodTest==========

測試testMethod()方法調用

str= hello, my dear!

名字= 阿蜜果

結束testMethod()方法調用

returnStr= 返回值

===========end getMethodTest==========

    分析運行結果,咱們能夠發現,Java的反射機制使得咱們在運行時可以判斷一個對象所屬的類,獲取對象的方法並得其進行調用,並獲取方法的返回結果等功能。

二.             IOC使用的背景

在咱們平常的設計中,類與類之間存在着千絲萬縷的關係,若是兩個類存在着強耦合關係,那麼在維護時,一個類的修改極可能會牽動另外一個類的關聯修改,從而使得咱們的維護工做寸步難行。下面讓咱們來看這樣的一個強耦合反面例子。

首先咱們創建一個Chinese.java類,該類的sayHelloWorld(String name)方法,用中文對名爲name的人問好,其內容以下:

package  org.amigo.reflection;


publicclass Chinese 
{

    

    publicvoid sayHelloWorld(String name) 
{
       String helloWorld 
= "你好," + name;
       System.out.println(helloWorld);
     }

 }

 

下面咱們接着創建一個American.java類,該類的sayHelloWorld(String name)方法,用英文對名爲name的人問好,其內容以下:

package  org.amigo.reflection;


publicclass American 
{

    

    publicvoid sayHelloWorld(String name) 
{
       String helloWorld 
= "Hello," + name;
       System.out.println(helloWorld);
     }

 }

 

    最後咱們編寫一個測試類對這兩個類的sayHelloWorld(String name)方法進行測試,下面是該類的內容:

package  org.amigo.reflection;


publicclass HelloWorldTest 
{

    

    publicstaticvoid main(String[] args) 
{
       Chinese chinese 
= new Chinese();
       chinese.sayHelloWorld(
"阿蜜果");
       
       American american 
= new American();
       american.sayHelloWorld(
"Amigo");
     }

 }


    觀察HelloWorldTest咱們能夠很清楚的看到,該類與Chinese.java類和American.java類都存在強耦合關係。

上面的例子讓咱們想到的是在N年之前,當咱們須要某個東西時,咱們通常是本身製造。可是當發展到了必定的階段後,工廠出現了,咱們能夠工廠中購買咱們須要的東西,這極大的方便了咱們。在上例中,咱們都是經過new來建立新的對象,在開發中,這種強耦合關係是咱們所不提倡的,那麼咱們應該如何來實現這個例子的解耦呢?咱們接着想到了使用工廠模式,咱們須要新建一個工廠類來完成對象的建立,並採用依賴接口的方式,此時須要對代碼進行以下修改:

首先創建接口類Human.java,其內容以下:

package  org.amigo.reflection;


public   interface  Human  {

    

    
public void sayHelloWorld(String name);
 }


並將American.java類和Chinese.java類改成實現該接口,即類頭分別改爲:public class American implements Humanpublic class Chinese implements Human

接着編寫HumanFactory.java工廠類,其內容爲

package  org.amigo.reflection;


public   class  HumanFactory  {

       

       
public Human getHuman(String type) {
              
if ("chinese".equals(type)) {
                     
return new Chinese();
              }
 else {
                     
return new American();
               }

        }

 }


最後咱們還須要修改測試類HelloWorld.java類,修改後的內容以下:

package  org.amigo.reflection;


public   class  HelloWorldTest  {

       

       
public static void main(String[] args) {
              HumanFactory factory 
= new HumanFactory();
              Human human1 
= factory.getHuman("chinese");
              human1.sayHelloWorld(
"阿蜜果");

              Human human2 
= factory.getHuman("american");
              human2.sayHelloWorld(
"Amigo");
               }

 }

觀察此例咱們能夠看到,該類再也不與具體的實現類ChineseAmerican存在耦合關係,而只是與它們的接口類Human存在耦合關係,具體對象的獲取只是經過傳入字符串來獲取,很大程度上下降了類與類之間的耦合性。

可是咱們仍是不太知足,由於還須要經過chineseamerican在類中獲取實例,那麼當咱們須要修改時實現時,咱們還須要在類中修改這些字符串,那麼還有沒有更好的辦法呢?讓咱們在下節中進行繼續探討。

三.             IOC粉墨登場

IOCInverse of Control)可翻譯爲「控制反轉」,但大多數人都習慣將它稱爲「依賴注入」。在Spring中,經過IOC能夠將實現類、參數信息等配置在其對應的配置文件中,那麼當須要更改實現類或參數信息時,只須要修改配置文件便可,這種方法在上例的基礎上更進一步的下降了類與類之間的耦合。咱們還能夠對某對象所須要的其它對象進行注入,這種注入都是在配置文件中作的,SpringIOC的實現原理利用的就是Java的反射機制, Spring還充當了工廠的角色,咱們不須要本身創建工廠類。Spring的工廠類會幫咱們完成配置文件的讀取、利用反射機制注入對象等工做,咱們能夠經過bean的名稱獲取對應的對象。

下面讓咱們看看以下的模擬Springbean工廠類:

package  org.amigo.reflection;

import  java.io.InputStream;
import  java.lang.reflect.Method;
import  java.util.HashMap;
import  java.util.Iterator;
import  java.util.Map;
import  org.dom4j.Attribute;
import  org.dom4j.Document;
import  org.dom4j.Element;
import  org.dom4j.io.SAXReader;


public   class  BeanFactory  {
       
private Map<String, Object> beanMap = new HashMap<String, Object>();

       

       
public void init(String xml) {
              
try {
                     
//讀取指定的配置文件
                     SAXReader reader = new SAXReader();
                     ClassLoader classLoader 
= Thread.currentThread().getContextClassLoader();
                     
//從class目錄下獲取指定的xml文件
                     InputStream ins = classLoader.getResourceAsStream(xml);
                     Document doc 
= reader.read(ins);
                     Element root 
= doc.getRootElement();   
                     Element foo;
                     
//遍歷bean
                     for (Iterator i = root.elementIterator("bean"); i.hasNext();) {   
                            foo 
= (Element) i.next();
                            
//獲取bean的屬性id和class
                            Attribute id = foo.attribute("id");   
                            Attribute cls 
= foo.attribute("class");
                            
//利用Java反射機制,經過class的名稱獲取Class對象
                            Class bean = Class.forName(cls.getText());
                            
//獲取對應class的信息
                            java.beans.BeanInfo info = java.beans.Introspector.getBeanInfo(bean);
                            
//獲取其屬性描述
java.beans.PropertyDescriptor pd[] = info.getPropertyDescriptors();
                            
//設置值的方法
                            Method mSet = null;
                            
//建立一個對象
                            Object obj = bean.newInstance();
                            
//遍歷該bean的property屬性
                            for (Iterator ite = foo.elementIterator("property"); ite.hasNext();) {   
                                   Element foo2 
= (Element) ite.next();
                                   
//獲取該property的name屬性
                                   Attribute name = foo2.attribute("name");
                                  String value 
= null;
                                   
//獲取該property的子元素value的值
                                   for(Iterator ite1 = foo2.elementIterator("value"); ite1.hasNext();) {
                                          Element node 
= (Element) ite1.next();                                          value = node.getText();
break;
                                    }

                                   
for (int k = 0; k < pd.length; k++{
                                          
if (pd[k].getName().equalsIgnoreCase(name.getText())) {                                             mSet = pd[k].getWriteMethod();
//利用Java的反射極致調用對象的某個set方法,並將值設置進去                                              mSet.invoke(obj, value);
 
}

 }

 }


//將對象放入beanMap中,其中key爲id值,value爲對象
beanMap.put(id.getText(), obj);
 }

}
 catch (Exception e) {
    System.out.println(e.toString());
 }

 }


       

       
public Object getBean(String beanName) {
              Object obj 
= beanMap.get(beanName);
              
return obj;
        }


       

       
public static void main(String[] args) {
             BeanFactory factory 
= new BeanFactory();
              factory.init(
"config.xml");
              JavaBean javaBean 
= (JavaBean) factory.getBean("javaBean");
              System.out.println(
"userName=" + javaBean.getUserName());
              System.out.println(
"password=" + javaBean.getPassword());
        }

 }

該類的init(xml)方法,經過指定的xml來給對象注入屬性,爲了對該類進行測試,我還須要新建一個JavaBean和在src目錄下新建一個名爲config.xml的配置文件。JavaBean的內容以下:

package  org.amigo.reflection;


public   class  JavaBean  {
       
private String userName;
       
private String password;

    
public String getPassword() {
              
return password;
        }


       
public String getUserName() {
              
return userName;
        }


       
public void setUserName(String userName) {
              
this.userName = userName;
        }


       
public void setPassword(String password) {
              
this.password = password;
        }

 }

 

這個簡單bean對象中有兩個屬性,分別爲userNamepassword,下面咱們在配置文件config.xml中對其屬性注入對應的屬性值。配置文件內容以下:

<? xml version="1.0" encoding="UTF-8" ?>

< beans >
    
< bean  id ="javaBean"  class ="org.amigo.reflection.JavaBean" >
       
< property  name ="userName" >
           
< value > 阿蜜果 </ value >
       
</ property >
       
< property  name ="password" >
           
< value > 12345678 </ value >
       
</ property >
    
</ bean >
</ beans >

類與配置文件都完成後,能夠運行BeanFactory.java文件,控制檯顯示內容爲:

userName=阿蜜果

password=12345678

能夠看到,雖然在main()方法中沒有對屬性賦值,但屬性值已經被注入,在BeanFactory類中的Class bean = Class.forName(cls.getText());經過類名來獲取對應的類,mSet.invoke(obj, value);經過invoke方法來調用特定對象的特定方法,實現的原理都是基於Java的反射機制,在此咱們有一次見證了Java反射機制的強大。

固然,這只是對IOC的一個簡單演示,在Spring中,狀況要複雜得多,例如,能夠一個bean引用另外一個bean,還能夠有多個配置文件、經過多種方式載入配置文件等等。不過原理仍是採用Java的反射機制來實現IOC的。

四.             總結

在本文中,筆者經過講述Java反射機制概述與初探、IOC使用的背景、IOC粉墨登場等內容,演示了Java反射機制API的強大功能,並經過編寫本身的簡單的IOC框架,讓讀者更好的理解了IOC的實現原理。

本文經過IOC的一個簡要實現實例,模擬了SpringIOC的實現,雖然只是完成了Spring中依賴注入的一小部分工做,可是很好的展示了Java反射機制在Spring中的應用,能使咱們能更好的從原理上了解IOC的實現,也能爲咱們實現本身的準Spring框架提供方案,有興趣的朋友能夠經過Spring的源碼進行IOC的進一步的學習。

相關文章
相關標籤/搜索