您的位置:首页 > 编程语言 > Java开发

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

2009-12-03 14:38 796 查看

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的进一步的学习。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息