廉颇老矣,尚能饭否

java:从技术到管理

常用链接

统计

最新评论

java对象序列化所引起的循环引用的思考[转载]

在Java中的模型关系设计中,循环的关系是很常见的,特别是ORM的出现,关系的循环更加有利于维护各自的状态。但就是这种循环的关系,在java对象序列化时,不可避免的会导致循环引用(Cycle Reference)的问题。


导致CycleReference的情况

  
 还是举个例子吧(代码说话):一个很典型,很常见的双向引用的对象关系。

 1@Entity
 2
 3public class Company{
 4
 5    @Id
 6
 7    @Column(.)
 8
 9    private String id;
10
11    @OneToMany(.)
12
13    private Set<Employee> employee = new HashSet<Employee>();
14
15    //setters/getters
16
17    
18
19}

20
21@Entity
22
23public class Employee{
24
25    @Id
26
27    @Column(.)
28
29    private String id;
30
31    @ManyToOne
32
33    private Company company;
34
35    //setters/getters
36
37    
38
39}

40
41


另外一种导致这个问题的情况是,多个对象间关系形成一条回路,跟电学有点像。(具体就不举例了,在模型设计时,最好还是避免出现这种情况。)

导致这种问题的原因

用jaxb(如果没有特殊解释,本文采用的都是jaxb)序列化Company时,无疑会发生循环引用。
当序列化引擎解析Company时,它发现这个对象持有一个Employee的引用,转而去骚扰Employee。解析Employee时,发现他又持有Company的引用,又转回Company。如此问题产生。
引擎它并不知道在遇到循环引用问题时,该怎么处理,它只是忠实得按照固定的算法去执行任务。所以我们得让引擎聪明点,我们得让引擎遇到这种问题有处理机制。

解决问题

既然需要引擎更聪明,那就没办法,就得烧香拜佛,希望制造商的产品扩展性足够好,考虑了足够多的情况。
幸运的是,jaxb提供了有这种扩展。(CycleRecoverable接口)
行军打仗(例子先行):(将以上的例子改下)

 1@Entity
 2
 3public class Company implements CycleRecoverable{
 4
 5    @Id
 6
 7    @Column(.)
 8
 9    private String id;
10
11    @OneToMany(.)
12
13    private Set<Employee> employee = new HashSet<Employee>();
14
15    //当遇到循环引用时,用temp替代this,返给jaxb解析。
16
17    public Object onCycleDetected(Context arg0) {
18
19        Company temp = new Company ();
20
21        temp.setId(id);
22
23        return temp;
24
25    }

26
27    //setters/getters
28
29    
30
31}

32
33@Entity
34
35public class Employee implements CycleRecoverable{
36
37    @Id
38
39    @Column(.)
40
41    private String id;
42
43    @ManyToOne
44
45    private Company company;
46
47    public Object onCycleDetected(Context arg0) {
48
49        Employee temp = new Employee();
50
51        temp.setId(id);
52
53        return temp;
54
55    }

56
57  //setters/getters
58
59    
60
61}

62
63


注意到onCycleDetected方法就是一个回调方法,当遇到循环引用时,jaxb引擎会调用这个方法,用return对象来替换this。所以在这个过程中,就可以将引起循环引用问题的关系断开,返回给jaxb
当然Company和Employee对象没必要全部实现CycleRecoverable接口,但是拿到现实的模型图中去,谁也保证不了他们不跟别的对象有循环的关系存在。因此还是建议大家把模型都实现这个接口,也好一劳永逸。

希望大家得到了想要的东西。



当一个类实现了序列化接口,有时会遇到 java.io.InvalidClassException 异常出现:
java.io.InvalidClassException: com.test.Test; local class incompatible: stream classdesc serialVersionUID = 7981560250804078637, local class serialVersionUID = -8334405535174160822
这是序列化兼容性所致;
java通过一个名为UID(stream unique identifier)来控制,这个UID是隐式的,它通过类名,方法名等诸多因素经过计算而得,理论上是一一映射的关系,也就是唯一的。如果UID不一样的话,就无法实现反序列化了,并且将会得到InvalidClassException。
当要人为的产生一个新的版本(实现并没有改动),而抛弃以前的版本的话,可以通过显式的声名UID来实现:
private static final long serialVersionUID=????;//(你可以编造一个UID,但不能有重复)

对于上例我们可以在com.test.Test类中加入 :
private static final long serialVersionUID=7981560250804078637l;
这样就解决了新老版本的兼容性问题。
当然,对于序列化还有很多问题,慢慢研究吧。


柳德才
13691193654
18942949207
QQ:422157370
liudecai_zan@126.com
湖北-武汉-江夏-庙山

posted on 2009-01-14 13:10 liudecai_zan@126.com 阅读(1582) 评论(0)  编辑  收藏 所属分类: 在路上


只有注册用户登录后才能发表评论。


网站导航: