廉頗老矣,尚能飯否

          java:從技術(shù)到管理

          常用鏈接

          統(tǒng)計(jì)

          最新評論

          java對象序列化所引起的循環(huán)引用的思考[轉(zhuǎn)載]

          在Java中的模型關(guān)系設(shè)計(jì)中,循環(huán)的關(guān)系是很常見的,特別是ORM的出現(xiàn),關(guān)系的循環(huán)更加有利于維護(hù)各自的狀態(tài)。但就是這種循環(huán)的關(guān)系,在java對象序列化時(shí),不可避免的會(huì)導(dǎo)致循環(huán)引用(Cycle Reference)的問題。


          導(dǎo)致CycleReference的情況

            
           還是舉個(gè)例子吧(代碼說話):一個(gè)很典型,很常見的雙向引用的對象關(guān)系。

           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


          另外一種導(dǎo)致這個(gè)問題的情況是,多個(gè)對象間關(guān)系形成一條回路,跟電學(xué)有點(diǎn)像。(具體就不舉例了,在模型設(shè)計(jì)時(shí),最好還是避免出現(xiàn)這種情況。)

          導(dǎo)致這種問題的原因

          用jaxb(如果沒有特殊解釋,本文采用的都是jaxb)序列化Company時(shí),無疑會(huì)發(fā)生循環(huán)引用。
          當(dāng)序列化引擎解析Company時(shí),它發(fā)現(xiàn)這個(gè)對象持有一個(gè)Employee的引用,轉(zhuǎn)而去騷擾Employee。解析Employee時(shí),發(fā)現(xiàn)他又持有Company的引用,又轉(zhuǎn)回Company。如此問題產(chǎn)生。
          引擎它并不知道在遇到循環(huán)引用問題時(shí),該怎么處理,它只是忠實(shí)得按照固定的算法去執(zhí)行任務(wù)。所以我們得讓引擎聰明點(diǎn),我們得讓引擎遇到這種問題有處理機(jī)制。

          解決問題

          既然需要引擎更聰明,那就沒辦法,就得燒香拜佛,希望制造商的產(chǎn)品擴(kuò)展性足夠好,考慮了足夠多的情況。
          幸運(yùn)的是,jaxb提供了有這種擴(kuò)展。(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    //當(dāng)遇到循環(huán)引用時(shí),用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方法就是一個(gè)回調(diào)方法,當(dāng)遇到循環(huán)引用時(shí),jaxb引擎會(huì)調(diào)用這個(gè)方法,用return對象來替換this。所以在這個(gè)過程中,就可以將引起循環(huán)引用問題的關(guān)系斷開,返回給jaxb
          當(dāng)然Company和Employee對象沒必要全部實(shí)現(xiàn)CycleRecoverable接口,但是拿到現(xiàn)實(shí)的模型圖中去,誰也保證不了他們不跟別的對象有循環(huán)的關(guān)系存在。因此還是建議大家把模型都實(shí)現(xiàn)這個(gè)接口,也好一勞永逸。

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



          當(dāng)一個(gè)類實(shí)現(xiàn)了序列化接口,有時(shí)會(huì)遇到 java.io.InvalidClassException 異常出現(xiàn):
          java.io.InvalidClassException: com.test.Test; local class incompatible: stream classdesc serialVersionUID = 7981560250804078637, local class serialVersionUID = -8334405535174160822
          這是序列化兼容性所致;
          java通過一個(gè)名為UID(stream unique identifier)來控制,這個(gè)UID是隱式的,它通過類名,方法名等諸多因素經(jīng)過計(jì)算而得,理論上是一一映射的關(guān)系,也就是唯一的。如果UID不一樣的話,就無法實(shí)現(xiàn)反序列化了,并且將會(huì)得到InvalidClassException。
          當(dāng)要人為的產(chǎn)生一個(gè)新的版本(實(shí)現(xiàn)并沒有改動(dòng)),而拋棄以前的版本的話,可以通過顯式的聲名UID來實(shí)現(xiàn):
          private static final long serialVersionUID=????;//(你可以編造一個(gè)UID,但不能有重復(fù))

          對于上例我們可以在com.test.Test類中加入 :
          private static final long serialVersionUID=7981560250804078637l;
          這樣就解決了新老版本的兼容性問題。
          當(dāng)然,對于序列化還有很多問題,慢慢研究吧。


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

          posted on 2009-01-14 13:10 liudecai_zan@126.com 閱讀(1593) 評論(0)  編輯  收藏 所屬分類: 在路上

          主站蜘蛛池模板: 伊吾县| 扶沟县| 长垣县| 南丰县| 阿尔山市| 阿荣旗| 清苑县| 嘉荫县| 年辖:市辖区| 西平县| 洪湖市| 义乌市| 桂平市| 南昌市| 尚志市| 静海县| 塔城市| 平顶山市| 进贤县| 图木舒克市| 永登县| 商水县| 康平县| 都江堰市| 余江县| 三穗县| 海城市| 揭东县| 隆德县| 玉山县| 鄯善县| 顺平县| 瓮安县| 蓝田县| 朔州市| 迁安市| 安庆市| 克东县| 乾安县| 黑山县| 沽源县|