@Cacheable、@CachePut、@CacheEvict
①Spring配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"> <!--Test_Spring--> <bean id="helloWorld" class="com.cqh.Test_Spring.HelloWorld"> <property name="name" value="Spring"/> </bean> <!--Cache_Spring--> <cache:annotation-driven /> <bean id="accountServiceBean" class="com.cqh.Cache_Spring.AccountService"/> <!-- generic cache manager --> <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager"> <property name="caches"> <set> <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default" /> <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="accountCache" /> </set> </property> </bean> </beans>
② 定义服务类
• package com.cqh.Cache_Spring; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; /** * Created by yl1794 on 2018/3/30. */ public class AccountService { // 当调用这个方法的时候,会从一个名叫accountCache的缓存中查询,如果有,返回缓存中的对象,不执行该方法了。 // 如果没有,则执行实际的方法(即查询数据库),并将执行的结果存入缓存中。 @Cacheable(value="accountCache") public Account getAccountByName(String userName) { // 方法内部实现不考虑缓存逻辑,直接实现业务 //TODO return getFromDB(userName); } // 基于SpEL表达式的condition定义,访问了参数 userName 对象的 length() 方法,条件表达式返回一个布尔值 // 当条件为 true,则进行缓存操作,否则直接调用方法执行的返回结果。 @Cacheable(value="accountCache", condition = "#userName.length()<=4") public Account getAccountByShortName(String userName) { // 方法内部实现不考虑缓存逻辑,直接实现业务 //TODO return getFromDB(userName); } // 可以利用 SpEL 表达式对缓存 key 进行设计 @Cacheable(value="accountCache",key="#userName.concat(#password)") public Account getAccountByNamePwd(String userName,String password,boolean sendLog) { // 方法内部实现不考虑缓存逻辑,直接实现业务 //TODO return getFromDB(userName,password); } // CachePut可以确保方法被执行,同时方法的返回值也被记录到缓存中,主要用于数据新增和修改方法。 @CachePut(value="accountCache",key="#account.getName()")// 更新 accountCache 缓存 public Account updateAccount2(Account account) { return updateDB2(account); } @CacheEvict(value="accountCache",key="#account.getName()")// 清空 accountCache 缓存 public void updateAccount(Account account) { updateDB(account); } @CacheEvict(value="accountCache",allEntries=true)// 清空 accountCache 缓存 public void reload() { } // 从数据库查询 private Account getFromDB(String acctName) { System.out.println("real querying db..."+acctName); Account account = new Account(); account.setName(acctName); return account; } // 从数据库查询 private Account getFromDB(String acctName, String password) { System.out.println("real querying db..."+acctName); Account account = new Account(); account.setName(acctName); account.setPassword(password); return account; } // 更新数据库 private void updateDB(Account account) { System.out.println("real update db..."+account.getName()); } private Account updateDB2(Account account) { System.out.println("real updating db..."+account.getName()); return account; } }
③定义Account实体
package com.cqh.Cache_Spring; /** * Created by yl1794 on 2018/3/30. */ public class Account { private int id; private String name; private String password; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
③ 测试类
package com.cqh.Cache_Spring; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Created by yl1794 on 2018/3/30. */ public class Test_cache_spring { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext( "spring_config.xml");// 加载 spring 配置文件 AccountService s = (AccountService) context.getBean("accountServiceBean"); System.out.println("开始测试:缓存"); // 第一次查询,应该走数据库 System.out.println("first query:"); s.getAccountByName("somebody"); // 第二次查询,应该不查数据库,直接返回缓存的值 System.out.println("second query:"); s.getAccountByName("somebody"); System.out.println(); System.out.println("开始测试:清空缓存"); //首先构造两个账号记录,然后记录到缓存中 Account account1 = s.getAccountByName("somebody1");//走数据库 Account account2 = s.getAccountByName("somebody2");//走数据库 // 开始更新其中一个 account1.setId(1212); s.updateAccount(account1); s.getAccountByName("somebody1");// 因为被更新了,所以走数据库 s.getAccountByName("somebody2");// 没有更新过,应该走缓存 s.getAccountByName("somebody1");// 再次查询,应该走缓存 s.reload(); // 更新所有缓存 s.getAccountByName("somebody1");// 应该会查询数据库 s.getAccountByName("somebody2");// 应该会查询数据库 s.getAccountByName("somebody1");// 应该走缓存 s.getAccountByName("somebody2");// 应该走缓存 System.out.println(); System.out.println("开始测试:按照条件操作缓存"); s.getAccountByShortName("somebody3");// 长度大于 4,不会被缓存 s.getAccountByShortName("sbd"); // 长度小于 4,会被缓存 s.getAccountByShortName("somebody3");// 还是查询数据库 s.getAccountByShortName("sbd"); // 会从缓存返回 System.out.println(); System.out.println("开始测试:如果有多个参数,如何进行 key 的组合"); s.getAccountByNamePwd("somebody", "123456", true);// 应该查询数据库 s.getAccountByNamePwd("somebody", "123456", true);// 应该走缓存 s.getAccountByNamePwd("somebody", "123456", false);// 应该走缓存 s.getAccountByNamePwd("somebody", "654321", true);// 应该查询数据库 s.getAccountByNamePwd("somebody", "654321", true);// 应该走缓存 System.out.println(); System.out.println("开始测试:既要保证方法被调用,又希望结果被缓存"); Account account = s.getAccountByName("someone"); // 应该查询数据库 account.setPassword("123"); s.updateAccount2(account); // 更新数据库 account.setPassword("321"); s.updateAccount2(account); // 更新数据库 account = s.getAccountByName("someone"); // 应该走缓存 System.out.println(account.getPassword()); } }