一步步实现Spring框架(二)XML注入

一步步实现Spring框架(二)XML注入

首先看要解析的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>

看下此次完成后项目的类文件和包结构

一步步实现Spring框架(二)XML注入

这里有几个类可以不需要的,还没做优化。

看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 就是上面代码的实现




你可能感兴趣的:(spring,spring原理,Spring底层,SpringIOC,自己实现Spring)