org.springframework.beans簡單解讀

dennis_zane發表於2007-04-16
    這個包的說明是說主要是包括用於操作JavaBean的類和介面,將被大部分spring包使用。在讀這個包的程式碼前,我特意將JavaBean規範讀了一遍。JavaBean規範不僅僅是getter、setter,定義了一個完整的輕量級元件模型,事件、方法、屬性、持久化等等支援均包含在內。JavaBean規範很明顯是學習Delphi的元件模型,sun希望通過它來形成一個java元件的市場,可惜結果不如人意,JavaBean在GUI方面並未形成一個類似delphi控制元件市場;隨著spring等輕量級框架的流行,而EJB重量級的元件模型被越來越多的人放棄,JavaBean反而在服務端模型方面佔據了主流 。廢話不提,這個包的核心介面和類就是BeanWrapper和BeanWrapperImpl,顧名思義,這個介面就是用於包裝JavaBean的行為,諸如設定和獲取屬性,設定屬性編輯器等(PropertyEditor)。看下這個包的核心類圖:
spring-bean.jpg
BeanWrapper介面繼承了PropertyAccessor(用於屬性訪問和設定)和PropertyEditorRegistry(屬性編輯器的獲取和設定),而BeanWrapperImpl除了實現BeanWrapper介面外還繼承自PropertyEditorRegistrySupport 類。在PropertyEditorRegistrySupport 類中可以看到spring預設設定的一系列自定義PropertyEditor。比如:
protected void registerDefaultEditors() {
        
this.defaultEditors = new HashMap(32);

        
// Simple editors, without parameterization capabilities.
        
// The JDK does not contain a default editor for any of these target types.
        this.defaultEditors.put(Class.classnew ClassEditor());
        
this.defaultEditors.put(File.classnew FileEditor());
        
this.defaultEditors.put(InputStream.classnew InputStreamEditor());
        
this.defaultEditors.put(Locale.classnew LocaleEditor());
        
this.defaultEditors.put(Properties.classnew PropertiesEditor());
        
this.defaultEditors.put(Resource[].classnew ResourceArrayPropertyEditor());
        
this.defaultEditors.put(String[].classnew StringArrayPropertyEditor());
        
this.defaultEditors.put(URL.classnew URLEditor());

。。。。。。。

    PropertyEditor的概念就是屬性編輯器,或者說屬性轉換器,比如我們在spring的配置檔案中設定某個bean的class,這是一個字串,怎麼轉換為一個Class物件呢?通過上面註冊的ClassEditor,看看這個類是怎麼實現的:

public class ClassEditor extends PropertyEditorSupport {

    
private final ClassLoader classLoader;

    /**
     * Create a default ClassEditor, using the given ClassLoader.
     * 
@param classLoader the ClassLoader to use
     * (or <code>null</code> for the thread context ClassLoader)
     
*/
    
public ClassEditor(ClassLoader classLoader) {
        
this.classLoader =
                (classLoader 
!= null ? classLoader : Thread.currentThread().getContextClassLoader());
    }


    
public void setAsText(String text) throws IllegalArgumentException {
        
if (StringUtils.hasText(text)) {
            
try {
                //呼叫輔助類,得到Class物件
                setValue(ClassUtils.forName(text.trim(), 
this.classLoader));
            }
            
catch (ClassNotFoundException ex) {
                
throw new IllegalArgumentException("Class not found: " + ex.getMessage());
            }
        }
        
else {
            setValue(
null);
        }
    }

    
public String getAsText() {
        Class clazz 
= (Class) getValue();
        
if (clazz == null) {
            
return "";
        }
        
if (clazz.isArray()) {
            
return clazz.getComponentType().getName() + ClassUtils.ARRAY_SUFFIX;
        }
        
else {
            
return clazz.getName();
        }
    }

}
    程式碼已經解釋了一切,繼承javabean的PropertyEditorSupport,自己實現轉換即可。這個包另外就是定義了一個完整的異常體系,值的我們參考。另外一個值的注意的地方是CachedIntrospectionResults類的實現,這個類使用了單例模式,它的作用在於快取JavaBean反省(Introspect)得到的資訊,因為每次使用Introspector對獲取JavaBean資訊是個不小的效能開支。快取使用的是WeakHashMap,而不是HashMap,看看spring的解釋:
/**
     * Map keyed by class containing CachedIntrospectionResults.
     * Needs to be a WeakHashMap with WeakReferences as values to allow
     * for proper garbage collection in case of multiple class loaders.
     
*/
    
private static final Map classCache = Collections.synchronizedMap(new WeakHashMap());

因為快取使用的key是bean的Class物件(以保證唯一性),因此在應用存在多個class loaders的時候,為了保證垃圾收集的進行,不出現記憶體洩露而採用WeakHashMap,為了理解這一點,我用JProfiler測試了自定義ClassLoader情況下,記憶體堆的使用情況,從快照上看。在使用HashMap的情況下,因為測試的bean的Class物件被載入它的ClassLoader以及java.beans.BeanInfo,java.beans.PropertyDescriptor,java.lang.reflect.Method這四個物件強引用,而導致不可回收。而在使用WeakHashMap時,判斷當載入bean的ClassLoader和載入CachedIntrospectionResults的ClassLoader是不同的時候,使用弱引用包裝快取物件,保證能被回收。請看:
private static boolean isCacheSafe(Class clazz) {
        
//CachedIntrospectionResults的ClassLoader
        ClassLoader cur = CachedIntrospectionResults.class.getClassLoader();
        
//載入bean的ClassLoader
        ClassLoader target = clazz.getClassLoader();
        
if (target == null || cur == target) {
            
return true;
        }
        
while (cur != null) {
            cur 
= cur.getParent();
            
if (cur == target) {
                
return true;
            }
        }
        
return false;
    }

public static CachedIntrospectionResults forClass(Class beanClass) throws BeansException {

   dot.gifdot.gif
   
boolean cacheSafe = isCacheSafe(beanClass);
   
if (cacheSafe) {
                classCache.put(beanClass, results);
            }
            
else {
           //弱引用   
            classCache.put(beanClass, new WeakReference(results));

            }
   dot.gifdot.gif

    不知道我的理解是否有誤,如果有誤,請不吝指出,謝謝。

110889.html

dennis 2007-04-16 10:23 發表評論

相關文章