springboot 缓存@EnableCaching实例
目录
- springboot 缓存@EnableCaching
- Spring @EnableCaching的工作原理
springboot 缓存@EnableCaching
很多时候系统的瓶颈都在一些比较复杂的IO操作,例如读取数据库,如果一些比较稳定的数据,一般的解决方案就是用缓存。spring boot提供了比较简单的缓存方案。只要使用 @EnableCaching即可完成简单的缓存功能。
缓存的实现有多种实现,ConcurentHashMapCache , GuavaCache, EnCacheCache等多种实现,spring boot 有默认的实现。本文不深入源码解读,首先用起来。
此处我们模拟要缓存的User
class User { private Long id; private String name; // setter getter }
然后我们业务对象:
import javax.annotation.PostConstruct; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.EnableCaching; import org.springframework.stereotype.Component; /** * @author micro * @date 2017年8月2日 * @description : */ @Component @EnableCaching public class UserDao { private Map<Long, User> userMap; @PostConstruct public void init() { //模拟数据库 userMap = new HashMap<Long, User>(); userMap.put(1L, new User(1L,"micro1")); userMap.put(2L, new User(2L, "micro2")); } @Cacheable("user") // 注解key属性可以执行缓存对象user(可以理解为一个map)的key public User getUser(Long userId) { System.out.println("查询数据库:userId ->" + userId); return userMap.get(userId); } @Cacheable(value = "nameCache", key = "#name") public User getUserByName(Long userId, String name) { System.out.println("查询数据库:userId ->" + userId); return userMap.get(userId); } @Cacheable("nameCache") public User getUserByName(String name) { System.out.println("查询数据库:userName : " + name); for (Long k : userMap.keySet()) { if (userMap.get(k).equals(name)) { return userMap.get(k); } } return null; } @CachePut("user") // 与Cacheable区别就是Cacheable先看缓存如果有,直接缓存换回,CachePut则是每次都会调用并且把返回值放到缓存 public User getUser2(Long userId) { System.out.println("查询数据库:userId : " + userId); return userMap.get(userId); } @CacheEvict("user") public void removeFromCache(Long userId) { return ; } }
然后我们编写启动类:
@SpringBootApplication public class CacheTest implements CommandLineRunner { @Autowired private UserDao userDao; public static void main(String[] args) { new SpringApplication(CacheTest.class).run(args); } @Override public void run(String... args) throws Exception { System.out.println("第一次查询"); System.out.println(userDao.getUser(1L)); System.out.println("第二次查询"); System.out.println(userDao.getUser(1L)); userDao.removeFromCache(1L);// 移除缓存 System.out.println("第三次查询"); userDao.getUser(1L);// 没有缓存了 System.out.println("--------"); // 测试不同的key缓存 userDao.getUserByName("micro1"); userDao.getUserByName(1L, "micro1");// 指定了参数name 为key 此次读取缓存 } }
打印结果:
第一次查询
查询数据库:userId ->1
User@65da01f4
第二次查询
User@65da01f4
第三次查询
查询数据库:userId ->1
--------
查询数据库:userName : micro1
Spring @EnableCaching的工作原理
1、开发人员使用注解@EnableCaching
2、注解@EnableCaching导入CachingConfigurationSelector
3、CachingConfigurationSelector根据注解@EnableCaching 属性AdviceMode mode决定引入哪些配置类
PROXY
: AutoProxyRegistrar,ProxyCachingConfiguration;ASPECTJ
: AspectJCachingConfiguration;
本文以mode=PROXY为例;
4、CachingConfigurationSelector导入AutoProxyRegistrar会确保容器中存在一个自动代理创建器(APC);
- 用于确保目标bean需要被代理时有可用的代理创建器
5、ProxyCachingConfiguration向容器定义如下基础设施bean
- 名称为org.springframework.cache.config.internalCacheAdvisor类型为BeanFactoryCacheOperationSourceAdvisor的bean
- 名称为cacheOperationSource类型为CacheOperationSource的bean
用于获取方法调用时最终应用的Spring Cache注解的元数据
- 名称为cacheInterceptor类型为CacheInterceptor的bean
一个MethodInterceptor,包裹在目标bean外面用于操作Cache的AOP Advice。
6、AutoProxyRegistrar在容器启动阶段对每个bean创建进行处理,如果该bean中有方法应用了Spring Cache注解,为其创建相应的代理对象,包裹上面定义的BeanFactoryCacheOperationSourceAdvisor bean;
7、使用了Spring Cache注解的bean方法被调用,其实调用首先发生在代理对象上,先到达cacheInterceptor,然后才是目标bean方法的调用;
- cacheInterceptor既处理调用前缓存操作,也处理调用返回时缓存操作
以上为个人经验,希望能给大家一个参考,也希望大家多多支持自由互联。