天天看點

單例設計模式反射,序列化漏洞及解決方案

單例設計模式的實作方式有很多種,如餓漢式,懶漢式,雙重檢查鎖,靜态内部類,枚舉等等,但是在平時的開發中,我們實作的單利模式是有一定的漏洞的,可以通過反射或者序列化以及反序列化擷取不同的執行個體,雖然這個漏洞在系統運作的時候不會展現出來,但是在開發時也是值得注意的問題。

以下是一個簡單的餓漢式的單利模式的代碼實作:

當我們需要擷取Singleton對象的時候,直接調用靜态方法getInstance就可以了:

但是學過反射的人都知道,通過反射技術也能擷取到一個類的執行個體對象,即使它的構造函數時私有化的,我們也可以通過暴力通路來調用其構造函數,是以以上測試類的運作結果為:

com.spring.designmodel.Singleton@73fbaf73

com.spring.designmodel.Singleton@3f4f44

com.spring.designmodel.Singleton@3c6cf97c

可以看出通過調用getInstance方法擷取到的執行個體是一樣的,但是通過反射擷取到的執行個體卻是不同的,違反了單例設計模式的思想,那麼我們應該怎麼解決呢?我們隻需要在私有的構造函數中加入一個判斷即可:

此時,我們再次啟動測試類,獲得到以下結果:

java.lang.reflect.InvocationTargetException

at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)

at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)

at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)

at java.lang.reflect.Constructor.newInstance(Constructor.java:526)

at org.springsource.loaded.ri.ReflectiveInterceptor.jlrConstructorNewInstance(ReflectiveInterceptor.java:1075)

at com.spring.designmodel.SingletonTest.main(SingletonTest.java:19)

Caused by: java.lang.RuntimeException

at com.spring.designmodel.Singleton.(Singleton.java:13)

… 6 more

當然,就解決了使用反射技術來擷取不同執行個體的問題了。

如果我們的單例類實作了Serializable接口,那麼這個類就能進行序列化和反序列化,測試代碼如下:

運作結果如下:

com.spring.designmodel.Singleton@139008b

com.spring.designmodel.Singleton@221c3dfe

我們發現進過序列化及反序列化之後對象的引用就改變了,顯然也是違反了單例設計模式的思想的,跟蹤readObject源碼後,發現這個方法會先寫出一個newInstance,然後判斷這個對象中是否存在readResolve這個方法,如果不存在,那麼直接傳回這個newInstance,如果存在,那麼就調用readResolve這個方法,将這個方法的傳回值傳回給readObject.源碼片段如下:

由上可知,我們隻需要在Singleton這個類中添加一個readResolve這個方法即可。

再次啟用測試類,運作結果如下:

com.spring.designmodel.Singleton@403729c5

繼續閱讀