今天,复习黑马第6天学习的pull解析XML文档,结合第10天讲的JavaBean,突然了有了新的思考。
以下是我创建的XML文档:
<?xml version="1.0" encoding="UTF-8"?> <!-- 定义一个演示文档,存储黑马的所有班级,以及每个班级的信息 利用XML的功能之一:XML用来表示生活中有关系的数据 --> <itheima> <class> <name>Anroid70期</name> <teacher>于俏</teacher> <time>2015年6月21日</time> <count>77</count> </class> <class> <name>JavaEE70期</name> <teacher>张子艺</teacher> <time>2015年7月2日</time> <count>83</count> </class> </itheima>
下面是XML解析的Java代码:
//pull解析xml文档开始。。。。。
try {
//第1步:导入pull解析的包,得到pull解析工厂对象
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
//第2步:通过pull解析工厂得到pull解析器
XmlPullParser parser = factory.newPullParser();
//将XML文档的输入流引入pull解析器
InputStream in = new FileInputStream("D:\\HeiMa Dairy\\Other\\MyWorkSpace\\JDBCPractice\\WebRoot\\xml\\itheima.xml");
//指定解析器的输入流和编码集
parser.setInput(in, "utf-8");
//第4步:开始解析
//创建一个List集合,用来存储javabean,javabean会封装班级信息。
List<HeiMaClass> hmClassList = null;
HeiMaClass hmClass = null;
int type = 0; //标记解析的位置
while((type = parser.getEventType())!= XmlPullParser.END_DOCUMENT) //到了XML的文档末尾就结束解析
{
//得到标签名,以作相应的判断 。
String tagName = parser.getName();
//用switch来判断解析的具体位置,比对标签名,采取相应的操作。
switch(type)
{
case XmlPullParser.START_TAG: //开始标签
if("itheima".equals(tagName))
{
//创建封装班级javabean的集合对象
hmClassList = new ArrayList<HeiMaClass>();
}
else if("class".equals(tagName))
{
//创建班级javabean,以封装数据
hmClass = new HeiMaClass();
}
else
{
//得到标签内容,用来封装进javabean hmClass中去。
String tagText = parser.nextText();
//利用内省,直接用标签文本,也即Javabean的属性来获取对应
//写入方法来赋值。
try {
//内省重要代码************ //构造一个PropertyDescriptor对象
PropertyDescriptor pd = new PropertyDescriptor
(tagName,hmClass.getClass());
//内省重要代码************ //得到属性的写入方法
Method writeMethod = pd.getWriteMethod();
try {
//内省重要代码************ writeMethod.invoke(hmClass, tagText);
} catch (Exception e) {
e.printStackTrace();
}
} catch (IntrospectionException e) {
e.printStackTrace();
}
}
break;
case XmlPullParser.END_TAG: //结束标签
if("class".equals(tagName))
{
//将班级对象封装进List集合
hmClassList.add(hmClass);
}
break;
}
parser.next();
}
//先在控制台上将List集合打印查看一下
for(HeiMaClass cla : hmClassList)
{
System.out.println(cla.toString());
}
//将List集合写到Session域中,供请求页面获取输出
request.getSession().setAttribute("hmClassList2", hmClassList);
//转发到请求页面
request.getRequestDispatcher("/pra/jdbc.jsp").forward(request, response);
} catch (XmlPullParserException e) {
e.printStackTrace();
}
在设计代码的时候,我采用的是javabean封装数据,XML文档中class这个标签对应的javabean代码如下:
//黑马班级类,用来演示pull解析XML,将XML中的班级数据封装到黑马班级类中。
public class HeiMaClass {
private String name,teacher,time,count;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTeacher() {
return teacher;
}
public void setTeacher(String teacher) {
this.teacher = teacher;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public String getCount() {
return count;
}
public void setCount(String count) {
this.count = count;
}
public String toString()
{
return this.name + ":" + this.teacher + ":" + this.time + ":" + this.count;
}
}
在用else if语句判断对应javabean中的属性标签名时,我发现继续都是用属性名对应的方法来封装数据。那么,有没有可能通过javabean的属性名称(即对应xml文档的标签名称),得到这个属性的set方法呢?
后来我看了一下张孝祥老师的高新技术最后几个视频,发现这个正是内省技术,于是众多的else if语句通过javabean就很轻松的完成了。内省的简单操作是通过java.beans下的PropertyDescriptor类来实现的,构造这个类的对象需传递属性名称和字节码文件,然后通过getWriteMethod即可得到属性对应的写入方法,然后利用反射的原理即可封装数据至javabean中。
-------------------------------------------------------------------------------------------
以前看了反射和内省,当时还是朦朦胧胧,不知道它们到底有什么了不起的。现在我慢慢地发现这些技术如果明白了话,真的是非常实用的,真的像老师所说的,可以让我们少写很多的代码。
同理,pull序列化(将内存中的集合、对象)写到xml文档中去,可以使用反射和内省来减写很多的代码。代码如下:
//从域中得到List集合,hmClassList2。
List<HeiMaClass> hmClassList = (List<HeiMaClass>)request.getSession().getAttribute("hmClassList2");
//准备进行集合的序列化
try {
//第1步:得到解析器工厂
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
//第2步:从解析器工厂得到序列化的接口
XmlSerializer serializer = factory.newSerializer();
//第3步:将文件输出流引入到序列化的接口
OutputStream out = new FileOutputStream("C:\\Users\\LENOVO\\Desktop\\serializer.xml");
serializer.setOutput(out, "utf-8");
//第4步:正式开始序列化
//1。生成文档声明和文档结束
serializer.startDocument(null, true);
//2.生成根标签<itheima>
serializer.startTag(null, "itheima");
//3.遍历生成字标签
for(HeiMaClass hmClass : hmClassList)
{
//4.生成班级的开始和结束标签
serializer.startTag(null, "class");
//利用反射+内省技术,获取HeiMaClass这个javabean的所有属性,并获取它们的值写入xml文档。
Field[] fields = hmClass.getClass().getDeclaredFields();
//遍历字段
for(Field field : fields)
{
//获取字段的属性名
String propertyName = field.getName();
//得到属性描述器
try {
PropertyDescriptor pd = new PropertyDescriptor(propertyName,hmClass.getClass());
//通过属性描述器得到字段对应的读取方法,获取得它的值并写入XML文档。
Method readMethod = pd.getReadMethod();
//调用方法获取属性值
try {
String propertyValue = (String)readMethod.invoke(hmClass);
//生成开始和结束标签,并插入标签文本。
serializer.startTag(null, propertyName);
serializer.text(propertyValue);
serializer.endTag(null, propertyName);
} catch (Exception e) {
e.printStackTrace();
}
} catch (IntrospectionException e) {
e.printStackTrace();
}
}
serializer.endTag(null, "class");
}
serializer.endTag(null, "itheima");
serializer.endDocument();
//序列化成功,返回一个提示信息到request域中去
request.setAttribute("tipXML", "XML文档创建成功,已保存到桌面。");
} catch (XmlPullParserException e) {
//序列化失败,返回一个提示信息到request域中去,因为只想失败的一次请求提醒,所以不需要将提示写至session域中去。
request.setAttribute("tipXML", "XML文档创建失败!");
e.printStackTrace();
}finally
{
//转发至请求页面
request.getRequestDispatcher("/pra/jdbc.jsp").forward(request, response);
}
内省在WEB中的一个小小的应用,
比如在Servlet里获取请求参数,可以利用内省根据属性名获取属性方法,将属性赋值给JavaBean.
遇到个需求,要克隆实体,我立马想到内省技术。但是会有以下问题
在android studio里,没有java.beans包,从而无法使用内省技术的问题。
https://blog.youkuaiyun.com/u010445297/article/details/60967146 (使用Apache的BeanUtils,发现这个jar包里没有java.beans包)
摒弃上面的方法,使用google的openbeans.jar,这个包和java.beans里的类的名字和功能一样,只是包名不一样。解决问题。
https://mvnrepository.com/artifact/com.googlecode/openbeans/1.0
下面是我克隆实现方法:
/**
* Created by XinYi on 2019/4/29.
*/
public class CZBeanUtils {
/**
* javabean克隆
* @param beanOrigin 原始bean
* @return 新bean
*/
public static Object cloneBean(Object beanOrigin){
return cloneBean(beanOrigin,null);
}
/**
* javabean克隆(带字段过滤)
* @param beanOrigin 原始bean
* @param exceptFields 不需要复制的字段
* @return 新bean
*/
public static Object cloneBean(Object beanOrigin,String[] exceptFields){
Object newBean = null;
try {
newBean = beanOrigin.getClass().newInstance();
Field[] fields = beanOrigin.getClass().getDeclaredFields();
//遍历字段
for(Field field : fields)
{
field.setAccessible(true);
//获取字段的属性名
String propertyName = field.getName();
if(exceptFields != null && Arrays.asList(exceptFields).contains(propertyName)) continue;
//得到属性描述器
try {
//获取源Bean的get方法,得到属性。
PropertyDescriptor pdOrigin = new PropertyDescriptor(propertyName,beanOrigin.getClass());
//通过属性描述器得到字段对应的读取方法,获取得它的值并写入XML文档。
Method readMethod = pdOrigin.getReadMethod();
//调用方法获取属性值
//获取新Bean的set方法,设置属性。
PropertyDescriptor pdNew = new PropertyDescriptor(propertyName,beanOrigin.getClass());
//通过属性描述器得到字段对应的读取方法,获取得它的值并写入XML文档。
Method writeMethod = pdNew.getWriteMethod();
try {
Object beanOriginPropertyValue = readMethod.invoke(beanOrigin);
writeMethod.invoke(newBean,beanOriginPropertyValue);
} catch (Exception e) {
e.printStackTrace();
}
} catch (IntrospectionException e) {
e.printStackTrace();
}
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return newBean;
}
/**
* 获取Interface里所有的静态成员变量值
* @param cls 要遍历的字节码
* @return
*/
public static List<Object> getAllPropertyValues(Class cls){
List<Object> result = new ArrayList<>();
Field[] fields = cls.getDeclaredFields();
for(Field field : fields){
field.setAccessible(true);
try {
Object object = field.get(null);
if(object != null){
result.add(object);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return result;
}
}
转载于:https://blog.51cto.com/4259297/1672205