private void clearByTypeCache() {
this.allBeanNamesByType.clear();
this.singletonBeanNamesByType.clear();
}
看看clearByTypeCache()方法的代码,做的事情很简单,就是清楚这两个map,那么这两个map到底是啥玩意呢?
/** Map of singleton and non-singleton bean names, keyed by dependency type.
*缓存了依赖项key是依赖项的类型 value是名字
* */
private final Map<Class<?>, String[]> allBeanNamesByType = new ConcurrentHashMap<>(64);
/** 仅依赖单例的bean名称的映射,由依赖项类型作为键 */
private final Map<Class<?>, String[]> singletonBeanNamesByType = new ConcurrentHashMap<>(64);
可以看到这两个都是ConcurrentHashMap,这个和逻辑分析没太大关系哈,allBeanNamesByType map,key:bean 的 type, value:bean的名字
singletonBeanNamesByType就是仅仅存单例的,而这两个map里面的对象是怎么put进去的呢,点着查一查
@Override
public String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
//这个判断只有第一个有意义!isConfigurationFrozen()
//判断是否冻结了bd,如果冻结了则返回的false,则不会进if里
//如果进了if就会调用里面,就表示要去查找
if (!isConfigurationFrozen() || type == null || !allowEagerInit) {
return doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, allowEagerInit);
}
//第一次找依赖bean是走缓存
//把allBeanNamesByType或者singletonBeanNamesByType赋给了cache
Map<Class<?>, String[]> cache =
(includeNonSingletons ? this.allBeanNamesByType : this.singletonBeanNamesByType);
String[] resolvedBeanNames = cache.get(type);
if (resolvedBeanNames != null) {
return resolvedBeanNames;
}
//如果缓存中没有则查找和前面if是一样的
resolvedBeanNames = doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, true);
//找到了则put进cache
if (ClassUtils.isCacheSafe(type, getBeanClassLoader())) {
//缓存
cache.put(type, resolvedBeanNames);
}
return resolvedBeanNames;
}
会看到是在这个方法里面对其进行了操作,将这两个map赋值给cache,再put对象进去。
可以得知这是一个cache,一个缓存,是要做缓存用的,那缓存的是什么呢?看名字哈getBeanNamesForType,根据类型得到beanname,这不是和上面这两个map有关系了,这两个map就是以key:type,value:beanname,这样存的呀。
仔细分析分析这个方法的代码。
if里面可以看到有三个判断,后面两个判断都是通过参数来的,都是默认为false,所以看第一个判断
这个判断其实又跟BeanFactoryPostProcessor的扩展点的API有关系
/**
* 冻结所有bean定义,表示已注册的bean定义将不会被进一步修改或后处理。
* <p>This allows the factory to aggressively cache bean definition metadata.
*/
void freezeConfiguration();
/**
* Return whether this factory's bean definitions are frozen,
i.e. are not supposed to be modified or post-processed any further.
* @return {@code true} if the factory's configuration is considered frozen
*/
boolean isConfigurationFrozen();
@Override
public void freezeConfiguration() {
this.configurationFrozen = true;
this.frozenBeanDefinitionNames = StringUtils.toStringArray(this.beanDefinitionNames);
}
@Override
public boolean isConfigurationFrozen() {
return this.configurationFrozen;
}
看看那个接口的方法,和实现类的重写方法。
表示的是冻结,和是否冻结。这个调用了,所以默认是为true,所以!isConfigurationFrozen()肯定是false,不会进入这个if里面。
会走下面,下面就是这两个map对cache的一个赋值,includeNonSingletons是这个方法的参数,看名字得知是包括不是单例的,点查看,发现基本上都是true,所以默认true吧,就把allBeanNamesByType赋值给cache,不包括单例的就是用另外一个map,里面存的都是单例的。
然后会调用这个缓存,通过type看能不能get到 beanname,如果get不到,那么还是得调用和if里面一样的方法doGetBeanNamesForType,这个方法里面就会从
另外的缓存里面查,那这就要经过更多的调用了,所以这里用cache作为缓存,节省时间。
其实getBeanNamesForType,也就是和咋们的getBean有很大关系,spring就是先通过这样的个方法,得到beanname,再调用doGetBean,通过beanname从单例池或者其他地方得到Bean。
而clearByTypeCache方法,就是将这两个key:type,value:BeanName的map,也就是缓存清除。
那么说到缓存,肯定会有个东西要考虑,也就是缓存的更新。
写linux的文章里面也有过那个TLB,页的虚拟地址到物理地址映射的个缓存,可以看到每次页表项映射的物理内存每次发生变化的时候,都会刷新TLB。
而这个缓存是什么时候更新的呢?
这就跟标题呼应了,也就是这个clearByTypeCache清除掉,然后再通过getBeanNamesForType代码里put进cache里的。