当前位置: 首页 > news >正文

用反射和内省技术实现简单 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

相关文章:

  • [杂谈]Executor-1
  • spring中的设计模式
  • SpringMVC+RestFul详细示例实战教程
  • Struts工作原理
  • 【git】git知识梳理(一):基本操作远程控制分支管理
  • Java 反射机制模拟hibernate实现持久化
  • JNDI是什么?
  • OI队测题解:
  • JDBC使用总结
  • freeCodeCamp:Convert HTML Entities
  • JDBC中驱动加载的过程分析(上)
  • JavaScript--substring 和 substr 方法手记
  • 颜色选择器插件
  • JDBC中驱动加载的过程分析(下)
  • 条件控制语句
  • angular组件开发
  • - C#编程大幅提高OUTLOOK的邮件搜索能力!
  • canvas实际项目操作,包含:线条,圆形,扇形,图片绘制,图片圆角遮罩,矩形,弧形文字...
  • CSS实用技巧干货
  • ECS应用管理最佳实践
  • gcc介绍及安装
  • JavaScript的使用你知道几种?(上)
  • Linux链接文件
  • Vim Clutch | 面向脚踏板编程……
  • 分享一个自己写的基于canvas的原生js图片爆炸插件
  • 关于springcloud Gateway中的限流
  • 计算机在识别图像时“看到”了什么?
  • 面试遇到的一些题
  • 面试总结JavaScript篇
  • 思维导图—你不知道的JavaScript中卷
  • 算法-图和图算法
  • 小试R空间处理新库sf
  • 蚂蚁金服CTO程立:真正的技术革命才刚刚开始
  • ​​​​​​​sokit v1.3抓手机应用socket数据包: Socket是传输控制层协议,WebSocket是应用层协议。
  • ​iOS实时查看App运行日志
  • #ubuntu# #git# repository git config --global --add safe.directory
  • (MonoGame从入门到放弃-1) MonoGame环境搭建
  • (附源码)spring boot校园拼车微信小程序 毕业设计 091617
  • (三)Hyperledger Fabric 1.1安装部署-chaincode测试
  • (原創) 如何將struct塞進vector? (C/C++) (STL)
  • (转)一些感悟
  • .net Application的目录
  • .net core 连接数据库,通过数据库生成Modell
  • .NET MVC之AOP
  • .net 提取注释生成API文档 帮助文档
  • .NET企业级应用架构设计系列之结尾篇
  • .NET委托:一个关于C#的睡前故事
  • .Net下的签名与混淆
  • [ 攻防演练演示篇 ] 利用通达OA 文件上传漏洞上传webshell获取主机权限
  • [Android]常见的数据传递方式
  • [C++]类和对象【上篇】
  • [Git 1]基本操作与协同开发
  • [IE编程] WebBrowser控件的多页面浏览(Tabbed Browsing)开发接口
  • [LeetCode][面试算法]逻辑闭环的二分查找代码思路
  • [linux] 创建用户