org.springframework.beans簡單解讀
這個包的說明是說主要是包括用於操作JavaBean的類和接口,將被大部分spring包使用。在讀這個包的代碼前,我特意將JavaBean規範讀了一遍。JavaBean規範不僅僅是getter、setter,定義了一個完整的輕量級組件模型,事件、方法、屬性、持久化等等支持均包含在內。JavaBean規範很明顯是學習Delphi的組件模型,sun希望通過它來形成一個java組件的市場,可惜結果不如人意,JavaBean在GUI方麵並未形成一個類似delphi控件市場;隨著spring等輕量級框架的流行,而EJB重量級的組件模型被越來越多的人放棄,JavaBean反而在服務端模型方麵占據了主流 。廢話不提,這個包的核心接口和類就是BeanWrapper和BeanWrapperImpl,顧名思義,這個接口就是用於包裝JavaBean的行為,諸如設置和獲取屬性,設置屬性編輯器等(PropertyEditor)。看下這個包的核心類圖:
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.class, new ClassEditor());
this.defaultEditors.put(File.class, new FileEditor());
this.defaultEditors.put(InputStream.class, new InputStreamEditor());
this.defaultEditors.put(Locale.class, new LocaleEditor());
this.defaultEditors.put(Properties.class, new PropertiesEditor());
this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor());
this.defaultEditors.put(String[].class, new StringArrayPropertyEditor());
this.defaultEditors.put(URL.class, new URLEditor());
。。。。。。。
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.class, new ClassEditor());
this.defaultEditors.put(File.class, new FileEditor());
this.defaultEditors.put(InputStream.class, new InputStreamEditor());
this.defaultEditors.put(Locale.class, new LocaleEditor());
this.defaultEditors.put(Properties.class, new PropertiesEditor());
this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor());
this.defaultEditors.put(String[].class, new StringArrayPropertyEditor());
this.defaultEditors.put(URL.class, new 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的解釋: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();
}
}
}
/**
* 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());
* 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是不同的時候,使用弱引用包裝緩存對象,當垃圾收集起發現弱引用時將馬上清除弱引用對象,該弱引用也將加入一個隊列,而WeakHashMap將定時檢查這個隊列,當有新的弱引用達到時(意味著已經被回收)就清除相應的鍵值。請看:
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 {


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

//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 {


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


不知道我的理解是否有誤,如果有誤,請不吝指出,謝謝。
文章轉自莊周夢蝶 ,原文發布時間5.17
最後更新:2017-05-17 13:35:26