JavaBean 持久化

【0】README

0.1)本文文字描述转自 core java volume 2,旨在学习  JavaBean 持久化 的基础知识;

0.2)本文所有源代码荔枝均为原创;

0.3) for complete souce code, please visit  https://github.com/pacosonTang/core-java-volume/tree/master/coreJavaAdvanced/chapter8

--------------------------------------------------------------------------------------------------------------

【1】JavaBean 持久化

1)JavaBena持久化定义: 用JavaBean的属性来将 bean 存储到流中, 并在之后的某个时刻,或者在另一个虚拟机中再将它们读回来;(javabean持久化定义)
2)JavaBean持久化与对象序列化的区别: JavaBean 持久化适合于长期存储;
3)problem+solution:
3.1)problem:序列化不适合长期存储;
3.2)solution:人们发明了长期存储机制——JavaBean持久化技术。
4)JavaBean持久化背后的思想:
4.1)对象序列化:假设你想将一个 JFrame 对象保存到一个文件中,以便以后可以读取。如果看一看JFrame 的源码会发现它有几十个实例域。如果该类被序列化,那么所有这些域的值都需要被写下来。
4.1.1)看看窗体(JFrame)是如何构造的:
JFrame frame = new JFrame();
frame.setTitle("my app");
frame.setVisible(true);
默认的无参构造器会初始化所有的实例域,然后由你设置一两个属性。然而, 如果该类被序列化,那么所有这些域的值都需要被写下来。(干货——序列化存储类的缺点是太冗余了,因为它要把所有域都保存下来)
4.2)如果用JavaBean持久化来保存的话,会产生如下XML语句:
<object class="javax.swing.JFrame">
    <void property="title">
        <string>my app</string>
    </void>
    <void property="visible">
        <boolean>true</boolean>
    </void>
</object>
4.2.1)当该对象被读回的时候,只需要加载上述XML文件;
Attention) JavaBean持久化只会保存那些与默认值不同的属性。这个过程叫做——消除冗余;(干货——消除冗余定义)
5)XMLEncoder + XMLDecoder:
5.1)要将对象保存为流,需要使用 XMLEncoder:
XMLEncoder out = new XMLEncoder(new FileOutputStream(...));
out.writeObject();
out.close();
5.2)从文件中读取对象,需要使用 XMLDecoder:
XMLDecoder in = new XMLDecoder(new FileInputStream(...));
JFrame frame = (JFrame)in.readObject();
in.close();
package com.corejava.chapter8;

public class Hello
{
	private String msg = "hello";
	private String speaker = "xiaotang";

	public String getMsg()
	{
		return msg;
	}

	public void setMsg(String msg)
	{
		this.msg = msg;
	}

	public String getSpeaker()
	{
		return speaker;
	}

	public void setSpeaker(String speaker)
	{
		this.speaker = speaker;
	}
}
package com.corejava.chapter8;

import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class PersistentTest
{
	public static void main(String[] args)
	{
		XMLEncoder e = null;
		// 持久化 java类到 XML
		try
		{
			e = new XMLEncoder(new BufferedOutputStream(
					new FileOutputStream("com/corejava/chapter8/persistent.xml")));
		} catch (Exception ex)
		{
			ex.printStackTrace();
		}
		Hello hello = new Hello();
		hello.setMsg("this is a test");
		hello.setSpeaker("persistent tang");
		e.writeObject(hello);
		e.close();
		// 持久化处理结束
		
		System.out.println("write Sucess");

		// 读取持久化对象
		XMLDecoder d = null;
		try
		{
			d = new XMLDecoder(new BufferedInputStream(
					new FileInputStream("com/corejava/chapter8/persistent.xml")));
		} catch (Exception f)
		{
			f.printStackTrace();
		}
		Object result = d.readObject();
		Hello test = (Hello) result;
		System.out.println(test.getMsg() + test.getSpeaker());
		// 读取持久化对象结束
	}
}
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.8.0_60" class="java.beans.XMLDecoder">
 <object class="com.corejava.chapter8.Hello">
  <void property="msg">
   <string>this is a test</string>
  </void>
  <void property="speaker">
   <string>persistent tang</string>
  </void>
 </object>
</java>



【2】 JavaBean持久化可用于任何数据
1)XMLEncoder 内置了对下列类型的支持:

null;

所有基本类型及其包装器类型;

枚举;(从 Java SE6 开始)

String;

数组;

集合与映射表;

反射类型 Class, Field, Method 和 Proxy;

AWT类型.....

AWT 和 Swing 构建, 边界,布局管理器和模型;

事件处理器;


  【3】编写一个持久化代理(Persistence Delegate) 来构造对象
2.1)problem+solution:
2.1.1)problem:  如果可以通过属性得到每个对象的状态,那么使用 JavaBean持久化就是小菜一碟。但是,在真实的程序中,总有些类无法这样工作。如一个没有默认的无参构造器的Employee,它没有 setName, setSalary, setHireDay方法;
2.1.2)solution:要克服这个问题,可以定义一个持久化代理,该代理负责生成对象的XML 编码机制:(干货——引入持久化代理)
2.2)Employee类的持久化代理覆写了 instantiate 方法,以生成一个构造对象的表达式:  
PersistenceDelegate delegate = new PersistenceDelegate()
         {
            protected Expression instantiate(Object oldInstance, Encoder out)
            {
               Employee e = (Employee) oldInstance;
               GregorianCalendar c = new GregorianCalendar();
               c.setTime(e.getHireDay());
               return new Expression(oldInstance, Employee.class, "new", new Object[] {
                     e.getName(), e.getSalary(), c.get(Calendar.YEAR), c.get(Calendar.MONTH),
                     c.get(Calendar.DATE) });
            }
         };
对以上代码的分析(Analysis): 要重新生成oldInstance, 可以调用 Employee.class 对象上的new 方法(也就是构造器), 并提供指定的参数。 
3)安装持久化代理有两种选择:
3.1)一种是将其与某个具体的 XMLWriter 相关联:
out.setPersistenceDelegate(Employee.class, delegate);
3.2)或者,可以设置 Beaninfo 的 bena 描述符的 persistenceDelegate 属性:
BeanInfo info = Introspector.getBeanInfo(GregorianCalendar.class);
info.getBeanDescriptor().setValue("persistenceDelegate", delegate);
3.3)一旦安装了代理, 就可以保存Employee 对象了,如:
Object myData = new Employee("harry hacker", 50000, 1989, 10, 1);
4) 持久化代理构造对象的荔枝

package com.corejava.chapter8;

import java.beans.DefaultPersistenceDelegate;
import java.beans.Encoder;
import java.beans.Expression;
import java.beans.PersistenceDelegate;
import java.beans.XMLEncoder;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;

public class PersistentDelegateTest
{
	public static void main(String[] args)
	{
		// 持久化代理对象
		PersistenceDelegate delegate = new DefaultPersistenceDelegate()
		{
			protected Expression instantiate(Object oldInstance, Encoder out)
			{
				Employee e = (Employee) oldInstance;
				return new Expression(oldInstance, Employee.class, "new", new Object[]
				{
					e.getName(),
					e.getSalary(),
					e.getAge()
				});
			}
		};
		
		XMLEncoder out = null;
		// 持久化 java类到 XML
		try
		{
			out = new XMLEncoder(new BufferedOutputStream(
					new FileOutputStream("com/corejava/chapter8/persistent_delegate.xml")));
		} catch (Exception ex)
		{
			ex.printStackTrace();
		}
		// 安装持久化代理对象
		out.setPersistenceDelegate(Employee.class, delegate);
		Employee employee = new Employee("xiao tang", 110, 25);
		out.writeObject(employee);
		out.close();
		// 持久化处理结束
		
		System.out.println("write Sucess");
	}
}
package com.corejava.chapter8;

// <span style="color:#ff0000;"><strong>无设置器(setXXX方法),也没有默认构造器</strong></span>
public class Employee
{
	private String name;
	private double salary;
	private int age;
	
	public Employee(String name, double salary, int age)
	{
		this.name = name;
		this.salary = salary;
		this.age = age;
	}

	public String getName()
	{
		return name;
	}

	public double getSalary()
	{
		return salary;
	}

	public int getAge()
	{
		return age;
	}
}
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.8.0_60" class="java.beans.XMLDecoder">
 <object class="com.corejava.chapter8.Employee">
  <string>xiao tang</string>
  <double>110.0</double>
  <int>25</int>
 </object>
</java>

【4】由属性构造对象
4.1)如果构造器所需的参数都可以通过访问 oldInstance 的属性获得, 那你就不必自己编写 instantiate 方法了。 你可以直接构造一个  DefaultPersistenceDelegate, 然后提供属性的名字即可。
4.2)看个荔枝:下面的语句为 Rectangle2D.Double 类设置了持久化代理:
out.setPersistenceDelegate(Rectangle2D.Double.class, new DefaultPersistenceDelegate(new String[]{"x", "y", "width", "height"}));
对上述代码的分析(Analysis):
A1)它告诉编译器: “要编码一个 Rectangle2D.Double 对象, 就要取得它的 x, y, width 和 height属性”, 并且用它们来调用构造器。其结果会产生包含如下元素的输出:
<object class="java.awt.geom.Rectangle2DDouble">
    <double>5.0</double>
    <double>10.0</double>
    <double>20.0</double>
    <double>30.0</double>
</object>
5)使用@ConstructorProperties注解构造器:
5.1)看个荔枝: 假设Employee 类有一个接收3个参数的构造器,我们对其进行注解:   
@ConstructorProperties({"name","salary","age"})
public Employee(String name, double salary, int age)
对上述代码的分析(Analysis):
A1) 这可以告诉编码器去调用 getName, getSalary  和 getHireDay 这三个属性获取方法, 并将得到的值写入 object 表达式;
A2) @ConstructorProperties 注解是在 Java SE6 中引入的,因此只在 java 管理扩展(JMX) API中得到了应用;
public class DefaultPersistentDelegateTest
{
	public static void main(String[] args)
	{
		XMLEncoder out = null;
		// 持久化 java类到 XML
		try
		{
			out = new XMLEncoder(new BufferedOutputStream(
					new FileOutputStream("com/corejava/chapter8/default_persistent_delegate.xml")));
		} catch (Exception ex)
		{
			ex.printStackTrace();
		}
		// 安装持久化代理对象
		// 它告诉编译器: “要编码一个 Employee 对象, 
		// 就要取得它的 name, salary, age 属性”, 并且用它们来调用构造器。
		out.setPersistenceDelegate(Employee.class, 
				new DefaultPersistenceDelegate(new String[]{"name","salary","age"}));
		Employee employee = new Employee("default xiao tang", 110, 25);
		out.writeObject(employee);
		out.close();
		// 持久化处理结束
		
		System.out.println("DefaultPersistentDelegateTest write Sucess");
	}
}

---------------------------------------------------------------------------------------------------------
【5】 使用工厂方法构造对象
5.1)
有时,你需要保存那些通过工厂方法获得的对象,而不是通过构造器产生的对象。
5.2)看个荔枝: 思考 你是如何得到一个 InetAddress对象的:
byte[] bytes = new byte[] {127, 0, 0, 1};
InetAddress address = InetAddress.getByAddress(bytes);
5.3) PersistenceDelegate 的 instantiate 方法会产生对工厂方法的一次调用:
protected Expression instantiate(Object oldInstance, Encoder out)
{
return new Expression(oldInstance, InetAddress.class, "getByAddress", new Object[] {((InetAddress)oldInstance).getAddress(); });
}
5.4) 下面是一个输出样例:
<object class="java.net.Inet4Address" method="getByAddress">
<array class="byte" length="4">
<void index="0">
<byte>127</byte>
</void>
<void index="3">
<byte>1</byte>
</void>
</array>
</object>

------------------------------------------------------------------------------------------------
【6】构造后的工作
6.1)problem+solution:
6.1.1)problem:
某些类的状态是通过调用 非属性设置方法来建立的,即 非 setXXX方法;
6.1.2)solution: 你可以通过覆写 DefaultPersistenceDelegate 的 initialize 方法来处理这种状况。initialize 方法会在 instantiate 方法之后调用, 在其中你可以生成将被 记录在bean存档文件中的 语句序列;
6.2)看个荔枝: 为了重新生成一个 BitSet 对象,你要设置原对象中存在的所有位。下面的 initialize 方法生成了必要的语句:
protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) 
{ 
super.initialize(type, oldInstance, newInstance, out);
BitSet bs = (BitSet) oldInstance;
for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1))
out.writeStatement(new Statement(bs, "set", new Object[] { i, i + 1, true }));
}
6.2.1)输出样例如下所示:
<object class="java.util.BitSet">
<void method="set">
<int>1</int>
<int>2</int>
<boolean>true</boolean>
</void>
<void method="set">
<int>4</int>
<int>5</int>
<boolean>true</boolean>
</void>
</object>
Attention) 编写 new Statement(bs, "set", new Object[]{i}) 会更加有意义, 不过,那样的话, XMLWriter 会使用一个空的名字来设置属性, 而这样的语句不是很直观;
------------------------------------------------------------------------------------------------
【7】 瞬态属性
7.1)problem+solution:
7.1.1)problem:
偶尔, 类中会包含能够被 XMLDecoder 发现的带有 getXXX 和 setXXX的属性, 但是 你不希望在存档文件中包含该属性的值;
7.1.2)solution:为了禁止某个属性被存档, 可以在属性描述符中将 它标记为 transient;(干货——引入transient属性标记)
7.2)看个荔枝:
下面的语句吧 DamageReporter 类的 removeMode属性标记为 瞬时的;
BeanInfo info = Introspector.getBeanInfo(DamageReport.class);
for(PropertyDescriptor desc : info.getPropertyDescriptors())
if(desc.getName().equals("removeMode"))
desc.setValue("transient", Boolean.TRUE);
JavaBean 持久化的存档的总结(Conclusion):
C1)
适合长期存储的;
C2)小而且快的;
C3)易于生成的;
C4)易于人们进行编辑的;
C5)是标准java 的一部分;   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值