package org.theonefx.wcframework.ioc;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.WeakHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.theonefx.wcframework.core.exception.BeansException;
import org.theonefx.wcframework.core.exception.FatalBeanException;
import org.theonefx.wcframework.utils.ClassUtils;
import org.theonefx.wcframework.utils.StringUtils;
/**
* 框架的内部使用类,用于根据Class来缓存JavaBean的{@link java.beans.PropertyDescriptor}信息。
* 直接使用本类进行开发可能会发生意想不到的错误,所以框架的使用者不要使用此类。
*
* <p>
* Necessary for own caching of descriptors within the application's ClassLoader,
* 而不依赖与JDK本身的BeanIndo缓存(以避免类加载器关闭时发生内存泄露)
*
* <p>
* 因为使用static变量缓存数据,所以我们不需要在操作每一个JavaBean时都实例化新的对象。
* 因此,这个类实现了工厂模式,使用一个private的构造函数和一个static的{@link #forClass(Class)}方法来获取实例
*/
public class CachedIntrospectionResults {
private static final Log log = LogFactory.getLog(CachedIntrospectionResults.class);
/**
* 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<Class<?>, Object> classCache = Collections.synchronizedMap(new WeakHashMap<Class<?>, Object>());
/** 自行类对应的BeanInfo对象 */
private final BeanInfo beanInfo;
/** 以property name为键的PropertyDescriptor对象MAP */
private final Map<String, PropertyDescriptor> propertyDescriptorCache;
/**
* 根据给定的Class创建一个新的CachedIntrospectionResults实例
* 注意,这个构造函数是private的
*
* @param beanClass 需要用于分析的Class
* @param cacheFullMetadata 是否缓存所有元数据
* @throws BeansException 自省失败的时候抛出这个异常
*/
private CachedIntrospectionResults(Class<?> beanClass, boolean cacheFullMetadata) throws BeansException {
try {
if (log.isTraceEnabled()) {
log.trace("获取类[" + beanClass.getName() + "]的BeanInfo");
}
this.beanInfo = Introspector.getBeanInfo(beanClass);
// 此时已经获取了beanInfo,所以立刻从Introspector的缓存中删除class相关的全部BeanInfo。
// 为的是让类加载器在shutdown时可以被对应的垃圾回收其回收其中的BeanInfo对象,因为我们已经适合垃圾回收的方式将他缓存起来了。
// 和CachedIntrospectionResults相比,java原生的自行工具类Introspector没有使用WeakReferences
Class<?> classToFlush = beanClass;
do {
Introspector.flushFromCaches(classToFlush);
classToFlush = classToFlush.getSuperclass();
} while (classToFlush != null);
if (log.isTraceEnabled()) {
log.trace("缓存类[" + beanClass.getName() + "]的PropertyDescriptor");
}
this.propertyDescriptorCache = new LinkedHashMap<String, PropertyDescriptor>();
// 这个方法调用速度很慢,所以我们只调用一次
PropertyDescriptor[] pds = this.beanInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : pds) {
if (Class.class.equals(beanClass) && "classLoader".equals(pd.getName())) {
// 忽略 Class.getClassLoader() 方法,因为没有人会需要绑定这个方法
continue;
}
if (log.isTraceEnabled()) {
log.trace("发现Bean的property '" + pd.getName() + "'" +
(pd.getPropertyType() != null ? " 类型是 [" + pd.getPropertyType().getName() + "]" : "") +
(pd.getPropertyEditorClass() != null ?
"; editor是 [" + pd.getPropertyEditorClass().getName() + "]" : ""));
}
if (cacheFullMetadata) {
pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd);
}
this.propertyDescriptorCache.put(pd.getName(), pd);
}
} catch (IntrospectionException ex) {
throw new FatalBeanException("获取 [" + beanClass.getName() + "]的BeanInfo失败", ex);
}
}
/**
* 根据给定的Class创建CachedIntrospectionResults.
* <P>
* 我们在这里不想使用synchronization,因为对象references是原子的,所以我们可以接受只在启动的时候做一些偶尔的以及不必要的查找工作。
*
* @param beanClass 供分析的Class
* @return 对应的 CachedIntrospectionResults
* @throws BeansException 内省失败是发生异常
*/
public static CachedIntrospectionResults forClass(Class<?> beanClass) throws BeansException {
CachedIntrospectionResults results;
Object value = classCache.get(beanClass);
if (value instanceof Reference) {
Reference<?> ref = (Reference<?>) value;
results = (CachedIntrospectionResults) ref.get();
} else {
results = (CachedIntrospectionResults) value;
}
if (results == null) {
// 在JDK1.5和更高的版本里,缓存一个class几乎总是安全的
// 唯一可能出现异常的情况是自定义的BeanInfo类被提供给了一个不安全的ClassLoader
boolean fullyCacheable = ClassUtils.isCacheSafe(beanClass, CachedIntrospectionResults.class.getClassLoader());
if (fullyCacheable || !ClassUtils.isPresent(beanClass.getName() + "BeanInfo", beanClass.getClassLoader())) {
results = new CachedIntrospectionResults(beanClass, fullyCacheable);
classCache.put(beanClass, results);
} else {
if (log.isDebugEnabled()) {
log.debug("Not strongly caching class [" + beanClass.getName() + "] because it is not cache-safe");
}
results = new CachedIntrospectionResults(beanClass, true);
classCache.put(beanClass, new WeakReference<CachedIntrospectionResults>(results));
}
}
return results;
}
public Class<?> getBeanClass() {
return this.beanInfo.getBeanDescriptor().getBeanClass();
}
public PropertyDescriptor getPropertyDescriptor(String name) {
PropertyDescriptor pd = this.propertyDescriptorCache.get(name);
if (pd == null && StringUtils.isNotBlank(name)) {
pd = this.propertyDescriptorCache.get(name.substring(0, 1).toLowerCase() + name.substring(1));
if (pd == null) {
pd = this.propertyDescriptorCache.get(name.substring(0, 1).toUpperCase() + name.substring(1));
}
}
return (pd == null || pd instanceof GenericTypeAwarePropertyDescriptor ? pd : buildGenericTypeAwarePropertyDescriptor(getBeanClass(), pd));
}
public PropertyDescriptor[] getPropertyDescriptors() {
PropertyDescriptor[] pds = new PropertyDescriptor[this.propertyDescriptorCache.size()];
int i = 0;
for (PropertyDescriptor pd : this.propertyDescriptorCache.values()) {
pds[i] = (pd instanceof GenericTypeAwarePropertyDescriptor ? pd : buildGenericTypeAwarePropertyDescriptor(getBeanClass(), pd));
i++;
}
return pds;
}
private PropertyDescriptor buildGenericTypeAwarePropertyDescriptor(Class<?> beanClass, PropertyDescriptor pd) {
try {
return new GenericTypeAwarePropertyDescriptor(beanClass, pd.getName(), pd.getReadMethod(), pd.getWriteMethod(), pd.getPropertyEditorClass());
} catch (IntrospectionException ex) {
throw new FatalBeanException("对类[" + beanClass.getName() + "]的重新自省失败", ex);
}
}
}