首先看要解析的XML,XML验证使用的是Spring的xsd来验证,因为Spring实现的功能比较强大,但是这里只是实现了部分功能
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="str1" class="java.lang.String" value="str1" /> <bean id="str2" class="java.lang.String" value="string test " /> <!--基本数据类型待支持--> <bean id="outerBean" class="com.emmet.core.factory.HelloWorldService"> <property name="a" value="11111"/> <property name="aa" value="222222"/> <property name="b" value="我是外部Bean,有Map+property注入哦"/> <property name="map"> <map> <entry key="s1" value-ref="str1" /> <entry key="s2" value-ref="str2" /> <entry key="s3" value="str3" /> </map> </property> <property name="properties"> <props> <prop key="p1">I'm p1</prop> <prop key="p3">I'm p3</prop> <prop key="p4">I'm p4</prop> <prop key="p5">I'm p5</prop> </props> </property> </bean> <bean id="bean" class="com.emmet.core.factory.HelloWorldService"> <property name="a" value="1000"/> <property name="aa" value="1001" /> <property name="b" value="我是Bean,延迟加载的哦" /> <property name="innerBean"> <bean class="com.emmet.core.factory.HelloWorldService"> <property name="a" value="99" /> <property name="aa" value="100" /> <property name="b" value="我是内部Bean,内部注入+collection注入"/> <property name="outerBean" ref="outerBean"/> <property name="collection"> <set> <ref bean="str1"/> <bean class="java.lang.String" value="connection value" /> </set> </property> </bean> </property> <property name="collection"> <list> <ref bean="str1"/> <ref bean="str2"/> </list> </property> </bean> <bean id="contractorInit" class="com.emmet.core.factory.HelloWorldService"> <constructor-arg value="555" type="int" /> <constructor-arg value="666" type="java.lang.String" /> <constructor-arg ref="outerBean"/> </bean> <!--TODO 延迟加载暂未实现--> <bean id="lazyInitBean" class="com.emmet.core.factory.HelloWorldService" lazy-init="true"> </bean> </beans>
看下此次完成后项目的类文件和包结构
这里有几个类可以不需要的,还没做优化。
看io报下的:
Resource,一个资源读取借口,File,XMl,URL等方式获得连接
package com.emmet.core.io; import java.io.IOException; import java.io.InputStream; /** * 读取资源的内部接口 * Created by EMMET on 14-4-24 * * @author EMMET */ public interface Resource { InputStream getInputStream() throws IOException; }
UrlResource,Resource获得的URL实现方式
package com.emmet.core.io; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; /** * URL方式读取资源文件 * Created by EMMET on 14-4-24 * * @author EMMET */ public class UrlResource implements Resource { private final URL url; public UrlResource(URL resource) { this.url = resource; } @Override public InputStream getInputStream() throws IOException { URLConnection urlConnection = url.openConnection(); urlConnection.connect(); return urlConnection.getInputStream(); } }
ResourceLoader,资源加载器,这里获得URL 资源(后面还会修改此类,变成不仅可以返回URL resource方式获取资源)
package com.emmet.core.io; import java.io.IOException; import java.net.URL; /** * 获取资源并加载 * Created by EMMET on 14-4-24 * * @author EMMET */ public class ResourceLoader{ public UrlResource getResource(String location) throws IOException { URL resource = this.getClass().getClassLoader().getResource(location); return new UrlResource(resource); } }
support包下的XmlBeanDefinitionReader,解析XML进行Bean的注入(XML解析最主要的类,虽然有点多,仔细看看就能看懂)
package com.emmet.core.support; import com.emmet.core.AbstractBeanDefinitionReader; import com.emmet.core.BeanDefinition; import com.emmet.core.io.ResourceLoader; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.*; /** * XML注入Bean * Created by EMMET on 14-4-24 * * @author EMMET */ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { private Document document;//beans XML文档 public XmlBeanDefinitionReader(ResourceLoader resourceLoader) { super(resourceLoader); } @Override public void loadBeanDefinitions(String location) { try { //获取XML文件,即将进行解析XML InputStream inputStream = getResourceLoader().getResource(location).getInputStream(); doLoadBeanDefinitions(inputStream); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException("获取资源失败:" + location); } } private void doLoadBeanDefinitions(InputStream inputStream) { //使用w3c dom 解析 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); try { DocumentBuilder documentBuilder = factory.newDocumentBuilder(); document = documentBuilder.parse(inputStream); //解析Bean registerBeanDefinitions(document); inputStream.close();//关闭文件流 } catch (ParserConfigurationException | SAXException | IOException e) { e.printStackTrace(); throw new RuntimeException("获取Dom示例失败或加载资源文件失败"); } } private void registerBeanDefinitions(Document document) { Element root = document.getDocumentElement();//获取根节点 parseBeanDefinitions(root);//解析XML } /** * 解析所有节点 * * @param root 根节点 */ private void parseBeanDefinitions(Element root) { for (Node node = root.getFirstChild(); node != null; node = node.getNextSibling()) { if (node.getNodeName().equals("bean") //如果是延迟加载类型,不进行初始化 && ((Element) node).hasAttribute("lazy-init") && ((Element) node).getAttribute("lazy-init").equals("true") ) { continue; } //是节点类型才进行解析 if (node.getNodeType() == Node.ELEMENT_NODE) { Element beanElement = (Element) node; String name; //如果Bean节点上没有 Id属性,那么就用它的Class来作为标识 if (beanElement.hasAttribute("id")) { name = beanElement.getAttribute("id"); } else { name = beanElement.getAttribute("class"); } //放到BeanMap里 getRegister().put(name, processBeanDefinition(beanElement)); } } } /** * 解析Bean * * @param element 需要解析的Bean节点 */ private BeanDefinition processBeanDefinition(Element element) { String className = element.getAttribute("class"); if (className == null) { throw new NullPointerException("初始化Bean,Class为空" + element.toString()); } //Bean定义对象 BeanDefinition beanDefinition = new BeanDefinition(className); Object beanObject = null; //Bean对象 try { //创建Bean实例 beanObject = doCreateBean(beanDefinition); beanDefinition.setBean(beanObject); } catch (IllegalAccessException | InstantiationException e) { e.printStackTrace(); throw new IllegalArgumentException("实例化Bean对象失败:" + className); } //过滤基本数据类型,如果Bean的属性是基本数据类型 if (isPrimitive(beanDefinition.getBeanClass()) && element.hasAttribute("value")) { beanObject = getInstanceForName(className, element.getAttribute("value")); beanDefinition.setBean(beanObject); return beanDefinition; } //是否已构造方式注入 boolean isContractor = false; ArrayList<Class> parameterTypes = new ArrayList<Class>(); //构造函数参数类型 ArrayList<Object> parameters = new ArrayList<Object>(); //构造函数参数值 for (Node property = element.getFirstChild(); property != null; property = property.getNextSibling()) { if (property.getNodeType() != Node.ELEMENT_NODE) { continue; } Element elementProperty = (Element) property; //确定是已Contractor if (elementProperty.getNodeName().equals("constructor-arg")) { isContractor = true; if (elementProperty.hasAttribute("value")) { Class clazz = nameToPrimitiveClass(elementProperty.getAttribute("type")); Object constructorParameter = getInstanceForName(elementProperty.getAttribute("type"), elementProperty.getAttribute("value")); parameterTypes.add(clazz); parameters.add(constructorParameter); } else if (elementProperty.hasAttribute("ref")) { String refId = elementProperty.getAttribute("ref"); //是否已经初始化过此Bean if (getRegister().containsKey(refId)) { //已经初始化过,直接添加 BeanDefinition constructorParameter = getRegister().get(refId); parameterTypes.add(constructorParameter.getBeanClass()); parameters.add(constructorParameter.getBean()); } else { //未初始化过 Element elementById = document.getElementById(refId); if (elementById.getNodeName().equals("bean")) { BeanDefinition constructorParameterBean = processBeanDefinition(elementById); parameterTypes.add(constructorParameterBean.getBeanClass()); parameters.add(constructorParameterBean.getBean()); } } } } else if (elementProperty.getNodeName().equals("property")) { //以属性方式注入 String elementVName = elementProperty.getAttribute("name");//获取property 节点上的name Method[] methods = beanDefinition.getBeanClass().getMethods(); Method method = null; for (Method tempMethod : methods) { if (tempMethod.getName().equalsIgnoreCase("set" + elementVName)) { method = tempMethod; break; } } if (method == null) { throw new NullPointerException("没有找到Bean:beanDefinition.getBean() 的" + elementVName + "方法"); } Object propertyValue = null;//Bean属性的值 ,Property 注入的属性值 if (elementProperty.hasAttribute("value")) { // 该属性为直接属性,非外部bean引用属性 String value = elementProperty.getAttribute("value"); //methodParameterTypes[0] set方法正常情况下就一个参数,如果Set方法第一个参数的Class类型,然后创建对象 propertyValue = getInstanceForName(method.getParameterTypes()[0].getName(), value); } else if (elementProperty.hasAttribute("ref")) {//Bean下引用别的Bean String refId = elementProperty.getAttribute("ref"); if (getRegister().containsKey(refId)) { //已经初始化过此Bean propertyValue = getRegister().get(refId).getBean(); } else { //未初始化 Element propertyValueElement = document.getElementById(refId); propertyValue = processBeanDefinition(propertyValueElement).getBean(); } } else if (containNode(elementProperty, "bean")) {//Bean下内部Bean //此Bean下是否还包含Bean Node innerNode; for (innerNode = elementProperty.getFirstChild(); innerNode != null; innerNode = innerNode.getNextSibling()) { if (innerNode.getNodeType() == Node.ELEMENT_NODE && innerNode.getNodeName().equals("bean")) { break; } } BeanDefinition innerBeanDefinition = processBeanDefinition((Element) innerNode); propertyValue = innerBeanDefinition.getBean(); } else if (containNode(elementProperty, "list") || containNode(elementProperty, "set")) {//Bean下Set 或List集合 //如果是以list 或set 注入 Collection<Object> collection = containNode(elementProperty, "list") ? new ArrayList<>() : new HashSet<>(); // <property name="collection"> // <list> // <ref bean="str1"/> getFirstChild(getFirstChild(elementProperty)) 获取子节点的子节点 ,定位到这里,下面Map,Prpos 同理 // <ref bean="str2"/> // </list> // </property> for (Node currentNode = getFirstChild(getFirstChild(elementProperty)); currentNode != null; currentNode = currentNode.getNextSibling()) { if (currentNode.getNodeType() != Node.ELEMENT_NODE) { continue; } Element currentElement = (Element) currentNode; if (currentNode.getNodeName().equals("ref") && currentElement.hasAttribute("bean") && getRegister().containsKey(currentElement.getAttribute("bean")) ) { String ref = currentElement.getAttribute("bean"); collection.add(getRegister().get(ref).getBean()); } else { // collection.add(processBeanDefinition(currentElement).getBean()); } } propertyValue = collection; } else if (containNode(elementProperty, "map")) { HashMap<String, Object> map = new HashMap<>(); for (Node currentNode = getFirstChild(getFirstChild(elementProperty)); currentNode != null; currentNode = currentNode.getNextSibling()) { if (currentNode.getNodeType() != Node.ELEMENT_NODE) { continue; } Element currentElement = (Element) currentNode; if (currentElement.getNodeName().equals("entry") && currentElement.hasAttribute("key")) { String key = currentElement.getAttribute("key"); if (currentElement.hasAttribute("value-ref") && getRegister().containsKey(currentElement.getAttribute("value-ref"))) { String ref = currentElement.getAttribute("value-ref"); map.put(key, getRegister().get(ref).getBean()); } else if (currentElement.hasAttribute("value")) { map.put(key, currentElement.getAttribute("value")); } } } propertyValue = map; } else if (containNode(elementProperty, "props")) { Properties props = new Properties(); for (Node currentNode = getFirstChild(getFirstChild(elementProperty)); currentNode != null; currentNode = currentNode.getNextSibling()) { if (currentNode.getNodeType() != Node.ELEMENT_NODE) { continue; } Element currentElement = (Element) currentNode; if (currentElement.getNodeName().equals("prop") && currentElement.hasAttribute("key")) { String key = currentElement.getAttribute("key"); String propValue = currentElement.getTextContent(); props.setProperty(key, propValue); } } propertyValue = props; } //调用Set方法,传入参数 try { method.invoke(beanDefinition.getBean(), propertyValue); } catch (IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } } } //若为构造函数创建 if (isContractor) { Class[] aClass = {}; Class[] classes = parameterTypes.toArray(aClass); Object[] prams = parameters.toArray(); try { Constructor constructor = beanDefinition.getBeanClass().getConstructor(classes); beanDefinition.setBean(constructor.newInstance(prams)); } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { e.printStackTrace(); throw new RuntimeException("通过构造函数创建Bean失败:" + beanDefinition.getBeanClass() + "," + e.getMessage()); } } //返回创建后的Bean定义,包含了Bean对象 return beanDefinition; } /** * 查看节点下是否存在某个节点 * @param elementProperty 源节点 * @param targetElement 目标节点 * @return 如果找到返回true,否则返回false */ private boolean containNode(Element elementProperty, String targetElement) { for (Node currentNode = elementProperty.getFirstChild(); currentNode != null; currentNode = currentNode.getNextSibling()) { if (currentNode.getNodeName().equals(targetElement)) { return true; } } return false; } /** * 通过Class Name转成Class对象,如果是基本数据类型,使用包装类 * @param name class名字 * @return 创建后的Class对象,如果创建失败,返回null */ private Class nameToPrimitiveClass(String name) { Class clazz = null; switch (name) { case "int": clazz = int.class; break; case "char": clazz = char.class; break; case "boolean": clazz = boolean.class; break; case "short": clazz = short.class; break; case "long": clazz = long.class; break; case "float": clazz = float.class; break; case "double": clazz = double.class; break; case "byte": clazz = byte.class; break; default: try { clazz = Class.forName(name); } catch (ClassNotFoundException e) { e.printStackTrace(); } break; } return clazz; } /** * 通过Class名字获取构造函数,并创建对象 * @param className class名字 * @param value 已构造函数创建传入的值 * @return 已构造函数创建的结果 */ private Object getInstanceForName(String className, String value) { Class clazz = nameToClass(className); try { return clazz.getConstructor(String.class).newInstance(value); } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { e.printStackTrace(); } return null; } private Class nameToClass(String className) { Class clazz = null; switch (className) { case "int": clazz = Integer.class; break; case "char": clazz = Character.class; break; case "boolean": clazz = Boolean.class; break; case "short": clazz = Short.class; break; case "long": clazz = Long.class; break; case "float": clazz = Float.class; break; case "double": clazz = Double.class; break; case "byte": clazz = Byte.class; break; default: try { clazz = Class.forName(className); } catch (ClassNotFoundException e) { e.printStackTrace(); } break; } return clazz; } /** * 是否为基本数据类型 * @param className 要判断的Class对象 * @return 如果是基本数据类型返回true,否则返回false */ private boolean isPrimitive(Class className) { String name = className.getName(); return className.isPrimitive() || name.equals("java.lang.String") || name.equals("java.lang.Integer") || name.equals("java.lang.Short") || name.equals("java.lang.Double") || name.equals("java.lang.Float") || name.equals("java.lang.Byte") || name.equals("java.lang.Boolean") || name.equals("java.lang.Character"); } protected Object doCreateBean(BeanDefinition beanDefinition) throws IllegalAccessException, InstantiationException { return beanDefinition.getBeanClass().newInstance(); } private Node getFirstChild(Node currentNode) { return currentNode.getFirstChild().getNodeType() == Node.ELEMENT_NODE ? currentNode.getFirstChild() : currentNode.getFirstChild().getNextSibling(); } }
每个方法结合注释,应该都能看懂的,其实这个类还可以优化一些,不过,先实现功能,后面再优化
差不多都写完了,接下来是测试类
测试类实体HelloWorldService,包含了Map,List,Property,构造函数,都会使用XMl的方式给注入进来
package com.emmet.core.factory; import java.util.Collection; import java.util.Map; import java.util.Properties; /** * 测试注入的对象 * Created by EMMET on 14-4-23 * * @author EMMET */ public class HelloWorldService { private int a; private Integer aa; private String b; private HelloWorldService innerBean; private HelloWorldService outerBean; private Collection<String> collection; private Map<String,String> map; private Properties properties; public HelloWorldService() { } public HelloWorldService(int a, String b, HelloWorldService outerBean) { this.a = a; this.b = b; this.outerBean = outerBean; } public int getA() { return a; } public void setA(int a) { this.a = a; } public Integer getAa() { return aa; } public void setAa(Integer aa) { this.aa = aa; } public String getB() { return b; } public void setB(String b) { this.b = b; } public HelloWorldService getInnerBean() { return innerBean; } public void setInnerBean(HelloWorldService innerBean) { this.innerBean = innerBean; } public HelloWorldService getOuterBean() { return outerBean; } public void setOuterBean(HelloWorldService outerBean) { this.outerBean = outerBean; } public Collection<String> getCollection() { return collection; } public void setCollection(Collection<String> collection) { this.collection = collection; } public Map<String, String> getMap() { return map; } public void setMap(Map<String, String> map) { this.map = map; } public Properties getProperties() { return properties; } public void setProperties(Properties properties) { this.properties = properties; } }
XmlBeanDefinitionReaderTest,Junit测试类
package com.emmet.core.support; import com.emmet.core.BeanDefinition; import com.emmet.core.factory.HelloWorldService; import com.emmet.core.io.ResourceLoader; import org.junit.Test; import java.util.Map; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; /** * 测试XML注入Bean * Created by EMMET on 14-4-24 * * @author EMMET */ public class XmlBeanDefinitionReaderTest { @Test public void testLoadBeanDefinitions() throws Exception { XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader()); beanDefinitionReader.loadBeanDefinitions("beans.xml");//加载XML并解析Bean Map<String,BeanDefinition> beans = beanDefinitionReader.getRegister();//获得所有Bean assertTrue(beans.size() == 5); assertEquals( "str1" , beans.get("str1").getBean()); assertEquals( "string test " , beans.get("str2").getBean()); HelloWorldService outerBean = (HelloWorldService) beans.get("outerBean").getBean(); assertNotNull(outerBean.getMap()); //是否初始化了MAP assertTrue(outerBean.getMap().size() == 3); // 断言获取的时候和XMl中一致 assertNotNull(outerBean.getProperties()); assertTrue(outerBean.getProperties().size() == 4); //获取构造函数创建的Bean HelloWorldService contractorInit = (HelloWorldService) beans.get("contractorInit").getBean(); assertEquals(555, contractorInit.getA());//断言是否和XML中一致 assertEquals("666", contractorInit.getB()); assertEquals(outerBean,contractorInit.getOuterBean()); //构造函数创建这个内部引用Bean 引用 outerBean } }
至此,完工
源码下载 http://download.csdn.net/detail/csharpppp/7265865
代码托管在开源中国,使用tag管理的 http://git.oschina.net/EMMET/EMMET_MVC/tags
v0.0.2 实现了XML注入 这个Tag 就是上面代码的实现