NOTE

2.49 原理分析

缓存管理和缓存类是怎么生成的 1. spring-boot-starter预先导入的自动配置类 - @CacheAutoConfiguration cache自动配置的类在于CacheAutoConfiguration,他导入了一个CacheConfigurationImportSelector 我

Java创建于 更新于 historical

这是历史学习笔记,可能存在过时或不完整的理解。

缓存管理和缓存类是怎么生成的

1. spring-boot-starter预先导入的自动配置类

  • @CacheAutoConfiguration cache自动配置的类在于CacheAutoConfiguration,他导入了一个CacheConfigurationImportSelector
@Configuration
@ConditionalOnClass(CacheManager.class)
@ConditionalOnBean(CacheAspectSupport.class)
@ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver")
@EnableConfigurationProperties(CacheProperties.class)
@AutoConfigureBefore(HibernateJpaAutoConfiguration.class)
@AutoConfigureAfter({ CouchbaseAutoConfiguration.class, HazelcastAutoConfiguration.class,
		RedisAutoConfiguration.class })
@Import(CacheConfigurationImportSelector.class)//导入了一些组件
public class CacheAutoConfiguration {

}

我们知道spring boot启动的时候需要refresh容器,其中有一个步骤会调用所有BeanFactoryPostProcessor的postProcessBeanDefinitionRegistry方法,他会获取所有的ImportSelector,调用其selectImport方法,因此我们主要看CacheConfigurationImportSelector的selectImport方法

1.1. 默认导入SimpleCacheConfiguration缓存配置类

  • CacheConfigurationImportSelector
static class CacheConfigurationImportSelector implements ImportSelector {

	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		//获取所有预先定义的cache组件的枚举值
		CacheType[] types = CacheType.values();
		String[] imports = new String[types.length];
		//遍历枚举,从CacheConfigurations定义的【cache组件名:cache配置类】获取相应的配置类
		for (int i = 0; i < types.length; i++) {
			imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
		}
		//所有的cache配置类如下
		//0 = "org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration"
		//1 = "org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration"
		//2 = "org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration"
		//3 = "org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration"
		//4 = "org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration"
		//5 = "org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration"
		//6 = "org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration"
		//7 = "org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration"
		//8 = "org.springframework.boot.autoconfigure.cache.GuavaCacheConfiguration"
		//9 = "org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration"//配置文件中开启debug=true发现默认是这个
		//10 = "org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration"
		return imports;
	}

}

1.2. 向容器中导入ConcurrentMapCacheManager缓存管理器

我们以org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration为例

@Configuration
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class SimpleCacheConfiguration {

	private final CacheProperties cacheProperties;

	private final CacheManagerCustomizers customizerInvoker;

	SimpleCacheConfiguration(CacheProperties cacheProperties,
			CacheManagerCustomizers customizerInvoker) {
		this.cacheProperties = cacheProperties;
		this.customizerInvoker = customizerInvoker;
	}
	//作用就是给ioc容器注入了ConcurrentMapCacheManager
	@Bean
	public ConcurrentMapCacheManager cacheManager() {
		ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
		List<String> cacheNames = this.cacheProperties.getCacheNames();
		if (!cacheNames.isEmpty()) {
			cacheManager.setCacheNames(cacheNames);
		}
		return this.customizerInvoker.customize(cacheManager);
	}

}

1.2.1. 通过缓存管理器获取缓存类

是个Map,key为cacheName,value为Cache【其实也是一个Map】

public class ConcurrentMapCacheManager implements CacheManager, BeanClassLoaderAware {

	//把Cache类保存进ConcurrentHashMap,真正的缓存数据实在Cache类里
	private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<String, Cache>(16);

	public ConcurrentMapCacheManager() {
	}

	@Override
	public Cache getCache(String name) {
		//从ConcurrentMap获取cache
		Cache cache = this.cacheMap.get(name);
		//如果为空,那么创建新的Cache并放入ConcurrentMap
		if (cache == null && this.dynamic) {
			//这里加了锁
			synchronized (this.cacheMap) {
				//重复检测??
				cache = this.cacheMap.get(name);
				if (cache == null) {
					//创建cache实例
					cache = createConcurrentMapCache(name);
					this.cacheMap.put(name, cache);
				}
			}
		}
		return cache;
	}

	protected Cache createConcurrentMapCache(String name) {
		SerializationDelegate actualSerialization = (isStoreByValue() ? this.serialization : null);
		//使用的是ConcurrentMapCache
		return new ConcurrentMapCache(name, new ConcurrentHashMap<Object, Object>(256),
				isAllowNullValues(), actualSerialization);

	}

}

1.2.2. 缓存类的操作

public class ConcurrentMapCache extends AbstractValueAdaptingCache {

	private final String name;

	//依旧使用的ConcurrentMap作为缓存数据的容器
	private final ConcurrentMap<Object, Object> store;

	private final SerializationDelegate serialization;

	protected ConcurrentMapCache(String name, ConcurrentMap<Object, Object> store,
			boolean allowNullValues, SerializationDelegate serialization) {

		super(allowNullValues);
		Assert.notNull(name, "Name must not be null");
		Assert.notNull(store, "Store must not be null");
		this.name = name;
		this.store = store;
		this.serialization = serialization;
	}

	//cache的操作基本就是map的操作

	@Override
	public void put(Object key, Object value) {
		this.store.put(key, toStoreValue(value));
	}
	@Override
	public void evict(Object key) {
		this.store.remove(key);
	}

	@Override
	public void clear() {
		this.store.clear();
	}	

	@Override
	protected Object lookup(Object key) {
		return this.store.get(key);
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> T get(Object key, Callable<T> valueLoader) {
		// Try efficient lookup on the ConcurrentHashMap first...
		ValueWrapper storeValue = get(key);
		if (storeValue != null) {
			return (T) storeValue.get();
		}

		// No value found -> load value within full synchronization.
		synchronized (this.store) {
			storeValue = get(key);
			if (storeValue != null) {
				return (T) storeValue.get();
			}

			T value;
			try {
				value = valueLoader.call();
			}
			catch (Throwable ex) {
				throw new ValueRetrievalException(key, valueLoader, ex);
			}
			put(key, value);
			return value;
		}
	}	
}

2. 代理类的生成

2.1. 开启缓存代理类配置

通过这个注解我们会导入一个CachingConfigurationSelector

  • @EnableCaching
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)//导入了CachingConfigurationSelector
public @interface EnableCaching
{
	boolean proxyTargetClass() default false;
	AdviceMode mode() default AdviceMode.PROXY;
	int order() default Ordered.LOWEST_PRECEDENCE;
}

CachingConfigurationSelector继承了AdviceModeImportSelector,我们想看AdviceModeImportSelector的selectImport方法

2.1.1. 向容器导入AutoProxyRegistrar和ProxyCachingConfiguration

  • AdviceModeImportSelector.selectImports
@Override
public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
	Class<?> annType = GenericTypeResolver.resolveTypeArgument(getClass(), AdviceModeImportSelector.class);
	AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
	if (attributes == null) {
		throw new IllegalArgumentException(String.format(
				"@%s is not present on importing class '%s' as expected",
				annType.getSimpleName(), importingClassMetadata.getClassName()));
	}

	//获取代理模式 默认是AdviceMode.PROXY
	AdviceMode adviceMode = attributes.getEnum(getAdviceModeAttributeName());
	//调用CachingConfigurationSelector的selectImport方法
	String[] imports = selectImports(adviceMode);
	if (imports == null) {
		throw new IllegalArgumentException("Unknown AdviceMode: " + adviceMode);
	}
	return imports;
}

public String[] selectImports(AdviceMode adviceMode) {
	switch (adviceMode) {
		case PROXY:
			//走这个
			return getProxyImports();
		case ASPECTJ:
			return getAspectJImports();
		default:
			return null;
	}
}

private String[] getProxyImports() {
	List<String> result = new ArrayList<String>(3);
	//导入了AutoProxyRegistrar和ProxyCachingConfiguration
	result.add(AutoProxyRegistrar.class.getName());
	result.add(ProxyCachingConfiguration.class.getName());
	if (jsr107Present && jcacheImplPresent) {
		result.add(PROXY_JCACHE_CONFIGURATION_CLASS);
	}
	return StringUtils.toStringArray(result);
}

CachingConfigurationSelector主要的作用是导入AutoProxyRegistrar和ProxyCachingConfiguration,我们只需要研究完AutoProxyRegistrar这个类就能明白代理类怎么生成的

2.1.2. AutoProxyRegistrar向容器注入了用于生成代理对象的InfrastructureAdvisorAutoProxyCreator

public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {//是一个ImportBeanDefinitionRegistrar ,关注其registerBeanDefinitions方法
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		boolean candidateFound = false;
		Set<String> annoTypes = importingClassMetadata.getAnnotationTypes();
		for (String annoType : annoTypes) {
			AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);
			if (candidate == null) {
				continue;
			}
			Object mode = candidate.get("mode");
			Object proxyTargetClass = candidate.get("proxyTargetClass");
			if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
					Boolean.class == proxyTargetClass.getClass()) {
				candidateFound = true;
				if (mode == AdviceMode.PROXY) {
					//这段代码先容器中注入了InfrastructureAdvisorAutoProxyCreator
					AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
					if ((Boolean) proxyTargetClass) {
						AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
						return;
					}
				}
			}
		}
		if (!candidateFound && logger.isWarnEnabled()) {
			String name = getClass().getSimpleName();
			logger.warn(String.format("%s was imported but no annotations were found " +
					"having both 'mode' and 'proxyTargetClass' attributes of type " +
					"AdviceMode and boolean respectively. This means that auto proxy " +
					"creator registration and configuration may not have occurred as " +
					"intended, and components may not be proxied as expected. Check to " +
					"ensure that %s has been @Import'ed on the same class where these " +
					"annotations are declared; otherwise remove the import of %s " +
					"altogether.", name, name, name));
		}
	}

}

InfrastructureAdvisorAutoProxyCreator这个类继承了AbstractAdvisorAutoProxyCreator,后者继承了AbstractAutoProxyCreator,原理同1.给容器中注入AspectJAnnotationAutoProxyCreator组件.md 关键就在于AbstractAutoProxyCreator的postProcessAfterInitialization方法,会在TestService的bean实例化->赋值->调用自定义初始化方法的时候调用,接着生成代理类返回

2.1.3. 代理对象拦截被代理方法的调用,进入缓存操作

代理类会调用CacheInterceptor这个拦截器,进入到ConcurrentMapCache类的操作

3. 总结

  • spring启动的时候refresh context有个步骤invokeBeanFactoryPostProcessors,会调用所有BeanFactoryPostProcessor的postProcessBeanDefinitionRegistry方法
  • 其中有个ConfigurationClassPostProcessor,他的postProcessBeanDefinitionRegistry方法会解析所有**@Configuration类的@Import标签,调用ImportSelector的selectImport方法以及ImportBeanDefinitionRegistrar的registerBeanDefinitions方法**
  • CacheAutoConfiguration的CacheConfigurationImportSelector就是其中一个ImportSelector,他的selectImport方法作用是导入SimpleCacheConfiguration配置类,进而生成ConcurrentMapCacheManager这个缓存管理类
  • EnableCaching的CachingConfigurationSelector也是其中一个,他的selectImport方法作用是导入AutoProxyRegistrar
  • spring启动的时候refresh context有个步骤registerBeanPostProcessors,会向容器注册所有BeanPostProcessor,AutoProxyRegistrar就是其中一个
  • spring启动的时候refresh context有个步骤finishBeanFactoryInitialization,生成TestService的bean的时候,调用自定义初始化方法时会调用AutoProxyRegistrar的postProcessAfterBeanInitialization方法,这个方法会为TestService返回一个代理对象
  • 当TestController调用TestService的方法的时候先经过代理类,代理类会调用CacheInterceptor这个拦截器,最终进入到ConcurrentMapCache类的操作
  • 最后的ConcurrentMapCache类的操作如下 调用方法之前会用@Cacheable的cacheNames去ConcurrentMapCacheManager的getCache方法获取相应的Cache类,没有的话则创建 然后调用ConcurrentMapCache的lookup方法,key是在__CacheAspectSupport__的generateKey生成的
  • SimpleKeyGenerator
public static Object generateKey(Object... params) {
	//长度为0,new SimpleKey();
	if (params.length == 0) {
		return SimpleKey.EMPTY;
	}
	//长度为1,直接返回params
	if (params.length == 1) {
		Object param = params[0];
		if (param != null && !param.getClass().isArray()) {
			return param;
		}
	}
	//组合所有params
	return new SimpleKey(params);
}

发现没有的话会调用目标方法,并把结果放进ConcurrentMapCache的put方法,把null存进去

EnableXX的都是向容器导入BeanPostProcessor, XXXAutoconfiguration都是先容器导入@Configuration,进而@Bean