今天leadr提出需求,原来公司项目中读取解析xml文件的代码效率太低,考虑切换一种xml为数据封装格式与读取方式以提高效率。我这灵机一动spring对bean的依赖注入就是读取xml文件,可以尝试扒一扒spring的源码,来实现一个轻量级的方案。
重构xml文件,向spring的xml文件格式看齐
重构完成的xml文件格式如下:
看起来很眼熟的有没有,跟spring的配置文件一样哦。
扒一扒spring读取xml文件的源码
手动扒了一下spring读取xml文件的代码,由于spring过于庞大,读取spring的xml方法又按照读取不同的标签被分拆出n多个方法,楼主能力有限就不在这里卖弄了,不过spring从配置文件把bean加载到bean工厂跟楼主下面读取xml文件的方式理论上是一样的。
废话不多说,上代码:
/** * 模拟spring依赖注入的方式读取xml文件. * @author whf * @date Aug 23, 2017 */public class XMLBeanFactory { private String xmlName; private SAXReader reader; private Document document; /** * 构造方法. * @param xmlName xmlName. */ public XMLBeanFactory(String xmlName) { // 在构造方法中 try { this.xmlName = xmlName; reader = new SAXReader(); document = reader.read(this.getClass().getClassLoader().getResourceAsStream(xmlName)); } catch (DocumentException e) { e.printStackTrace(); } } /** * 获取相同类型的bean. * @param type bean的class type. * @return 返回相同类型的bean的list. * @throws Exception Exception. */ publicList getBeansOfType(Class type) throws Exception { List objects = new ArrayList<>(); try { Element root = document.getRootElement(); List beans = root.elements(); if (beans.size() > 0) { for (Element bean : beans) { if (bean.attributeValue("class").equals(type.getName())) { T object = null; String clazz = bean.attributeValue("class"); // 通过反射来创建对象 Class beanClass = Class.forName(clazz); object = (T) beanClass.newInstance(); List propertys = bean.elements(); if (propertys.size() > 0) { for (Element property : propertys) { String key = property.attributeValue("name"); Field field = beanClass.getDeclaredField(key); field.setAccessible(true); List childBean = property.elements(); // 如果property下内嵌bean if (childBean.size() > 0) { Object childObject = getBean(key, property); field.set(object, childObject); } else { /* * 此属性值是一个字符串.这里单独处理int,float类型变量.如果不处理, * 会将String类型直接赋值给int类型,发生ClassCastException */ String value = property.attributeValue("value"); // 需要对类型进行判断 if (field.getType().getName().equals("int")) { // 整数 int x = Integer.parseInt(value); field.set(object, x); continue; } if (field.getType().getName().equals("float")) { // 浮点数 float y = Float.parseFloat(value); field.set(object, y); // 注意double可以接受float类型 continue; } field.set(object, value);// 处理String类型 } } } objects.add(object); } } } } catch (Exception e) { e.printStackTrace(); } return objects; } /** * 获取property内嵌的bean. * @param name id或者bean的name * @param root 根节点. * @return 返回封装完整的bean. * @throws Exception Exception. */ public Object getBean(String name, Element root) throws Exception { Object object = null; List beans = root.elements(); if (beans.size() > 0) { for (Element bean : beans) { if (bean.attributeValue("name").equals(name)) { // 如果bean name相同则开始创建对象 String clazz = bean.attributeValue("class"); // 通过反射来创建对象 Class beanClass = Class.forName(clazz); object = beanClass.newInstance(); List propertys = bean.elements(); if (propertys.size() > 0) { for (Element property : propertys) { String key = property.attributeValue("name"); Field field = beanClass.getDeclaredField(key); field.setAccessible(true); List childBean = property.elements(); // 如果property下内嵌bean if (childBean.size() > 0) { field.set(object, getBean(key, property)); } if (property.attribute("ref") != null) { /* * 此属性的值是一个对象.这里由于直接调用getBean方法赋值给对象,返回的对象一定是Bean参数的对象, 因此强制转换不会出问题 */ String refid = property.attributeValue("ref"); field.set(object, getBean(refid)); } else { /* * 此属性值是一个字符串.这里单独处理int,float类型变量.如果不处理,会将String类型直接赋值给int类型, * 发生ClassCastException */ String value = property.attributeValue("value"); // 需要对类型进行判断 field.set(object, value);// 处理String类型 } } } } } } return object; }}
楼主的代码就是实现读取xml文件中相同类型的bean封装到list中返回。具体如何实现,看代码,注释写的很清楚了。
文件读取的效率提升120多倍
代码完成之后,对比之前的读取xml的代码,比之前的效率提升了120。
总结一下
大牛都说要看开源框架的源码,收获会怎样怎样,但是对于大部分向我这样的伪码农去扒源码的时候总是一头雾水,不知所云。然而,当我们真正有需求的时候,开源代码的实现便成了一份巨大的宝藏,带着我们的目的去扒源码,有时候会有事半功倍的效果。楼主亲测有效,源码虽好,可不要贪杯哦。