一次代码重构之旅-快速读写xml文件工具类封装

   为了满足系统的灵活性,有些功能经常需要用到配置文件,一般是xml格式的居多.如何能快速读写配置文件呢?

   以前都是用dom4j提供的api来读写xml文件,用dom4j读写配置文件总感觉像是在结构化的处理问题,能不能直接把xml文件和JavaBean之间相互转换呢?答案肯定是可以,xstream中提供了很简单的方式将二者转化,感觉这样才像面向对象化处理问题.

xstream知识点简单总结:
1.JavaBean到xml,用toXML()方法;Xml到JavaBean,用fromXML()方法;
2.转换时别名映射:
  1)类别名,alias(String name, Class type)。 
  2)类成员别名,aliasField(String alias, Class definedIn, String fieldName) 
  3)类成员作为属性别名,aliasAttribute(Class definedIn, String attributeName, String alias)
  4)去掉JavaBean中某个字段不生成到xml中,omitField(definedIn, fieldName)
  5)去掉集合类型生成xml的父节点,addImplicitCollection(Class ownerType, String fieldName) 
  6)注册一个转换器,registerConverter(Converter converter)  
3.映射别名对应注解
  @XStreamAlias("xxx")
  @XStreamAsAttribute  
  @XStreamImplicit()或@XStreamImplicit(itemFieldName = "xxx")
  @XStreamOmitField
  @XStreamConverter(Class)

我比较倾向于使用注解来处理,这样只要修改JavaBean上的内容就可以了.
实体类Stdent,配置注解:

import com.thoughtworks.xstream.annotations.XStreamAlias;
@XStreamAlias("STUDENT")
public class Student {
	
	@XStreamAlias("NAME")
	private String name;
	
	@XStreamAlias("CLASS")
	private String classes;
	
	//get和set方法

	@Override
	public String toString() {
		return "Student [classes=" + classes + ", name=" + name + "]";
	}
}
    我觉得如果读不到配置文件就报错不太友好,为了程序健壮性考虑应该在读不到配置文件时生成一个默认的下次就能读到了.
第一版工具类:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;

public class XmlUtil1 {
	private static final String file = "student1.xml";//运行main方法后刷新整个工程可以找到student1.xml

	public void saveConfig(Student entity) {
		System.out.println("===保存配置====");
		XStream stream = new XStream(new DomDriver("utf-8"));// xml文件使用utf-8格式
		stream.autodetectAnnotations(true);// 设置自动匹配annotation.
		try {
			FileOutputStream out = new FileOutputStream(file);
			stream.toXML(entity, out);// 将实体类转为xml并输出到文件.
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 从配置文件中读取配置,并自动转换为对应的对象.
	 * 
	 * @return Student
	 */
	public Student getConfig() {
		XStream stream = new XStream(new DomDriver("utf-8"));// xml文件使用utf-8格式
		stream.autodetectAnnotations(true);// 设置自动匹配annotation.
		FileInputStream input;
		Student entity;
		try {
			input = new FileInputStream(file);
			// 由于xstream-1.3.1.jar中有个bug,从xml文件中读出的内容autodetectAnnotations(true)不生效,必须用alias后才正常.
			stream.alias("STUDENT", Student.class);
			entity = (Student) stream.fromXML(input); // 从配置文件中读取配置,并自动转换为对应的对象
		} catch (FileNotFoundException e1) {
			entity = setDefaultConfig();// 文件不存在时创建一个默认的配置文件.
			saveConfig(entity);
		}
		return entity;
	}

	// 创建一个默认的配置文件,需重写
	private Student setDefaultConfig() {
		Student stu = new Student();
		stu.setName("李坤");
		stu.setClasses("五期提高班");
		return stu;
	}

	public static void main(String[] args) {
		XmlUtil1 util = new XmlUtil1();
		System.out.println(util.getConfig());
	}
}
    当写了两三个配置文件之后发现有很多内容是重复的,读getConfig和写saveConfig的过程每个配置文件基本一致,只要修改一点点内容就行了.
    重构一下吧,以后修改的内容越少越好,考虑使用泛型(使用泛型的好处,把运行时错误带入到编译期发现),见下面的工具类以后不用修改读getConfig和写saveConfig的过程了,只需修改默认的配置文件setDefaultConfig()方法即可.所以形成了第二版工具类:
注:由于xstream-1.3.1.jar中有个bug,从xml文件中读出的内容autodetectAnnotations(true)不生效,必须用alias后才正常.

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.io.xml.DomDriver;

/*
 * 封装后的读写配置文件的工具类,可以将设置注解后的对象与xml转化. 
 * 写入用saveConfig方法,读取用getConfig方法
 * 
 * @author 李坤
 * 
 * 使用步骤: 
 * 1.引入工具类和xstream.jar
 * 2.修改文件名,可以用../代表上级文件夹路径.
 * 3.写一个类或几个类并配置xstream注解,用于对象和xml之间的转换.注意,一定要在需转化的实体类上设置注解.
 * 4.修改setDefaultConfig方法,本方法的作用是当配置文件找不到时自动生成一个默认的配置文件,提高程序的健壮性.
 * 5.修改main方法可进行测试,写入用saveConfig方法,读取用getConfig方法.
 * 6.还可以加一些自己的逻辑处理自己的业务的方法.
 */
public class XmlUtil2<T> {
	private static final String file = "student2.xml";// 文件名,可以用../代表上级文件夹路径
	Class<T> clazz;

	public XmlUtil2(Class<T> clazz) {
		this.clazz = clazz;
	}

	/**
	 *保存xml文件
	 * 
	 * @param entity
	 *            xml文件对应的对象
	 */
	public void saveConfig(T entity) {
		System.out.println("===保存配置====");
		XStream stream = new XStream(new DomDriver("utf-8"));// xml文件使用utf-8格式
		stream.autodetectAnnotations(true);// 设置自动匹配annotation.
		try {
			FileOutputStream out = new FileOutputStream(file);
			stream.toXML(entity, out);// 将实体类转为xml并输出到文件.
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 从配置文件中读取配置,并自动转换为对应的对象.
	 * 
	 * @return T
	 */
	public T getConfig() {
		XStream stream = new XStream(new DomDriver("utf-8"));// xml文件使用utf-8格式
		stream.autodetectAnnotations(true);// 设置自动匹配annotation.
		FileInputStream input;
		T entity;
		try {
			input = new FileInputStream(file);
			String alias = "";
			if (clazz.isAnnotationPresent(XStreamAlias.class)) {
				alias = clazz.getAnnotation(XStreamAlias.class).value();
			}
			stream.alias(alias, clazz);// 由于xstream-1.3.1.jar中有个bug,从xml文件中读出的内容autodetectAnnotations(true)不生效,必须用alias后才正常.
			entity = (T) stream.fromXML(input); // 从配置文件中读取配置,并自动转换为对应的对象
		} catch (FileNotFoundException e1) {
			entity = setDefaultConfig();
			saveConfig(entity);// 文件不存在时创建一个默认的配置文件.
		}
		return entity;
	}

	// 创建一个默认的配置文件,需重写
	private T setDefaultConfig() {
		Student stu = new Student();
		stu.setName("李坤");
		stu.setClasses("五期提高班");
		T entity = (T) stu;
		return entity;
	}

	// 运行main方法可查看测试结果.
	public static void main(String[] args) {
		XmlUtil2<Student> util = new XmlUtil2<Student>(Student.class);
		System.out.println(util.getConfig());
	}
}
    上面的工具类每次读写配置文件还是需要复制一份并修改一个方法,感觉又多余又违反了开闭原则,好吧,再重构一份出来,使用抽象类吧,子类继承父类并实现抽象方法,这样的好处是父类的方法以后完全不用修改了,需要读写一个新配置文件继承并实现抽象方法就行了,符合开闭原则.这样就有了第三版代码:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.io.xml.DomDriver;

/*
 * 封装后的读写配置文件的工具类,可以将设置注解后的对象与xml转化. 
 * 写入用saveConfig方法,读取用getConfig方法
 * 
 * @author 李坤
 * 
 * 使用步骤: 
 * 1.引入工具类和xstream.jar
 * 2.写一个类或几个类并配置xstream注解,用于对象和xml之间的转换.注意,一定要在需转化的实体类上设置注解.
 * 3.写一个子类继承XmlUtil3类,并且实现抽象方法.
 * 4.文件名,可以用../代表上级文件夹路径.
 * 5.setDefaultConfig方法,本方法的作用是当配置文件找不到时自动生成一个默认的配置文件,提高程序的健壮性.
 * 6.修改main方法可进行测试,写入用saveConfig方法,读取用getConfig方法.
 * 7.还可以加一些自己的逻辑处理自己的业务的方法.
 */
public abstract class XmlUtil3<T> {
	abstract String setFileName();
	abstract Class<T> setClazz();
	// 创建一个默认的配置文件,需重写
	abstract T setDefaultConfig();

	/**
	 *保存xml文件
	 * 
	 * @param entity
	 *            xml文件对应的对象
	 */
	public void saveConfig(T entity) {
		System.out.println("===保存配置====");
		XStream stream = new XStream(new DomDriver("utf-8"));// xml文件使用utf-8格式
		stream.autodetectAnnotations(true);// 设置自动匹配annotation.
		try {
			FileOutputStream out = new FileOutputStream(setFileName());
			stream.toXML(entity, out);// 将实体类转为xml并输出到文件.
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 从配置文件中读取配置,并自动转换为对应的对象.
	 * 
	 * @return T
	 */
	public T getConfig() {
		XStream stream = new XStream(new DomDriver("utf-8"));// xml文件使用utf-8格式
		stream.autodetectAnnotations(true);// 设置自动匹配annotation.
		FileInputStream input;
		T entity;
		try {
			input = new FileInputStream(setFileName());
			String alias = "";
			if (setClazz().isAnnotationPresent(XStreamAlias.class)) {
				alias = setClazz().getAnnotation(XStreamAlias.class).value();
			}
			stream.alias(alias, setClazz());// 由于xstream-1.3.1.jar中有个bug,从xml文件中读出的内容autodetectAnnotations(true)不生效,必须用alias后才正常.
			entity = (T) stream.fromXML(input); // 从配置文件中读取配置,并自动转换为对应的对象
		} catch (FileNotFoundException e1) {
			entity = setDefaultConfig();// 文件不存在时创建一个默认的配置文件.
			if (entity !=null) {
				saveConfig(entity);
			}
		}
		return entity;
	}
}
public class StuConfig3 extends XmlUtil3<Student>{

	@Override
	Class<Student> setClazz() {
		return Student.class;
	}

	@Override
	Student setDefaultConfig() {
		Student stu = new Student();
		stu.setName("李坤");
		stu.setClasses("五期提高班");
		return stu;
	}

	@Override
	String setFileName() {
		return "student3.xml";
	}

	public static void main(String[] args) {
		StuConfig3 config = new StuConfig3();
		System.out.println(config.getConfig());
	}
}
    感觉上一版中抽象方法太多了,继承一次需要实现三个抽象方法,能不能简化一下让类更易用呢?好吧,那就简化一下,因为文件名和泛型的真实类在上一版的抽象方法实现时只有一行代码return xxx;所以咱们可以拿它做文章,把它放到构造方法中,强制在构造这个类的时候初始化它们,OK,动手实现第四版代码:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.io.xml.DomDriver;

/*
 * 封装后的读写配置文件的工具类,可以将设置注解后的对象与xml转化. 
 * 写入用saveConfig方法,读取用getConfig方法
 * 
 * @author 李坤
 * 
 * 使用步骤: 
 * 1.引入工具类和xstream.jar
 * 2.写一个类或几个类并配置xstream注解,用于对象和xml之间的转换.注意,一定要在需转化的实体类上设置注解.
 * 3.写一个子类继承XmlUtil3类,并且实现抽象方法.
 * 4.文件名,可以用../代表上级文件夹路径.
 * 5.setDefaultConfig方法,本方法的作用是当配置文件找不到时自动生成一个默认的配置文件,提高程序的健壮性.
 * 6.修改main方法可进行测试,写入用saveConfig方法,读取用getConfig方法.
 * 7.还可以加一些自己的逻辑处理自己的业务的方法.
 */
public abstract class XmlUtil4<T> {
	// 创建一个默认的配置文件,需重写
	abstract T setDefaultConfig();

	private String file;
	private Class<T> clazz;

	public XmlUtil4(Class<T> clazz, String file) {
		this.clazz = clazz;
                this.file = file;
                String dirPath = file.substring(0, file.lastIndexOf("/"));
                File dirFile = new File(dirPath);
                if (!dirFile.exists()) {
                        dirFile.mkdirs();
                }
	}

	/**
	 *保存xml文件
	 * 
	 * @param entity
	 *            xml文件对应的对象
	 */
	public void saveConfig(T entity) {
		System.out.println("===保存配置====");
		XStream stream = new XStream(new DomDriver("utf-8"));// xml文件使用utf-8格式
		stream.autodetectAnnotations(true);// 设置自动匹配annotation.
		try {
			FileOutputStream out = new FileOutputStream(file);
			stream.toXML(entity, out);// 将实体类转为xml并输出到文件.
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 从配置文件中读取配置,并自动转换为对应的对象.
	 * 
	 * @return T
	 */
	public T getConfig() {
		XStream stream = new XStream(new DomDriver("utf-8"));// xml文件使用utf-8格式
		stream.autodetectAnnotations(true);// 设置自动匹配annotation.
		FileInputStream input;
		T entity;
		try {
			input = new FileInputStream(file);
			String alias = "";
			if (clazz.isAnnotationPresent(XStreamAlias.class)) {
				alias = clazz.getAnnotation(XStreamAlias.class).value();
			}
			stream.alias(alias, clazz);// 由于xstream-1.3.1.jar中有个bug,从xml文件中读出的内容autodetectAnnotations(true)不生效,必须用alias后才正常.
			entity = (T) stream.fromXML(input); // 从配置文件中读取配置,并自动转换为对应的对象
		} catch (FileNotFoundException e1) {
			entity = setDefaultConfig();// 文件不存在时创建一个默认的配置文件.
			if (entity != null) {
				saveConfig(entity);
			}
		}
		return entity;
	}
}
public class StuConfig4 extends XmlUtil4<Student> {
	private static final String file = "student4.xml";

	public StuConfig4() {
		super(Student.class, file);
	}

	@Override
	Student setDefaultConfig() {
		Student stu = new Student();
		stu.setName("李坤");
		stu.setClasses("五期提高班");
		return stu;
	}

	public static void main(String[] args) {
		StuConfig4 config = new StuConfig4();
		System.out.println(config.getConfig());
	}

}

    总结一下吧,重构的过程是体现一个程序员基本素质,知识上学到了xstream的基本使用,考虑问题时用到了:对象化的思想和思考方式,程序设计时健壮性的考虑,开闭原则,抽象类,继承和实现,泛型(使用泛型可以把运行时错误提前到编译期得到检查和处理),类也要求易用性等.

本例子的代码结构:

一次代码重构之旅-快速读写xml文件工具类封装_第1张图片

本例子的下载地址:http://download.csdn.net/detail/lk_blog/4806615

你可能感兴趣的:(xml,重构,工具类,xstream)