這個(gè)包的說明是說主要是包括用于操作JavaBean的類和接口,將被大部分spring包使用。在讀這個(gè)包的代碼前,我特意將JavaBean規(guī)范讀了 一遍。JavaBean規(guī)范不僅僅是getter、setter,定義了一個(gè)完整的輕量級(jí)組件模型,事件、方法、屬性、持久化等等支持均包含在內(nèi)。 JavaBean規(guī)范很明顯是學(xué)習(xí)Delphi的組件模型,sun希望通過它來形成一個(gè)java組件的市場(chǎng),可惜結(jié)果不如人意,JavaBean在GUI 方面并未形成一個(gè)類似delphi控件市場(chǎng);隨著spring等輕量級(jí)框架的流行,而EJB重量級(jí)的組件模型被越來越多的人放棄,JavaBean反而在 服務(wù)端模型方面占據(jù)了主流 。廢話不提,這個(gè)包的核心接口和類就是BeanWrapper和BeanWrapperImpl,顧名思義,這個(gè)接口就是用于包裝JavaBean的行 為,諸如設(shè)置和獲取屬性,設(shè)置屬性編輯器等(PropertyEditor)??聪逻@個(gè)包的核心類圖:
BeanWrapper 接口繼承了PropertyAccessor(用于屬性訪問和設(shè)置)和PropertyEditorRegistry(屬性編輯器的獲取和設(shè)置),而 BeanWrapperImpl除了實(shí)現(xiàn)BeanWrapper接口外還繼承自PropertyEditorRegistrySupport 類。在PropertyEditorRegistrySupport 類中可以看到spring默認(rèn)設(shè)置的一系列自定義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());
。。。。。。。
PropertyEditor的概念就是屬性編輯器,或者說屬性轉(zhuǎn)換器,比如我們?cè)趕pring的配置文件中設(shè)置某個(gè)bean的class,這是一個(gè)字符串,怎么轉(zhuǎn)換為一個(gè)Class對(duì)象呢?通過上面注冊(cè)的ClassEditor,看看這個(gè)類是怎么實(shí)現(xiàn)的:
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 {
//調(diào)用輔助類,得到Class對(duì)象
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();
}
}
}
代碼已經(jīng)解釋了一切,繼承javabean的PropertyEditorSupport,自己實(shí)現(xiàn)轉(zhuǎn)換即可。這個(gè)包另外就是定義了一個(gè)完整的異常體系, 值的我們參考。另外一個(gè)值的注意的地方是CachedIntrospectionResults類的實(shí)現(xiàn),這個(gè)類使用了單例模式,它的作用在于緩存 JavaBean反省(Introspect)得到的信息,因?yàn)槊看问褂肐ntrospector對(duì)獲取JavaBean信息是個(gè)不小的性能開支。緩存使 用的是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());
因?yàn)榫彺媸褂玫膋ey是bean的Class對(duì)象(以保證唯一性),因此在應(yīng)用存在多個(gè)class loaders的時(shí)候,為了保證垃圾收集的進(jìn)行,不出現(xiàn)內(nèi)存泄露而采用WeakHashMap,為了理解這一點(diǎn),我用JProfiler測(cè)試了自定義 ClassLoader情況下,內(nèi)存堆的使用情況,從快照上看。在使用HashMap的情況下,因?yàn)闇y(cè)試的bean的Class對(duì)象被載入它的 ClassLoader以及java.beans.BeanInfo,java.beans.PropertyDescriptor, java.lang.reflect.Method這四個(gè)對(duì)象強(qiáng)引用,而導(dǎo)致不可回收。而在使用WeakHashMap時(shí),判斷當(dāng)載入bean的 ClassLoader和載入CachedIntrospectionResults的ClassLoader是不同的時(shí)候,使用弱引用包裝緩存對(duì)象,當(dāng) 垃圾收集起發(fā)現(xiàn)弱引用時(shí)將馬上清除弱引用對(duì)象,該弱引用也將加入一個(gè)隊(duì)列,而WeakHashMap將定時(shí)檢查這個(gè)隊(duì)列,當(dāng)有新的弱引用達(dá)到時(shí)(意味著已 經(jīng)被回收)就清除相應(yīng)的鍵值。請(qǐng)看:
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));
}