处理缓存注解的步骤总结
Spring Cache是Spring框架的核心模块之一,不可谓不重要。用了好几篇文章专门来讲解使用、分析原理。下面按照正常的思路,我把Spring处理的步骤总结如下:
- CacheOperation封装了@CachePut、@Cacheable、@CacheEvict(下称三大缓存注解)的属性信息,以便于拦截的时候能直接操作此对象来执行逻辑。
- 1. 解析三大注解到CacheOperation的过程是由CacheAnnotationParser完成的
- CacheAnnotationSource代表缓存属性源,非常非常重要的一个概念。它提供接口方法来获取目标方法的CacheOperation集合。由上可知,这个具体工作是委托给CacheAnnotationParser去完成的
- BeanFactoryCacheOperationSourceAdvisor它代表增强器,至于需要增强哪些类呢???就是看有没有存在CacheOperation属性的方法
- CacheInterceptor实现了MethodInterceptor接口,在Spring AOP中实现对执行方法的拦截。在调用invoke执行目标方法前后,通过CacheAnnotationSource获取到方法所有的缓存操作属性,从而一个个的执行
- 执行的时候,每一个CacheOperation最后被封装成了CacheOperationContext,而CacheOperationContext最终通过CacheResolver解析出缓存对象Cache(可能是多个)
- 最后最后最后,CacheInterceptor调用其父类AbstractCacheInvoker执行对应的doPut / doGet / doEvict / doClear 等等。(可以处理执行异常)
CacheProxyFactoryBean:手动实现Cache功能
其实ProxyFactoryBean的设计模式在Spring AOP中已经非常不陌生了:【小家Spring】面向切面编程Spring AOP创建代理的方式:ProxyFactoryBean、ProxyFactory、AspectJProxyFactory(JDK Proxy和CGLIB)
如下截图,Spring内有非常多的xxxProxyFactoryBean的实现:
如果说把@EnableCaching称为自动模式的话,那使用CacheProxyFactoryBean就完全是手动档。话不多说,此处给个使用Demo就收场了:
@Configuration public class RootConfig { @Bean public CacheProxyFactoryBean cacheProxyFactoryBean() { CacheProxyFactoryBean proxyFactoryBean = new CacheProxyFactoryBean(); // 使用AnnotationCacheOperationSource来识别三大注解缓存 proxyFactoryBean.setCacheOperationSources(new AnnotationCacheOperationSource()); // 设置需要代理的目标类 CacheDemoService cacheDemoService = new CacheDemoServiceImpl(); proxyFactoryBean.setTarget(cacheDemoService); //proxyFactoryBean.setProxyInterfaces(); // 设置个性化的一些东西 CacheManager cacheManager = new ConcurrentMapCacheManager(); proxyFactoryBean.setCacheManager(cacheManager); //proxyFactoryBean.setKeyGenerator(); //proxyFactoryBean.setCacheResolver(); return proxyFactoryBean; } } //@Service // 因为使用了CacheProxyFactoryBean手动额皮质,此处请不要再被扫描进去,否则容器内就出现两个这样的Bean了 public class CacheDemoServiceImpl implements CacheDemoService { @Cacheable(cacheNames = "demoCache", key = "#id") @Override public Object getFromDB(Integer id) { System.out.println("模拟去db查询~~~" + id); return "hello cache..."; } }
测试:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {RootConfig.class, CacheConfig.class}) public class TestSpringBean { @Autowired private CacheDemoService cacheDemoService; @Test public void test1() { cacheDemoService.getFromDB(1); cacheDemoService.getFromDB(1); } }
打印结果:
模拟去db查询~~~1
只输出一套日志:缓存生效
此示例中
@EnableCaching
可不是打开状态哦,但我们依然能够使用手动档让缓存生效。使用手动档,我们可以很方便的使用
NameMatchCacheOperationSource
来根据方法名匹配~~~