【文章标题】用反射和内省技术实现简单
SpringIOC
【文章作者】曾健生
【作者邮箱】zengjiansheng1@126.com
【作者QQ】190678908
【作者博客】http://newjueqi.iteye.com/
http://blog.csdn.net/newjueqi
【作者声明】欢迎转载文章,但转载请保留文章的完整性以及注明文章的出处。
*******************************************************************************
我们知道,spring是个开源的控制反转(Inversion of Control,IoC)和面向切片(AOP)的容器框架。所谓控制反转就是应用本身不负责依赖对象的创建和维护,依赖对象的创建及维护是由外部容器负责的。这样控制权就由应用转移到外部容器,控制权的转移就是所谓的反转。
我们把需要创建的对象信息写在一个XML文件中,每次需要维护对象时只需要修改XML文件中的配置信息,不需要重新更改和编译代码,为项目开发带来了极大的方便。
反射
在以前的文章《JDK5.0新特性(2)——反射》(http://blog.csdn.net/newjueqi)中就已经介绍过反射技术,有了反射技术,就能方便地根据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
^-^结果证明代码编写正确!!!
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)