http://zhangjunhd.blog.51cto.com/113473/126545
这里将模仿Spring实现一种基于xml配置文件的依赖注入机制。文件中将实现3中注入,一是单值注入,包括int,float,double,char等,也包括String注入;二是Java容器注入,包括List,Set,Map三种容器的注入,最后一种是java bean对象注入。
实现的机制是,使用Dom4j对xml配置文件进行解析,这里使用dom4j的Element Handler机制,一种类似与责任链模式的实现机制;对于java对象的构建使用反射机制,这里主要是针对得到的类的Field进行set赋值。我试图通过调用Method的invoke方法调用类本身的setter方法,但是由于通过xml解析得到的值都是String,如果将这些String动态的转换为相应的确定类型是个难点,Method的invoke方法,如果形参是int,而传入java.lang.Integer,它不会认,所以尝试失败,只能通过Field的set方法传入特定值。
配置文件setting.xml
<?xml version="1.0" encoding="UTF-8"?> <beans> <bean id="me" class="com.zj.ioc.di.imp.Person"> <property name="name"> <value>ZJ</value> </property> <property name="age"> <value>26</value> </property> <property name="height"> <value>1.78</value> </property> </bean> <bean id="you" class="com.zj.ioc.di.imp.Person"> <property name="name"> <value>Mary</value> </property> <property name="age"> <value>27</value> </property> <property name="height"> <value>1.66</value> </property> </bean> <bean id="myList" class="com.zj.ioc.di.imp.ListOne"> <property name="msg"> <list> <value>java</value> <value>c</value> <value>windows</value> </list> </property> </bean> <bean id="mySet" class="com.zj.ioc.di.imp.SetOne"> <property name="msg"> <set> <value>tom</value> <value>cat</value> <value>dog</value> </set> </property> </bean> <bean id="myMap" class="com.zj.ioc.di.imp.MapOne"> <property name="msg"> <map> <entry key="c"> <value>CHINA</value> </entry> <entry key="j"> <value>JAPAN</value> </entry> <entry key="k"> <value>KOREA</value> </entry> </map> </property> </bean> <bean id="us" class="com.zj.ioc.di.imp.Persons"> <property name="i"> <ref bean="me" /> </property> <property name="u"> <ref bean="you" /> </property> </bean> </beans> |
依据setting.xml,这里将构建两个Person类的实例me和you:
Person.java
package com.zj.ioc.di.imp; public class Person { private String name; private int age; private float height; public String getName() {return name;} public void setName(String name) {this.name = name;} public int getAge() {return age;} public void setAge(int age) {this.age = age;} public float getHeight() {return height;} public void setHeight(float height) {this.height = height;} } |
紧接着,构建一个ListOne的实例myList:
ListOne.java
package com.zj.ioc.di.imp; import java.util.List; public class ListOne { private List<String> msg; public List<String> getMsg() {return msg;} public void setMsg(List<String> msg) {this.msg = msg;} } |
紧接着,构建一个SetOne的实例mySet:
SetOne.java
package com.zj.ioc.di.imp; import java.util.Set; public class SetOne { private Set<String> msg; public Set<String> getMsg() {return msg;} public void setMsg(Set<String> msg) {this.msg = msg;} } |
紧接着,构建一个MapOne的实例myMap:
MapOne.java
package com.zj.ioc.di.imp; import java.util.Map; public class MapOne { private Map<String,String> msg; public Map<String, String> getMsg() {return msg;} public void setMsg(Map<String, String> msg) {this.msg = msg;} } |
最后构建一个Persons类的实例us,其中包含me和you两个已经构建好的对象:
Persons.java
package com.zj.ioc.di.imp; public class Persons { private Person i; private Person u; public Person getI() {return i;} public void setI(Person i) {this.i = i;} public Person getU() {return u;} public void setU(Person u) {this.u = u;} } |
主要的实现机制是(代码BeanFactory.java以及工程见附件),
1.通过一个HashMap保存构造好的对象,key就是bean的id属性,value就是这个对象;
private Map<String, Object> beanMap = new HashMap<String, Object>(); …… public Object getBean(String beanId) { Object obj = beanMap.get(beanId); return obj; } |
查询时
BeanFactory factory = new BeanFactory(); factory.init("setting.xml"); Person p1 = (Person) factory.getBean("me"); |
2.init方法读入配置文件setting.xml,并直接定位到beans下的bean元素,并实例化一个ElementHandler对其处理。
public void init(String xmlUri) throws Exception { SAXReader saxReader = new SAXReader(); File file = new File(xmlUri); try { saxReader.addHandler("/beans/bean", new BeanHandler()); saxReader.read(file); } catch (DocumentException e) { System.out.println(e.getMessage()); } } |
3.ElementHandler,dom4j的ElementHandler接口有两个方法,一个是onStart(),它主要用于处理该元素的属性以及动态增加新的Handler类;另一个是onEnd(),它主要用于获得该元素的Text文本以及删除已添加的Handler。
BeanHandler
private class BeanHandler implements ElementHandler { Object obj = null; public void .Start(ElementPath path) { Element beanElement = path.getCurrent(); Attribute classAttribute = beanElement.attribute("class"); Class<?> bean = null; try { bean = Class.forName(classAttribute.getText()); } catch (ClassNotFoundException e) { e.printStackTrace(); } Field fields[] = bean.getDeclaredFields(); Map<String, Field> mapField = new HashMap<String, Field>(); for (Field field : fields) mapField.put(field.getName(), field); try { obj = bean.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } path.addHandler("property", new PropertyHandler(mapField, obj)); } public void .End(ElementPath path) { Element beanElement = path.getCurrent(); Attribute idAttribute = beanElement.attribute("id"); beanMap.put(idAttribute.getText(), obj); path.removeHandler("property"); } } |
PropertyHandler
private class PropertyHandler implements ElementHandler { Map<String, Field> mapField; Object obj; public PropertyHandler(Map<String, Field> mapField, Object obj) { this.mapField = mapField; this.obj = obj; } public void .Start(ElementPath path) { Element propertyElement = path.getCurrent(); Attribute nameAttribute = propertyElement.attribute("name"); path.addHandler("value", new ValueHandler(mapField, obj, nameAttribute)); path.addHandler("list", new ListHandler(mapField, obj, nameAttribute)); path.addHandler("set", new SetHandler(mapField, obj, nameAttribute)); path.addHandler("map", new MapHandler(mapField, obj, nameAttribute)); path.addHandler("ref", new RefHandler(mapField, obj, nameAttribute)); } public void .End(ElementPath path) { path.removeHandler("value"); path.removeHandler("list"); path.removeHandler("set"); path.removeHandler("map"); path.removeHandler("ref"); } } |
根据setting.xml,我们可以得到各种注入元素的Handler类处理流程图。
4. setFieldValue()基于反射机制和相应的类信息得到Field的类型,并根据setting.xml设置它的值。
private void setFieldValue(Object obj, Field field, String value) { String fieldType = field.getType().getSimpleName(); try { if (fieldType.equals("int")) field.setInt(obj, new Integer(value)); else if (fieldType.equals("float")) field.setFloat(obj, new Float(value)); else if (fieldType.equals("boolean")) field.setBoolean(obj, new Boolean(value)); else if (fieldType.equals("char")) field.setChar(obj, value.charAt(0)); else if (fieldType.equals("double")) field.setDouble(obj, new Double(value)); else if (fieldType.equals("long")) field.setLong(obj, new Long(value)); else field.set(obj, value); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } private void setFieldValue(Object obj, Field field, List<String> value) { try { field.set(obj, value); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } |
5.测试
public static void main(String[] args) { try { BeanFactory factory = new BeanFactory(); factory.init("setting.xml"); Person p1 = (Person) factory.getBean("me"); System.out.print(p1.getName() + " "); System.out.print(p1.getAge() + " "); System.out.println(p1.getHeight()); Person p2 = (Person) factory.getBean("you"); System.out.print(p2.getName() + " "); System.out.print(p2.getAge() + " "); System.out.println(p2.getHeight()); ListOne list = (ListOne) factory.getBean("myList"); System.out.println(list.getMsg()); SetOne set = (SetOne) factory.getBean("mySet"); System.out.println(set.getMsg()); MapOne map = (MapOne) factory.getBean("myMap"); System.out.println(map.getMsg()); Persons us = (Persons) factory.getBean("us"); System.out.println(us.getI()); System.out.println(us.getU()); } catch (Exception e) { e.printStackTrace(); } } |
测试结果:
ZJ 26 1.78
Mary 27 1.66
[java, c, windows]
[cat, tom, dog]
{c=CHINA, j=JAPAN, k=KOREA}
com.zj.ioc.di.imp.Person@1a5ab41
com.zj.ioc.di.imp.Person@18e3e60