利用Java的反射与代理机制实现IOC

Java中,其反射和动态代理机制极其强大,我们可以通过其反射机制在运行时获取信息。而代理是一种基本的设计模式,它是一种为了提供额外的或不同的操作而插入到真实对象中的某个对象。而 Java的动态代理在代理上更进一步,既能动态的创建代理对象,又能动态的调用代理方法。 Java的反射和动态代理机制,使 Java变得更加强大。

       Spring框架这几年风头正劲,虽然使用者众多,但真正了解其内部实现原理的朋友却并不是很多。其实,了解它的内部实现机制和设计思想是很有必要的大家都知道, Spring框架的 IOC AOP部分功能强大,很值得我们学习。那么让我们在这两篇文章中分别详细的学习 IOC AOP的实现吧。

在本文中,主要讲述的是用 Java的反射机制实现 IOC。下面,让我们开始 IOC之旅吧!

一.             Java 反射机制概述与初探

Java的反射机制是 Java语言的一个重要特性, Java具有的比较突出的动态机制就是反射( reflection)。通过它,我们可以获取如下信息:

1) 在运行时判断任意一个对象所属的类;

2) 在运行时获取类的对象;

3) 在运行时获得类所具有的成员变量和方法等。

下面让我们通过调用一个 Java Reflection API的演示实例来见识一下反射机制的强大。

首先在 IDE中建立名为 reflection_proxy Java工程,并建立存放源文件的目录 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;

/**
 *初探Java的反射机制.   
 *
@author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
 *Creationdate:2007-10-2-上午10:13:48
 
*/

publicclass ReflectionTest 
{

    
/**
     *@paramargs
     
*/

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


    
/**
     *Class的getName()方法测试.
     *@throwsException
     
*/

    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============ " );
    }

    
    
/**
     *Class的getMethod()方法测试.
     *@throwsException
     
*/

    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;

/**
 *中国人类.   
 *
@author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
 *Creationdate:2007-10-2-上午10:37:17
 
*/

publicclass Chinese 
{

    
/**
     *用中文对某人问好.
     *@paramname姓名
     
*/

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

}

 

下面我们接着建立一个 American.java 类, 该类的 sayHelloWorld(String name)方法,用英文对名为 name的人问好,其内容如下:

package  org.amigo.reflection;

/**
 *美国人类.   
 *
@author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
 *@version1.0
 *Creationdate:2007-10-2-上午10:41:27

 
*/

publicclass American 
{

    
/**
     *用英文对某人问好.
     *@paramname姓名
     
*/

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

}

 

    最后我们编写一个测试类对这两个类的 sayHelloWorld(String name) 方法进行测试,下面是该类的内容:

package  org.amigo.reflection;

/**
 *HelloWorld测试.
 *
@author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
 *Creationdate:2007-10-2-上午10:45:13
 
*/

publicclass HelloWorldTest 
{

    
/**
     *测试Chinese和American的sayHelloWorld()方法.
     *@paramargs
     *
@author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
     *Creationdate:2007-10-2-上午10:43:51
     
*/

    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;

/**
 * 人类接口类.    
 * 
@author  <a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
 * Creation date: 2007-10-2 - 上午11:04:56
 
*/

public   interface  Human  {

    
/**
     * 对某人问好.
     * 
@param  name 姓名
     
*/

    
public   void  sayHelloWorld(String name);
}

 

并将 American.java类和 Chinese.java类改为实现该接口,即类头分别改成: public class American implements Human public class Chinese implements Human

接着编写 HumanFactory.java工厂类,其内容为

package  org.amigo.reflection;

/**
 * 工厂类.    
 * 
@author  <a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
 * Creation date: 2007-10-2 - 上午11:09:30
 
*/

public   class  HumanFactory  {

       
/**
        * 通过类型字段获取人的相应实例
        * 
@param  type 类型
        * 
@return  返回相应实例
        
*/

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

       }

}

 

最后我们还需要修改测试类 HelloWorld.java类,修改后的内容如下:

package  org.amigo.reflection;

/**
 * HelloWorld测试.
 * 
@author  <a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
 * Creation date: 2007-10-2 - 上午10:45:13
 
*/

public   class  HelloWorldTest  {

       
/**
        * 测试sayHelloWorld()方法.
        * 
@param  args
        * 
@author  <a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
        * Creation date: 2007-10-2 - 上午10:43:51
        
*/

       
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 " );
              }

}

观察此例我们可以看到,该类不再与具体的实现类 Chinese American存在耦合关系,而只是与它们的接口类 Human存在耦合关系,具体对象的获取只是通过传入字符串来获取,很大程度上降低了类与类之间的耦合性。

但是我们还是不太满足,因为还需要通过 chinese american在类中获取实例,那么当我们需要修改时实现时,我们还需要在类中修改这些字符串,那么还有没有更好的办法呢?让我们在下节中进行继续探讨。

三.             IOC 粉墨登场

IOC Inverse of Control )可翻译为“控制反转”,但大多数人都习惯将它称为“依赖注入”。在 Spring中,通过 IOC可以将实现类、参数信息等配置在其对应的配置文件中,那么当需要更改实现类或参数信息时,只需要修改配置文件即可,这种方法在上例的基础上更进一步的降低了类与类之间的耦合。我们还可以对某对象所需要的其它对象进行注入,这种注入都是在配置文件中做的, Spring IOC的实现原理利用的就是 Java的反射机制, Spring还充当了工厂的角色,我们不需要自己建立工厂类。 Spring的工厂类会帮我们完成配置文件的读取、利用反射机制注入对象等工作,我们可以通过 bean的名称获取对应的对象。

下面让我们看看如下的模拟 Spring bean工厂类:

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;

/**
 * bean工厂类.    
 * 
@author  <a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
 * Creation date: 2007-10-6 - 上午10:04:41
 
*/

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

       
/**
        * bean工厂的初始化.
        * 
@param  xml xml配置文件
        
*/

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

}


       
/**
        * 通过bean的id获取bean的对象.
        * 
@param  beanName bean的id
        * 
@return  返回对应对象
        
*/

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


       
/**
        * 测试方法.
        * 
@param  args
        * 
@author  <a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
        * Creation date: 2007-10-6 - 上午11:21:14
        
*/

       
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;

/**
 * 
 * 简单的bean,用于测试   
 * 
@author  <a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
 * Creation date: 2007-10-6 - 上午11:24:30
 
*/

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对象中有两个属性,分别为 userName password,下面我们在配置文件 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的一个简要实现实例,模拟了 Spring IOC的实现,虽然只是完成了 Spring中依赖注入的一小部分工作,但是很好的展现了 Java反射机制在 Spring中的应用,能使我们能更好的从原理上了解 IOC的实现,也能为我们实现自己的准 Spring框架提供方案,有兴趣的朋友可以通过 Spring的源码进行 IOC的进一步的学习。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值