SpringContext(7)-CachedIntrospectionResults
BeanWrapperImpl对象中会缓存每个Property的PropertyDescriptor以及Bean的相关信息到CachedIntrospectionResults对象中,BeanWrapperImpl调用CachedIntrospectionResults的静态方法forClass来获取CachedIntrospectionResults对象
static CachedIntrospectionResults forClass(Class<?> beanClass) throws BeansException {
CachedIntrospectionResults results = strongClassCache.get(beanClass);
if (results != null) {
return results;
}
results = softClassCache.get(beanClass);
if (results != null) {
return results;
}
results = new CachedIntrospectionResults(beanClass);
ConcurrentMap<Class<?>, CachedIntrospectionResults> classCacheToUse;
if (ClassUtils.isCacheSafe(beanClass, CachedIntrospectionResults.class.getClassLoader()) ||
isClassLoaderAccepted(beanClass.getClassLoader())) {
classCacheToUse = strongClassCache;
}
else {
classCacheToUse = softClassCache;
}
CachedIntrospectionResults existing = classCacheToUse.putIfAbsent(beanClass, results);
return (existing != null ? existing : results);
}CachedIntrospectionResults内部维护了两个静态缓存:strongClassCache和softClassCache,均为ConcurrentMap类型,key为Class对象,value为解析出的CachedIntrospectionResults,区别是前者存储强引用,后者是ConcurrentReferenceHashMap类型的Map,存储的弱引用,一个Class满足下列条件中的一种时,存储强引用,否则存储弱引用:
- Class对象的ClassLoader与CachedIntrospectionResults的ClassLoader相同或者前者是后者的子加载器
- 上面不符合时,需要Class对象可被CachedIntrospectionResults的ClassLoader加载
- Class对象的ClassLoader在CachedIntrospectionResults的静态acceptedClassLoaders里
forClass静态方法通过调用构造器private CachedIntrospectionResults(Class<?> beanClass)来初始化新对象:
private CachedIntrospectionResults(Class<?> beanClass) throws BeansException {
try {
this.beanInfo = getBeanInfo(beanClass);
this.propertyDescriptorCache = new LinkedHashMap<>();
// This call is slow so we do it once.
PropertyDescriptor[] pds = this.beanInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : pds) {
if (Class.class == beanClass &&
("classLoader".equals(pd.getName()) || "protectionDomain".equals(pd.getName()))) {
// Ignore Class.getClassLoader() and getProtectionDomain() methods - nobody needs to bind to those
continue;
}
pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd);
this.propertyDescriptorCache.put(pd.getName(), pd);
}
// Explicitly check implemented interfaces for setter/getter methods as well,
// in particular for Java 8 default methods...
Class<?> currClass = beanClass;
while (currClass != null && currClass != Object.class) {
introspectInterfaces(beanClass, currClass);
currClass = currClass.getSuperclass();
}
this.typeDescriptorCache = new ConcurrentReferenceHashMap<>();
}
catch (IntrospectionException ex) {
throw new FatalBeanException("Failed to obtain BeanInfo for class [" + beanClass.getName() + "]", ex);
}
}- 调用getBeanInfo(beanClass)结果放入新CachedIntrospectionResults实例的beanInfo属性中
- 调用beanInfo属性的getPropertyDescriptors()方法获取所有的PropertyDescriptor构成的数组
- 迭代每个PropertyDescriptor对象,对其调用buildGenericTypeAwarePropertyDescriptor方法进行解析,解析后将PropertyDescriptor放入新CachedIntrospectionResults实例的propertyDescriptorCache缓存中
- 递归检查Class的继承链,解析可能有的接口的default方法(Since Java 8)
- 初始化typeDescriptorCache
1.1 Class到BeanInfo:getBeanInfo方法
private static BeanInfo getBeanInfo(Class<?> beanClass) throws IntrospectionException {
for (BeanInfoFactory beanInfoFactory : beanInfoFactories) {
BeanInfo beanInfo = beanInfoFactory.getBeanInfo(beanClass);
if (beanInfo != null) {
return beanInfo;
}
}
return (shouldIntrospectorIgnoreBeaninfoClasses ?
Introspector.getBeanInfo(beanClass, Introspector.IGNORE_ALL_BEANINFO) :
Introspector.getBeanInfo(beanClass));
}- 遍历beanInfoFactories,解析Class对象来获取BeanInfo,如果获取成功则返回获取的BeanInfo
- 如果第一步未获取到,则根据shouldIntrospectorIgnoreBeaninfoClasses属性(可以在初始化时定义"spring.beaninfo.ignore"参数来修改,),调用Introspector类的getBeanInfo方法获取BeanInfo
1.1.1 beanInfoFactory及其getBeanInfo方法
beanInfoFactories:在CachedIntrospectionResults类加载时调用SpringFactoriesLoader.loadFactories(BeanInfoFactory.class, CachedIntrospectionResults.class.getClassLoader())方法来加载,这个方法会扫描所有Classpath(包括Jar包)下定义的META-INF/spring.factories文件,解析内部所有的factoryClassName-factoryName对,通过使用LinkedMultiValueMap(每个key对应一个LinkedList),将factoryClassName对应的factoryName全部存储到LinkedList中,初始化实例后排序并返回
由于spring-beans的jar包下有定义META-INF/spring.factories文件,且内部有定义BeanInfoFactory:org.springframework.beans.BeanInfoFactory=org.springframework.beans.ExtendedBeanInfoFactory,因此实际上至少会调用到ExtendedBeanInfoFactory的getBeanInfo方法:
@Override
@Nullable
public BeanInfo getBeanInfo(Class<?> beanClass) throws IntrospectionException {
return (supports(beanClass) ? new ExtendedBeanInfo(Introspector.getBeanInfo(beanClass)) : null);
}- 调用supports方法,判断是否满足此Factory应用的条件:beanClass是否有定义或者继承返回的beanProperty或者setter方法
- 如果上述为真,则调用Instrospector的getBeanInfo方法来获取BeanInfo,并包裹到ExtendedBeanInfo对象中返回,否则返回null,交由调用者处理
1.1.1.1 ExtendedBeanInfoFactory的suppoorts判断
/**
* Return whether the given bean class declares or inherits any non-void
* returning bean property or indexed property setter methods.
*/
private boolean supports(Class<?> beanClass) {
for (Method method : beanClass.getMethods()) {
if (ExtendedBeanInfo.isCandidateWriteMethod(method)) {
return true;
}
}
return false;
}其中对Method的判断如下:
public static boolean isCandidateWriteMethod(Method method) {
String methodName = method.getName();
Class<?>[] parameterTypes = method.getParameterTypes();
int nParams = parameterTypes.length;
return (methodName.length() > 3 && methodName.startsWith("set") && Modifier.isPublic(method.getModifiers()) &&
(!void.class.isAssignableFrom(method.getReturnType()) || Modifier.isStatic(method.getModifiers())) &&
(nParams == 1 || (nParams == 2 && int.class == parameterTypes[0])));
}要求方法必须满足下面的所有条件,才算可能的write方法
- 方法名为setX形式
- 方法是public
- 方法返回值为非void或者是static方法
- 只有一个参数或两个参数但第一个参数为int型
一般来说,定义JavaBean的时候,set方法返回值均为void,不符合第三点,因此返回false
1.1.2 Introspector类的getBeanInfo方法
Instrospector类是rt.jar包中提供的工具类,调用时,先查看ThreadGroupContext的beanInfoCache是否已有BeanInfo被缓存,否则构造一个新的Introspector实例new Introspector(beanClass, null, USE_ALL_BEANINFO).getBeanInfo(),并调用新实例的getBeanInfo方法,如果当前类有父类,则优先构造父类的BeanInfo(这是个接口,实际上返回的是GenericBeanInfo对象),具体的获取BeanInfo的方法参考JDK实现
1.2. 调用beanInfo属性的getPropertyDescriptors()方法获取所有的PropertyDescriptor构成的数组
具体参考JDK中java.beans包中Introspector的内部类GenericBeanInfo的实现
1.3. buildGenericTypeAwarePropertyDescriptor
尝试把PropertyDescriptor的属性解构,存入GenericTypeAwarePropertyDescriptor对象中:new GenericTypeAwarePropertyDescriptor(beanClass, pd.getName(), pd.getReadMethod(),pd.getWriteMethod(), pd.getPropertyEditorClass())
GenericTypeAwarePropertyDescriptor对应的构造方法为
public GenericTypeAwarePropertyDescriptor(Class<?> beanClass, String propertyName,
@Nullable Method readMethod, @Nullable Method writeMethod, Class<?> propertyEditorClass)
throws IntrospectionException {
super(propertyName, null, null);
this.beanClass = beanClass;
Method readMethodToUse = (readMethod != null ? BridgeMethodResolver.findBridgedMethod(readMethod) : null);
Method writeMethodToUse = (writeMethod != null ? BridgeMethodResolver.findBridgedMethod(writeMethod) : null);
if (writeMethodToUse == null && readMethodToUse != null) {
// Fallback: Original JavaBeans introspection might not have found matching setter
// method due to lack of bridge method resolution, in case of the getter using a
// covariant return type whereas the setter is defined for the concrete property type.
Method candidate = ClassUtils.getMethodIfAvailable(
this.beanClass, "set" + StringUtils.capitalize(getName()), (Class<?>[]) null);
if (candidate != null && candidate.getParameterCount() == 1) {
writeMethodToUse = candidate;
}
}
this.readMethod = readMethodToUse;
this.writeMethod = writeMethodToUse;
if (this.writeMethod != null) {
if (this.readMethod == null) {
// Write method not matched against read method: potentially ambiguous through
// several overloaded variants, in which case an arbitrary winner has been chosen
// by the JDK's JavaBeans Introspector...
Set<Method> ambiguousCandidates = new HashSet<>();
for (Method method : beanClass.getMethods()) {
if (method.getName().equals(writeMethodToUse.getName()) &&
!method.equals(writeMethodToUse) && !method.isBridge() &&
method.getParameterCount() == writeMethodToUse.getParameterCount()) {
ambiguousCandidates.add(method);
}
}
if (!ambiguousCandidates.isEmpty()) {
this.ambiguousWriteMethods = ambiguousCandidates;
}
}
this.writeMethodParameter = new MethodParameter(this.writeMethod, 0);
GenericTypeResolver.resolveParameterType(this.writeMethodParameter, this.beanClass);
}
if (this.readMethod != null) {
this.propertyType = GenericTypeResolver.resolveReturnType(this.readMethod, this.beanClass);
}
else if (this.writeMethodParameter != null) {
this.propertyType = this.writeMethodParameter.getParameterType();
}
this.propertyEditorClass = propertyEditorClass;
}- 调用父类的构造器,初始化当前的PD的Name,初始化readMethod和writeMethod,先置为null
- 判断readMethod是否为null,不为null的话,调用BridgeMethodResolver来解析实际的readMethod
- 判断writeMethod是否为null,不为null的话,调用BridgeMethodResolver来解析实际的writeMethod
- 如果解析出来发现有readMethod,但是没有writeMethod,有可能是set方法使用了实际的类型,调用ClassUtils.getMethodIfAvailable方法获取可能的备用writeMethod
- 将上面解析出的readMethod和WriteMethod赋值给当前的PropertyDescriptor实例
- 根据readMethod的返回值类型或者writeMethod的参数判断propertyType并赋值
评论