用反射和内省技术实现简单 SpringIOC

转自:http://blog.csdn.net/newjueqi/archive/2009/06/03/4238602.aspx

【文章标题】用反射和内省技术实现简单 SpringIOC

【文章作者】曾健生

【作者邮箱】 zengjiansheng1@126.com

【作者 QQ 】 190678908

【作者博客】 http://blog.csdn.net/newjueqi

【作者声明】欢迎转载文章,但转载请保留文章的完整性以及注明文章的出处。

 

*******************************************************************************

       我们知道, spring 是个开源的控制反转( Inversion of  Control , IoC )和面向切片( AOP )的容器框架。所谓控制反转就是应用本身不负责依赖对象的创建和维护,依赖对象的创建及维护是由外部容器负责的。这样控制权就由应用转移到外部容器,控制权的转移就是所谓的反转。

       我们把需要创建的对象信息写在一个 XML 文件中,每次需要维护对象时只需要修改 XML 文件中的配置信息,不需要重新更改和编译代码,为项目开发带来了极大的方便。

 

反射

       在以前的文章《 JDK5.0 新特性( )——反射》 http://blog.csdn.net/newjueqi , http://newjueqi.javaeye.com/) 中就已经介绍过反射技术,有了反射技术,就能方便地根据 XML 的配置创建对象。那对象的维护应该怎么实现呢?这就需要内省技术出马了 ^-^

 

内省

       内省是 Java 中 Bean 类属性的一种缺省的处理方式。例如,有一个类 Person ,其中有个 age 属性,可通过 setAge ()和 getAge ()方法获取 设置 age 的值。通过 setAge() /getAge ()获取 设置 age 的值是 Java 中的默认的处理规则, Java 提供了一套 API 来访问某个属性的 setAge () /getAge ()。

通用的用法通过类 Introspector 来获取某个对象的 BeanInfo 信息,然后通过 BeanInfo 来获取属性的描述器( PropertyDescriptor ),通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法,然后通过反射机制来调用这些方法

      

 

实现简单 SpringIOC

       通过反射技术,就能动态地创建对象;通过内省技术,就能方便地维护对象。通过以上的两个技术基础,就能实现简单的 SpringIOC 

 

譬如, XML 文件中有如下的配置信息

    < bean id = "personDAO" class = "org.petrelsky5.dao.impl.PersonDAOImpl" />

   

    < bean id = "personService" class = "org.petrelsky5.service.impl.PersonServiceImpl" >

       < property name = "personDAO" ref = "personDAO" />

       < property name = "name" value = "Tom12345" ></ property >

       < property name = "age" value = "12" ></ property >

    </ bean >

 

 

其中 SpringTest.java 文件中 instanceSpring() 有如下测试代码:

 

ClassPathXmlApplicationContext ctx = null ;

       ctx = new ClassPathXmlApplicationContext( "beans.xml" );

 

       PersonDAO personService = (PersonDAO) ctx

              .getBean( "personDAO" );

       personService.add();

 

其中的调用关系如下图 1 :

 

                                                        图 1

 

在 SpringTest.java 文件中 instanceSpring2() 有如下测试代码:

 

       ClassPathXmlApplicationContext ctx = null ;

       ctx = new ClassPathXmlApplicationContext( "beans.xml" );

 

       PersonService personService = (PersonService) ctx

              .getBean( "personService" );

       personService.save();

 

其中的调用关系如下图 2 :

 

                                                        图 2

 

由此可知道:

(1)        类 ClassPathXmlApplicationContext 相当于一个容器。

(2)        在 XML 文件的节点 bean 中,各部分的内容解释如下:

A.  id :类的标识。

B. class :类的所在路径。

C. property :属性信息

①      name :属性名称。

②      value: 如果属性是值类型的,那么 value 是相应的属性值。

③      ref :如果属性是引用类型(譬如 PersonServiceImpl 类中的属性 personDAO ,指向类 PersonDAO ),那么 ref 是所指向的对象。

 

 

我们根据在 XML 文件信息和 类 ClassPathXmlApplicationContext 的功能,讨论一下相关的数据怎么存储(即数据结构)。

( 1 )在 类 ClassPathXmlApplicationContext 中有个方法 getBean ,可根据 id 值获取一个实际的对象,即 id 和对象是一一对应的关系,所以定义一个 Map 集合:

    Map<String,Object>

其中, 键为 id, 值 Object 为对应的对象。

 

( 2 ) 虽然现在编写的 XMl 文件中只有两个 Bean 节点,但我们都必须用面向对象的思想把 Bean 节点对象化,每个 bean 有以下三个属性:

1.    id

2.    class

3.    bean 节点(数量不确定)

 

由于 bean 节点的数量是不确定,对于数量不确定的数据的存储,自然地想到了用集合。

而每个 bean 又有三个属性:

1.    name :属性名称

2.    value :属性的数据值(可能有)

3.    ref :属性所指向的引用值(可能有)

 

所有的数据结构关系如图 3 所示:

 

                           图 3

 

 

    现在数据的相互关系已经清楚了,接下来就是程序的流程。

    我们在类 ClassPathXmlApplicationContext 初始化时只传入了 XML 文件的名称,以后只是在有需要时用 getBean ()获取 id 制定的对象,所以应该在类初始化时就把所有的对象创建完毕。

    根据如上的描述,可以把初始化分为以下三步:

1.    把 XML 的信息保存到相关的类中

2.    初始化每个 Bean , 把 id 值和 class 对应的对象保存在 Map 集合中

3.    设置 XMl 文件中每个 Bean 指定的值

 

这三步的代码如下:

1. 把 XML 的信息保存到相关的类中

// 把 XML 的信息保存到相关的类中

    public void initXML( String fileName )

    {

       URL url= null ;

       Document doc= null ;

      

       // 获取 XML 文件的 url

       url= this .getClass().getClassLoader().getResource( fileName );

       SAXReader saxreader= new SAXReader();

      

       try {

           // 获取 XML 的文档对象

           doc=saxreader.read( url);

       } catch (DocumentException e) {

           // TODO Auto-generated catch block

           e.printStackTrace();

           return ;

        }

      

       // 获取根节点

       List<Element> beans=doc.getRootElement().elements( "bean" ) ;

      

       // 遍历所有的 Bean 节点,把 class , id , property 保存起来

       for ( Element ele: beans )

       {

           // 创建一个 BeanInfo 对象

           BeanInfomation beanInfo= null ;

           beanInfo= new BeanInfomation( ele.attributeValue( "class" ),

                               ele.attributeValue( "id" ) );

          

           // 获取一个 Bean 中的所有 property

           List<Element> properties=ele.elements( "property" ) ;

          

           // 遍历所有的 property 信息

           for ( Element pro: properties )

           {

              Property property= null ;

             

              // 把 property 信息保存

              property= new Property(  pro.attributeValue( "name" ),

                                   pro.attributeValue( "value" ),

                                   pro.attributeValue( "ref" ) );

             

              beanInfo.addProperty( property );

           }

             

           beanList .add( beanInfo );         

       }

    }

 

2. 初始化每个 Bean ,把 id 值和 class 对应的对象保存在 Map 集合中

 

public void initBean ()

    {

       for ( BeanInfomation beaninfo: beanList )

       {

           String id=beaninfo.getId();

           String className=beaninfo.getClassName();

           Object obj= null ;

          

           try {

              // 用反射获取一个类的对象

              obj=Class.forName ( className ).newInstance();

           } catch ( Exception e) {

              // TODO Auto-generated catch block

              e.printStackTrace();

           }

          

           // 把 id 值和 class 对应的对象保存在 Map 集合中

           objectMap .put( id, obj );

       }

    }

 

3. 设置 XMl 文件中每个 Bean 指定的值

    public void initBeanProperty()

    {

       List<BeanInfomation> beanList=getBeanList();

      

       List<Property> proList= null ;

      

       // 遍历每个 Bean 类,读取其中的信息

       for ( BeanInfomation beaninfo: beanList)

       {

           String id=beaninfo.getId();

          

           // 根据 ID 值从 Map 集合中获取对象

           Object obj= objectMap .get(id);

          

           BeanInfo beanInfos= null ;

          

           // 获取 bean 中的属性值集合

           proList=beaninfo.getPerprotyList();

          

           for ( Property pro: proList )

           {

              String name=pro.getName();

              String ref=pro.getRef();

              Object value= null ;

             

              // 判断 ref 是否为空

              if ( ref!= null )

              {

                  value= objectMap .get( ref );

                 

              }

              else

              {

                  value=pro.getValue();

              }

             

              Class clazz=obj.getClass();

             

              try {

                  beanInfos = Introspector.getBeanInfo (clazz);

              } catch (IntrospectionException e1) {

                  // TODO Auto-generated catch block

                  e1.printStackTrace();

              }

              PropertyDescriptor[] propertyDescriptes=

                  beanInfos.getPropertyDescriptors();

             

              for ( PropertyDescriptor propertyDescript:propertyDescriptes )

              {

                  if ( name!= null && name.equals( propertyDescript.getName()))

                  {

                     try {

                         // 设置指定属性的值

                         propertyDescript.getWriteMethod().invoke( obj,value );

                     } catch (Exception e) {

                         // TODO Auto-generated catch block

                         e.printStackTrace();

                     }

                     break ;

                  }

              }

             

           }

       }

    }

 

 

最后就是完成 getBean 函数

 

// 获取对象

    public Object getBean( String beanName )

    {

       Object obj= null ;

       obj= objectMap .get(beanName);

       return obj;

    }

 

测试:

 

1.     运行测试代码:

public void instanceSpring() {

       ClassPathXmlApplicationContext ctx = null ;

       ctx = new ClassPathXmlApplicationContext( "beans.xml" );

 

       PersonDAO personService = (PersonDAO) ctx

              .getBean( "personDAO" );

       personService.add();

 

    }

 

输出结果如图 4 :

 

                            图 4

 

2.    运行测试代码:

public void instanceSpring2() {

       ClassPathXmlApplicationContext ctx = null ;

       ctx = new ClassPathXmlApplicationContext( "beans.xml" );

 

       PersonService personService = (PersonService) ctx

              .getBean( "personService" );

       personService.save();

    }

运行结果如图 5 :

 

              图 5

 

3.    运行测试代码:

public void instanceSpring3(){

       ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext( "beans.xml" );

       PersonServiceImpl ps = (PersonServiceImpl) ctx.getBean( "personService" );

      

       ps.save();

       String name = ps.getName();

       System. out .println( "name: " + name);

       System. out .println( "age: " + ps.getAge());

    }

运行结果如图 6 :

 

 

                  图 6

 

    ^-^ 结果证明代码编写正确!!!

 

源码下载地址:https://docs.google.com/leaf?id=0B3cFfe7PkmkxYzUwMGNkYzItMTUwNS00ZTAyLTg1YTctZmIxZGViZTgwZWJm&sort=name&layout=list&num=50

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值