@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());
}
}
⑤ 结果