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

2023-05-16

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

SpringIOC

 

 

【文章作者】曾健生

【作者邮箱】zengjiansheng1@126.com

【作者QQ190678908

【作者博客】http://newjueqi.iteye.com/

                http://blog.csdn.net/newjueqi

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

 

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

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

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

 

反射

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

 

内省

       内省是JavaBean类属性的一种缺省的处理方式。例如,有一个类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

 

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

 

 

 

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

用反射和内省技术实现简单SpringIOC 的相关文章

随机推荐