从xml配置文件初始化java对象

大道至简,衍化至繁。然而复杂的事情简单化是智慧的力量。

涉及知识点

  1. xml解析

  2. 反射 

先列出两个java bean的定义:

package xmlbean;

/** XML初始化类 */
public class Car {
	private String info = "made in China";

	public void showInfo() {
		System.out.println(this + ":" + info);
	}
}

package xmlbean;

/** XML初始化类 */
public class Train {
	private String info;

	public Train(String initInfo) {
		this.info = initInfo;
	}

	public void showInfo() {
		System.out.println(this + ":" + info);
	}
}

上面两个bean用来测试xml配置方式初始化,其中都有一个showinfo()方法,Car类有默认无参构造函数,Train类只有一个带参构造函数。

接下来在看一下我们xml配置文件,定义如下:

<?xml version="1.0" encoding="UTF-8"?>
<!-- 这里模拟spring 定义bean相关的配置文件 -->
<beans>
	<bean id="car" class="xmlbean.Car" />
	<bean id="train" class="xmlbean.Train">
		<constructor-arg arg="开往未来的火车" type="java.lang.String" />
	</bean>
</beans>

其中constructor-arg 节点用来定义我们构造函数的入参值,arg表示值,type表示值的类型,当然,所有的xml属性节点你都可以自己定义,但是在解析的时候需要做对应的约定。xml位置也截一个图:

然后就是我们一个极其简单的BeanFactory,代码如下:

package xmlbean;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * 超级简单的XMLBean工厂,目前使用的java自带的XML解析器,因为后面我们会拥抱注解抛弃xml
 * 
 * @author 大灰狼
 */
public class SimpleBeanFactory {
	// 类加载器
	final static ClassLoader loader = SimpleBeanFactory.class.getClassLoader();
	// bean映射缓存
	Map<String, Object> beanCache = new HashMap<String, Object>();

	// 初始化beanFactory
	public static synchronized SimpleBeanFactory loadConf(String xmlFile) throws Exception {
		SimpleBeanFactory factory = new SimpleBeanFactory();
		InputStream xmlIn = SimpleBeanFactory.class.getResourceAsStream(xmlFile);
		// 使用JDK自带的XML解析器
		DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
		Document document = builder.parse(xmlIn);
		NodeList nodes = document.getElementsByTagName("bean");
		// 遍历Bean节点并根据配置信息初始化bean
		for (int i = 0; i < nodes.getLength(); i++) {
			Node node = nodes.item(i);
			NamedNodeMap attrs = node.getAttributes();
			String id = attrs.getNamedItem("id").getNodeValue();
			String classPath = attrs.getNamedItem("class").getNodeValue();
			List<String[]> initPrarms = new ArrayList<String[]>();// 构造参数
			// 校验是否配置有构造函数参数,自带的XML解析很繁琐
			NodeList childNodes = node.getChildNodes();
			for (int j = 0; j < childNodes.getLength(); j++) {
				Node cnode = childNodes.item(j);
				if (cnode.getNodeType() != Node.ELEMENT_NODE) {// 自带JDK按照XML严格解析导致有换行或者空格也算一个TXT节点
					continue;
				}
				String arg = cnode.getAttributes().getNamedItem("arg").getNodeValue();
				String argType = cnode.getAttributes().getNamedItem("type").getNodeValue();
				// 添加构造器参数及参数类型
				initPrarms.add(new String[] { arg, argType });
			}
			// 根据class全路径加载类
			Class<?> cls = loader.loadClass(classPath);
			// 判断是否有构造函数参数,并根据构造函参数初始化类
			if (initPrarms.isEmpty()) {
				Object inst = cls.getConstructor().newInstance();
				factory.beanCache.put(id, inst);
			} else {
				// 获取参数类型顺序
				List<Class<?>> argTypes = new ArrayList<Class<?>>();
				List<String> arg = new ArrayList<String>();
				initPrarms.forEach(argInfo -> {
					try {
						arg.add(argInfo[0]);
						argTypes.add(loader.loadClass(argInfo[1]));
					} catch (ClassNotFoundException e) {
						e.printStackTrace();
					}
				});
				Object inst = cls.getConstructor(argTypes.toArray(new Class<?>[0])).newInstance(arg.toArray());
				factory.beanCache.put(id, inst);
			}
		}
		return factory;
	}

	// 根据名称获取Bean实例
	public Object getBean(String beanName) {
		return beanCache.get(beanName);
	}

}

BeanFactory定义了两个方法,一个是staticloadConf(),该方法用来加载xml文件并初始化bean缓存,另外一个是getBean()方法,该方法用户指定名称的Bean实例。

接下来我们测试一下这个BeanFactory功能,代码如下:

package xmlbean;

/**
 * 测试XML方式初始化Bean
 * 
 * @author 大灰狼
 */
public class TestApp {

	public static void main(String[] args) throws Exception {
		SimpleBeanFactory factory = SimpleBeanFactory.loadConf("bean-conf.xml");
		Car car = (Car) factory.getBean("car");
		Train train = (Train) factory.getBean("train");
		Train train1 = (Train) factory.getBean("train");
		car.showInfo();
		train.showInfo();
		train1.showInfo();
	}
}

运行结果如下 :

xmlbean.Car@5b6f7412:made in China
xmlbean.Train@27973e9b:开往未来的火车
xmlbean.Train@27973e9b:开往未来的火车

通过运行结果我们可以分析到两次调用factory.getBean("train")返回的对象是同一,因为打印出来的对象信息(xmlbean.Train@27973e9b)一致。所以我们这个BeanFactory是一个单例模式的工厂。在设计模式中还有一个叫原型模式。其实要实现起来很简单,有如下两种方法:

  1. 克隆

  2. 记录Bean配置类型,然后在调用getBean时候读取对应配置,如果是原型模式就反射生成一个对象。

以上内容就是本次xml配置初始化对象,下一次我们将摒弃xml,直接以注解的方式来实现BeanFactory 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值