在(一)中咱们配置好了 hibernate二级缓存 Hazelcast实现,可是当咱们使用spring cache相关注解(@CacheConfig,@Cacheable,@CachePut,@CacheEvict ,@Caching )的时候,并不能自动建立缓存配置,须要在hazelcast.xml文件中配置cache结点,若是使用的比较多的话就比较麻烦,并且大部分状况下,配置项都是同样的java
查看org.springframework.cache.jcache.JCacheCacheManager源码实现,发现针对没有发缓存,没有自动生成spring
@Override protected Cache getMissingCache(String name) { CacheManager cacheManager = getCacheManager(); Assert.state(cacheManager != null, "No CacheManager set"); // Check the JCache cache again (in case the cache was added at runtime) javax.cache.Cache<Object, Object> jcache = cacheManager.getCache(name); if (jcache != null) { return new JCacheCache(jcache, isAllowNullValues()); } return null; }
找到自动配置类源码org.springframework.boot.autoconfigure.cache.JCacheCacheConfigurationapache
@Bean @ConditionalOnMissingBean public CacheManager jCacheCacheManager() throws IOException { CacheManager jCacheCacheManager = createCacheManager(); List<String> cacheNames = this.cacheProperties.getCacheNames(); if (!CollectionUtils.isEmpty(cacheNames)) { for (String cacheName : cacheNames) { jCacheCacheManager.createCache(cacheName, getDefaultCacheConfiguration()); } } customize(jCacheCacheManager); return jCacheCacheManager; }
对于spring.cache.cache-names中配置的会自动生成,可是感受仍是有点多余缓存
在这一篇中,针对spring cache相关注解,对于没有配置的cache,也按默认配置项生成ide
思路是扫描org.springframework.cache.annotation.CacheConfig注解,将cache自动添加到spring.cache.cache-names配置项中,这样就会自动建立了this
一、建立包扫描路径注解hibernate
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.context.annotation.Import; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Import({CacheConfigScannerRegistrar.class}) public @interface CacheNameScan { /** * `@GrpcService` 所注解的包扫描路径 */ String[] packages() default {}; }
二、进行扫描添加到配置项中ssr
import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.cache.annotation.CacheConfig; import org.springframework.context.EnvironmentAware; import org.springframework.context.ResourceLoaderAware; import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.env.Environment; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.StandardEnvironment; import org.springframework.core.io.ResourceLoader; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.util.CollectionUtils; import lombok.extern.slf4j.Slf4j; @Slf4j public class CacheConfigScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware { private ResourceLoader resourceLoader; @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } private Environment environment; @Override public void setEnvironment(Environment environment) { this.environment = environment; } @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, false); scanner.setResourceLoader(this.resourceLoader); Set<String> cacheNameSet = new HashSet<>(); scanner.addIncludeFilter(new AnnotationTypeFilter(CacheConfig.class) { @Override protected boolean matchSelf(MetadataReader metadataReader) { AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); boolean isMatch = super.matchSelf(metadataReader); if (isMatch) { String[] cacheNames = (String[]) annotationMetadata .getAnnotationAttributes("org.springframework.cache.annotation.CacheConfig") .get("cacheNames"); if (cacheNames != null) { cacheNameSet.addAll(Arrays.asList(cacheNames)); } } return isMatch; } }); Set<BeanDefinition> beanDefinitions = scanPackages(importingClassMetadata, scanner); beanDefinitions.forEach(beanDefinition -> { log.info("配置CacheConfig缓存的class:{}", beanDefinition.getBeanClassName()); }); if (environment instanceof StandardEnvironment) { StandardEnvironment standardEnvironment = (StandardEnvironment) environment; cacheNameSet.addAll(Arrays .asList(StringUtils.split(standardEnvironment.getProperty("spring.cache.cache-names", ""), ","))); String configName = StringUtils.join(cacheNameSet, ","); Map<String, Object> map = new HashMap<>(); map.put("spring.cache.cache-names", configName); MapPropertySource propertySource = new MapPropertySource("cache.yml", map); standardEnvironment.getPropertySources().addFirst(propertySource); } } /** * 包扫描 */ private static Set<BeanDefinition> scanPackages(AnnotationMetadata importingClassMetadata, ClassPathBeanDefinitionScanner scanner) { List<String> packages = new ArrayList<>(); Map<String, Object> annotationAttributes = importingClassMetadata .getAnnotationAttributes(CacheNameScan.class.getCanonicalName()); if (annotationAttributes != null) { String[] basePackages = (String[]) annotationAttributes.get("packages"); if (basePackages.length > 0) { packages.addAll(Arrays.asList(basePackages)); } log.info("cache name 包扫描:{}", packages); } Set<BeanDefinition> beanDefinitions = new HashSet<>(); if (CollectionUtils.isEmpty(packages)) { return beanDefinitions; } packages.forEach(pack -> beanDefinitions.addAll(scanner.findCandidateComponents(pack))); return beanDefinitions; } }
这样就自动添加进去了,而不用做其余多余配置,使用的时候,使用@CacheConfig(cacheNames =“test”)和@Cacheable(key = "#groupName")配合code
另一种更直接的方式,直接修改SimpleCacheResolver的实现,并配置到org.springframework.cache.interceptor.CacheAspectSupport中xml
@Bean public HibernatePropertiesCustomizer hibernateSecondLevelCacheCustomizer(JCacheCacheManager jCacheCacheManager,CacheAspectSupport cacheAspectSupport) { cacheAspectSupport.setCacheResolver(new SimpleCacheResolver(jCacheCacheManager) { @Override public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) { Collection<String> cacheNames = getCacheNames(context); if (cacheNames == null) { return Collections.emptyList(); } Collection<Cache> result = new ArrayList<>(cacheNames.size()); for (String cacheName : cacheNames) { Cache cache = getCacheManager().getCache(cacheName); if (cache == null) { log.warn("未提早配置缓存:{},推荐使用org.springframework.cache.annotation.CacheConfig注解指定缓存", cacheName); jCacheCacheManager.getCacheManager().createCache(cacheName, defaultCacheConfiguration()); cache = getCacheManager().getCache(cacheName); } result.add(cache); } return result; } }); return (properties) -> properties.put(ConfigSettings.CACHE_MANAGER, jCacheCacheManager.getCacheManager()); }