一、前言
趁着周末学习一番,目标如下:
- 明白什么事java序列化,如何进行java序列化和反序列化
- 对jackson工具包系统的学习,从了json转换外还有没有其他好用的功能,jackson和cxf如何更好的派和使用
- java对象克隆的使用场景在哪儿,如何通过克隆进行java的深度复制
二、对象序列化(object serialization)
1、概念
java的向对象序列化API,它提供了一个框架,用来将对象编码成字节流,并从字节流编码中重新构建对象。“将一个对象编码成一个字节流”称作将改对象序列化(serializing) ;相反处理过程被称作反序列化(deserializing)。
2、java序列化的要点
- 你可以通过序列化来存储对象的状态
- 使用ObjectOutPutStream来序列化对象(Java.IO)
- 用FileOutPutStream链接ObjectOutPutStream来讲对象序列化到文件中
- 调用ObjectOutPutStream的writeObject(the object) 来将对象序列化,不需调用FileOutPutStream的方法
- 静态变量 不会被序列化,因为所有对象都是共享同一份静态变量值
3、例子将person对象反序列化
package com.fengshu.Spring;
import java.io.Serializable;
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "对象序列化 [name=" + name + ", age=" + age + "]";
}
}
package com.fengshu.Spring;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class SeriableTest {
public static void main(String[] args) throws Exception {
File file = new File("E:\\result.txt"); //如果不存在会被创建
ObjectOutputStream oout = new ObjectOutputStream(new FileOutputStream(file));
Person person = new Person("John", 101);
oout.writeObject(person);
oout.close();
ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));
Person newPerson = (Person)oin.readObject();
oin.close();
System.out.println(newPerson);
}
}
二、对象克隆和深度复制
1、以订单为例,下面是一组订单相关的对象,这些对象装在一个clone对象里面
订单对象
package com.fengshu.Spring;
import java.util.List;
public class Order {
@Override
public String toString() {
return "Order [name=" + name + ", orderItems=" + orderItems + "]";
}
private String name;
private List<OrderItem> orderItems;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<OrderItem> getOrderItems() {
return orderItems;
}
public void setOrderItems(List<OrderItem> orderItems) {
this.orderItems = orderItems;
}
}
package com.fengshu.Spring;
public class OrderItem {
@Override
public String toString() {
return "OrderItem [name=" + name + ", price=" + price + "]";
}
private String name;
private double price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
在实际项目中假设以上两个对象用于跟数据库打交道,很多时候我们为了解耦,我们会建立另外一组用于业务逻辑的对象 。如下:
用于业务逻辑的订单对象
package com.fengshu.Spring;
import java.util.List;
public class OrderDTO {
@Override
public String toString() {
return "OrderDTO [name=" + name + ", orderItems=" + orderItems + "]";
}
private String name;
private List<OrderItemDTO> orderItems;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<OrderItemDTO> getOrderItems() {
return orderItems;
}
public void setOrderItems(List<OrderItemDTO> orderItems) {
this.orderItems = orderItems;
}
}
package com.fengshu.Spring;
public class OrderItemDTO {
@Override
public String toString() {
return "OrderItemDTO [name=" + name + "]";
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
用于业务逻辑的订单结算对象
package com.fengshu.Spring;
import java.util.List;
public class SettleOrderDTO {
@Override
public String toString() {
return "SettleOrderDTO [name=" + name + ", orders=" + orders + "]";
}
private String name;
private List<OrderDTO> orders;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<OrderDTO> getOrders() {
return orders;
}
public void setOrders(List<OrderDTO> orders) {
this.orders = orders;
}
}
2、那么问题来了,我们如何一次性的把SettleOrderView里面的内容复制给SettleOrderDTO ,使用过apache或spring的beanUtils工具类的开发人员都知道beanutils在copy对象的过程中是不会帮忙copy对象里面的复杂属性的,即所谓的浅复制,那么如何进行深度复制呢,有一个办法是通过json字符串作为中间对象来进行转换,
3、json方式如下
package com.fengshu.Spring;
import java.util.ArrayList;
import java.util.List;
public class Clone {
private static List<OrderItem> orderItems = new ArrayList<OrderItem>();
private static List<Order> orders = new ArrayList<Order>();
static SettlerOrderView settlerOrderView = new SettlerOrderView();
static {
init();
}
public static void init() {
OrderItem orderItem = new OrderItem();
orderItem.setName("orderitem");
orderItems.add(orderItem);
Order order = new Order();
order.setName("order");
order.setOrderItems(orderItems);
orders.add(order);
settlerOrderView.setName("settlerOrderView");
settlerOrderView.setOrders(orders);
}
public static void main(String[] args) {
try {
String jsonStr = JsonUtils.toJson(settlerOrderView);
System.out.println(jsonStr);
SettleOrderDTO settleOrderDTO = JsonUtils.getObjectMapper().readValue(jsonStr, SettleOrderDTO.class);
jsonStr = JsonUtils.toJson(settleOrderDTO);
System.out.println(jsonStr);//price 为0 被忽略了
} catch (Exception e) {
// TODO: handle exception
}
}
}
package com.fengshu.Spring;
import java.io.IOException;
import org.codehaus.jackson.JsonEncoding;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion;
public class JsonUtils {
public static final String MESSAGE = "message";
@SuppressWarnings("unused")
private static JsonGenerator jsonGenerator;
private static ObjectMapper objectMapper;
@SuppressWarnings("deprecation")
private static void init() {
objectMapper = new ObjectMapper();
objectMapper.getSerializationConfig().setSerializationInclusion(Inclusion.ALWAYS);
objectMapper.getDeserializationConfig().set(org.codehaus.jackson.map.DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
try {
jsonGenerator = objectMapper.getJsonFactory().createJsonGenerator(System.out, JsonEncoding.UTF8);
} catch (IOException e) {
e.printStackTrace();
}
}
public static ObjectMapper getObjectMapper() {
if (objectMapper == null) {
init();
}
return objectMapper;
}
public static String toJson(Object obj) throws JsonGenerationException, JsonMappingException, IOException {
return getObjectMapper().writeValueAsString(obj);
}
public static <T> T formJson(JsonNode json, Class<T> clazz) throws JsonParseException, JsonMappingException, IOException {
return getObjectMapper().readValue(json, clazz);
}
}
4、通过实现clonable来实现对象的深度复制
经实践发现两个不同类的实例对象,即使克隆后好像也不能强制转换,apache的beanutils的cloneBean方法也会报错,还没找到一个好的方案 ,java的大部分资料也很少讨论克隆,如果有人实现了,希望可以交流下。
三、cxf和jackson
在cxf和jackson的配合使用中有以几点心得
1、客户端和服务器端对象之间的传递和返回最后使用json格式
尽量避免以xml的格式来进行对象之间的传递,先不说性能问题,但是使用的复杂性就足以让人望而却步,更重要的是使用xml格式进行对象传递返回的话,需要遵循一大堆的规范,一不小心就报错,而且可能报一个你需要长时间才能找到原因的错误
2、cxf报错的解决步骤
先让自己的cxf发布和调用遵循第一个原则,然后在检查是否遵循相关的规范,比如如果cxf的参数是复杂对象的话cxf必须设置为post请求,get请求将会报错,类似于参数Too long的错误
3、json转换的时候最好忽略null和未知对象
为避免不必要的错误,且减少一些空的属性的传递,最后设置为负率null,可通过注解和xml配置,建议使用第二种方案,第二种方案只需要配置一次就可以了,第一种方案需要给每个pojo加上注解.
4、jackson在转换过长中目前并不支持timestamp 和map作为参数
apache的所有工具类几乎都在不同场景不支持timestamp,不知道什么原因,至于map应该是因为本身结构太复杂了吧,可以通过是配置来解决此问题
5、关于复杂对象的传递
对象里面如果包裹复杂对象原则上是可以传递的,此种情况强烈建议使用json格式进行对象传输和返回,否则需要进行一系列复杂的注解来进行支持,很多时候这些注解你只知其然而不知道其所以然,所以很容易忘掉。
post请求如需要以json格式传输,必须强制指定传递格式为json
@Consumes(MediaType.APPLICATION_JSON)
6、梯子
关于cxf使用的博文 http://my.oschina.net/fengshuzi/blog/280408