在(一)中咱們配置好了 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()); }