背景
和事务实现类似Spring的事务实现二
那么缓存的实现也是借助于aop
方式
注解
同样是EnableCaching 相当类似于事务开启注解
Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(CachingConfigurationSelector.class) public @interface EnableCaching { /** * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed * to standard Java interface-based proxies. The default is {@code false}. <strong> * Applicable only if {@link #mode()} is set to {@link AdviceMode#PROXY}</strong>. * * <p>Note that setting this attribute to {@code true} will affect <em>all</em> * Spring-managed beans requiring proxying, not just those marked with * {@code @Cacheable}. For example, other beans marked with Spring's * {@code @Transactional} annotation will be upgraded to subclass proxying at the same * time. This approach has no negative impact in practice unless one is explicitly * expecting one type of proxy vs another, e.g. in tests. */ boolean proxyTargetClass() default false; /** * Indicate how caching advice should be applied. The default is * {@link AdviceMode#PROXY}. * @see AdviceMode */ AdviceMode mode() default AdviceMode.PROXY; /** * Indicate the ordering of the execution of the caching advisor * when multiple advices are applied at a specific joinpoint. * The default is {@link Ordered#LOWEST_PRECEDENCE}. */ int order() default Ordered.LOWEST_PRECEDENCE; }
同样我们关注Import
public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> { /** * {@inheritDoc} * @return {@link ProxyCachingConfiguration} or {@code AspectJCacheConfiguration} for * {@code PROXY} and {@code ASPECTJ} values of {@link EnableCaching#mode()}, respectively */ public String[] selectImports(AdviceMode adviceMode) { switch (adviceMode) { case PROXY: return new String[] { AutoProxyRegistrar.class.getName(), ProxyCachingConfiguration.class.getName() }; case ASPECTJ: return new String[] { AnnotationConfigUtils.CACHE_ASPECT_CONFIGURATION_CLASS_NAME }; default: return null; } } }
@Configuration public class ProxyCachingConfiguration extends AbstractCachingConfiguration { @Bean(name=AnnotationConfigUtils.CACHE_ADVISOR_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public BeanFactoryCacheOperationSourceAdvisor cacheAdvisor() { BeanFactoryCacheOperationSourceAdvisor advisor = new BeanFactoryCacheOperationSourceAdvisor(); advisor.setCacheOperationSource(cacheOperationSource()); advisor.setAdvice(cacheInterceptor()); advisor.setOrder(this.enableCaching.<Integer>getNumber("order")); return advisor; } @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public CacheOperationSource cacheOperationSource() { return new AnnotationCacheOperationSource(); } @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public CacheInterceptor cacheInterceptor() { CacheInterceptor interceptor = new CacheInterceptor(); interceptor.setCacheOperationSources(cacheOperationSource()); if (this.cacheManager != null) { interceptor.setCacheManager(this.cacheManager); } if (this.keyGenerator != null) { interceptor.setKeyGenerator(this.keyGenerator); } return interceptor; } }
这样我们就可以获知advice为CacheInterceptor
advisor为BeanFactoryCacheOperationSourceAdvisor
public class BeanFactoryCacheOperationSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor { private CacheOperationSource cacheOperationSource; private final CacheOperationSourcePointcut pointcut = new CacheOperationSourcePointcut() { @Override protected CacheOperationSource getCacheOperationSource() { return cacheOperationSource; } }; /** * Set the cache operation attribute source which is used to find cache * attributes. This should usually be identical to the source reference * set on the cache interceptor itself. */ public void setCacheOperationSource(CacheOperationSource cacheOperationSource) { this.cacheOperationSource = cacheOperationSource; } /** * Set the {@link ClassFilter} to use for this pointcut. * Default is {@link ClassFilter#TRUE}. */ public void setClassFilter(ClassFilter classFilter) { this.pointcut.setClassFilter(classFilter); } public Pointcut getPointcut() { return this.pointcut; } }
pointCut为
abstract class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable { public boolean matches(Method method, Class<?> targetClass) { CacheOperationSource cas = getCacheOperationSource(); return (cas != null && !CollectionUtils.isEmpty(cas.getCacheOperations(method, targetClass))); } @Override public boolean equals(Object other) { if (this == other) { return true; } if (!(other instanceof CacheOperationSourcePointcut)) { return false; } CacheOperationSourcePointcut otherPc = (CacheOperationSourcePointcut) other; return ObjectUtils.nullSafeEquals(getCacheOperationSource(), otherPc.getCacheOperationSource()); } @Override public int hashCode() { return CacheOperationSourcePointcut.class.hashCode(); } @Override public String toString() { return getClass().getName() + ": " + getCacheOperationSource(); } /** * Obtain the underlying {@link CacheOperationSource} (may be {@code null}). * To be implemented by subclasses. */ protected abstract CacheOperationSource getCacheOperationSource(); }
整体看来代码十分相似【事务&缓存】出自一人之手
XML
当然还有XML配置
由于缓存我们是选择性的使用 而事务基本都是全部Service都覆盖
所以我们选择注解模式的缓存
<cache:annotation-driven cache-manager="cacheManager"/>
查看xml parse
@Override protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { builder.addPropertyReference("cacheManager", CacheNamespaceHandler.extractCacheManager(element)); CacheNamespaceHandler.parseKeyGenerator(element, builder.getBeanDefinition()); List<Element> cacheDefs = DomUtils.getChildElementsByTagName(element, DEFS_ELEMENT); if (cacheDefs.size() >= 1) { // Using attributes source. List<RootBeanDefinition> attributeSourceDefinitions = parseDefinitionsSources(cacheDefs, parserContext); builder.addPropertyValue("cacheOperationSources", attributeSourceDefinitions); } else { // Assume annotations source. builder.addPropertyValue("cacheOperationSources", new RootBeanDefinition( AnnotationCacheOperationSource.class)); } }
和事务不一样当元素大于一个可以返回多个 NameMatchCacheOperationSource 当没有配置 这边可以直接返回AnnotationCacheOperationSource
这边注意一下cacheOperationSources可以塞一个List<RootBeanDefinition>也可以塞一个RootBeanDefinition 当然是由于最终cacheOperationSources是一个可变参数
/** * Set one or more cache operation sources which are used to find the cache * attributes. If more than one source is provided, they will be aggregated using a * {@link CompositeCacheOperationSource}. * @param cacheOperationSources must not be {@code null} */ public void setCacheOperationSources(CacheOperationSource... cacheOperationSources) { Assert.notEmpty(cacheOperationSources); this.cacheOperationSource = (cacheOperationSources.length > 1 ? new CompositeCacheOperationSource(cacheOperationSources) : cacheOperationSources[0]); }
虽然最终也会组合成CompositeCacheOperationSource