您的位置:首页 > 数据库 > Redis

采用redis作为独立缓存服务器(二)_spring Cache应用

2018-02-04 19:05 721 查看
请注意:该博文的环境架构和我前面的博文【http://blog.csdn.net/wx5040257/article/details/79226742】相同,不再详述!

前面提到过这个问题:如果我们的商品信息访问量特别大,那我们就把它们放到缓存里面去,如果我们更新了一个商品信息,肯定不希望把缓存里面的商品信息都给删了,而是希望在缓存中替换该商品信息。

再比方说:有一个缓存存放 list<User>,现在你执行了一个 update(user)的方法,你一定不希望清除整个缓存而想在缓存中替换掉update的元素。

要达到这个要求,要么你就自己写缓存架构,要么可以使用spring cache。当然没有人想自己写一个缓存程序,那样太难了,于是你肯定喜欢spring cache!!!

一  spring cache知识点

1.  启用Cache注解

在spring核心配置文件中加入

<cache:annotation-driven cache-manager="cacheManager" proxy-target-class="true"/>
另外还可以指定一个 key-generator,即默认的key生成策略 。

然后在需要用到缓存的类中加入如下注解

@EnableCaching

使用@EnableCaching启用Cache注解支持

2. @CachePut

@CachePut用法

应用到写数据的方法上,如新增/修改方法,调用方法时会自动把相应的数据放入缓存:

@CachePut(value = "user", key = "#user.id")
public User save(User user) {
users.add(user);
return user;
}
即调用该方法时,会把user.id作为key,返回值作为value放入缓存;

 

@CachePut源代码:

public @interface CachePut {
String[] value();              //缓存的名字,可以把数据写到多个缓存
String key() default "";       //缓存key,如果不指定将使用默认的KeyGenerator生成,后边介绍
String condition() default ""; //满足缓存条件的数据才会放入缓存,condition在调用方法之前和之后都会判断
String unless() default "";    //用于否决缓存更新的,不像condition,该表达只在方法执行之后判断,此时可以拿到返回值result进行判断了
}


3.  @CacheEvict
即应用到移除数据的方法上,如删除方法,调用方法时会从缓存中移除相应的数据:

@CacheEvict(value = "user", key = "#user.id") //移除指定key的数据
public User delete(User user) {
users.remove(user);
return user;
}
@CacheEvict(value = "user", allEntries = true) //移除所有数据
public void deleteAll() {
users.clear();
}
@CacheEvict源代码:
public @interface CacheEvict {
String[] value();                        //请参考@CachePut
String key() default "";                 //请参考@CachePut
String condition() default "";           //请参考@CachePut
boolean allEntries() default false;      //是否移除所有数据
boolean beforeInvocation() default false;//是调用方法之前移除/还是调用之后移除


4.  @Cacheable
应用到读取数据的方法上,即可缓存的方法,如查找方法:先从缓存中读取,如果没有再调用方法获取数据,然后把数据添加到缓存中:

@Cacheable(value = "user", key = "#id")
public User findById(final Long id) {
System.out.println("cache miss, invoke find by id, id:" + id);
for (User user : users) {
if (user.getId().equals(id)) {
return user;
}
}
return null;
}
@Cacheable源代码:

public @interface Cacheable {
String[] value();             //请参考@CachePut
String key() default "";      //请参考@CachePut
String condition() default "";//请参考@CachePut
String unless() default "";   //请参考@CachePut


5. Key生成器

如果在Cache注解上没有指定key的话@CachePut(value = "user"),会使用KeyGenerator进行生成一个key: 

public interface KeyGenerator {
Object generate(Object target, Method method, Object... params);
}
默认提供了DefaultKeyGenerator生成器(Spring 4之后使用SimpleKeyGenerator):
@Override
public Object generate(Object target, Method method, Object... params) {
if (params.length == 0) {
return SimpleKey.EMPTY;
}
if (params.length == 1 && params[0] != null) {
return params[0];
}
return new SimpleKey(params);
}
即如果只有一个参数,就使用参数作为key,否则使用SimpleKey作为key。

 

我们也可以自定义自己的key生成器,然后通过xml风格的<cache:annotation-driven key-generator=""/>或注解风格的CachingConfigurer中指定keyGenerator。 

6. 条件缓存

@Cacheable(value = "user", key = "#id", condition = "#id lt 10")  

public User conditionFindById(final Long id)  

 

根据运行流程,如下@CachePut将在执行完方法后(#result就能拿到返回值了)判断condition,如果返回true,则放入缓存; 

@CachePut(value = "user", key = "#id", condition = "#result.username ne 'zhang'")  

public User conditionSave(final User user)   

  

根据运行流程,如下@CachePut将在执行完方法后(#result就能拿到返回值了)判断unless,如果返回false,则放入缓存;(即跟condition相反)

@CachePut(value = "user", key = "#user.id", unless = "#result.username eq 'zhang'")  

    public User conditionSave2(final User user)   

 

根据运行流程,如下@CacheEvict, beforeInvocation=false表示在方法执行之后调用(#result能拿到返回值了);且判断condition,如果返回true,则移除缓存;

@CacheEvict(value = "user", key = "#user.id", beforeInvocation = false, condition = "#result.username ne 'zhang'")  

public User conditionDelete(final User user)    

7. @Caching
有时候我们可能组合多个Cache注解使用;比如用户新增成功后,我们要添加id-->user;username--->user;email--->user的缓存;此时就需要@Caching组合多个注解标签了。

 

如用户新增成功后,添加id-->user;username--->user;email--->user到缓存;

@Caching(
put = {
@CachePut(value = "user", key = "#user.id"),
@CachePut(value = "user", key = "#user.username"),
@CachePut(value = "user", key = "#user.email")
}
)
public User save(User user) { }
@Caching定义如下:

public @interface Caching {
Cacheable[] cacheable() default {}; //声明多个@Cacheable
CachePut[] put() default {};        //声明多个@CachePut
CacheEvict[] evict() default {};    //声明多个@CacheEvict
}


8  SpEL上下文数据
Spring Cache提供了一些供我们使用的SpEL上下文数据,下表直接摘自Spring官方文档:

名字位置示例描述
methodName                             root对象                    #root.methodName                               当前被调用的方法名
methodroot对象#root.method.name当前被调用的方法
targetroot对象#root.target当前被调用的目标对象
targetClassroot对象#root.targetClass当前被调用的目标对象类
argsroot对象#root.args[0]当前被调用的方法的参数列表
cachesroot对象#root.caches[0].name                                            当前方法调用使用的缓存列表(如@Cacheable(value={"cache1", "cache2"})),则有两个cache
argument name执行上下文                 #user.id当前被调用的方法的参数,如findById(Long id),我们可以通过#id拿到参数
result执行上下文#result方法执行后的返回值(仅当方法执行之后的判断有效,如‘unless’,'cache evict'的beforeInvocation=false)
二  具体应用

这里我们以对用户表的增删改查为例来说明spring cache的具体应用。

关键代码

=======================redis.properties==============================

#*****************jedis连接参数设置*********************
#redis服务器ip
redis.ip=169.254.130.122
#redis服务器端口号
redis.port=6379
#redis访问密码
redis.passWord=test123
#与服务器建立连接的超时时间
redis.timeout=3000
#************************jedis池参数设置*******************
#jedis的最大活跃连接数
jedis.pool.maxActive=50
#jedis最大空闲连接数
jedis.pool.maxIdle=10
#jedis最小空闲连接数
jedis.pool.minIdle=5
#jedis池没有连接对象返回时,等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。
#如果超过等待时间,则直接抛出JedisConnectionException
jedis.pool.maxWait=1500
#从池中获取连接的时候,是否进行有效检查
jedis.pool.testOnBorrow=true
#归还连接的时候,是否进行有效检查
jedis.pool.testOnReturn=false


=========================CacheSeriUtil=======================
package com.wx.utils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

//序列化及反序列化
public class CacheSeriUtil {
public static byte[] toByteArray(Object obj) {
byte[] bytes = null;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
oos.flush();
bytes = bos.toByteArray();
oos.close();
bos.close();
} catch (IOException ex) {
ex.printStackTrace();
}
return bytes;
}

public static Object toObject(byte[] bytes) {
Object obj = null;
try {
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis);
obj = ois.readObject();
ois.close();
bis.close();
} catch (IOException ex) {
ex.printStackTrace();
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
}
return obj;
}
}


springCache实现类

====================SpringRedisCache========================
package com.wx.utils;

import java.util.*;
import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;

public class SpringRedisCache implements Cache {

private JedisConnectionFactory jedisConnectionFactory;
private String name;

public void setJedisConnectionFactory(
JedisConnectionFactory jedisConnectionFactory) {
this.jedisConnectionFactory = jedisConnectionFactory;
}

public void setName(String name) {
this.name = name;
}

public String getName() {
return this.name;
}

public Object getNativeCache() {
return null;
}

public ValueWrapper get(Object key) {
System.out.println("get key from cache************:"+key.toString());
final String keyf = key.toString();
RedisConnection conn=null;
Object object = null;
try {
conn=jedisConnectionFactory.getConnection();
byte[] bkey = keyf.getBytes();
byte[] value = conn.get(bkey);
if (value == null) {
System.out.println("缓存中木有,要查数据库了");
object= null;
}else{
object=CacheSeriUtil.toObject(value);
System.out.println("恭喜,从缓存中找到了:"+object.toString());
}
} catch (Exception e) {
object=null;
e.printStackTrace();
}finally{
if(conn!=null){
conn.close();
}
}
return (object!=null) ? new SimpleValueWrapper(object) : null;
}

/**
* 添加操作,如果是save方法,则添加该对象的缓存的同时,把它添加到其它list缓存中去
* @param key 形式"[className]@[methodName]@[tableName].id"
*/
public void put(Object key, Object value) {
String srcKey = key.toString();
final long liveTime = 86400; //默认缓存一天
RedisConnection conn=null;
try {
conn=jedisConnectionFactory.getConnection();
if(srcKey.indexOf('@')==-1){ //非add方法的键值对添加
byte[] keyb = srcKey.getBytes();
byte[] valueb = CacheSeriUtil.toByteArray(value);
conn.set(keyb, valueb);
if (liveTime > 0) {
conn.expire(keyb, liveTime);
}
System.out.println("***键:"+srcKey+"*********添加进了缓存");
}else{//add方法的键值对添加
String objStr=value.toString();
String targetName=srcKey.substring(0, srcKey.indexOf('@'));
String methName=srcKey.substring(srcKey.indexOf('@')+1);
String theId=objStr.substring(0,objStr.indexOf('@'));//table.primarykey的形式
System.out.println(targetName+"=="+methName+"=="+theId);
String keyf = targetName+"."+theId;
System.out.println("****put key:" + keyf + "===value:" + value);
byte[] keyb = keyf.getBytes();
byte[] valueb = CacheSeriUtil.toByteArray(value);
conn.set(keyb, valueb);
if (liveTime > 0) {
conn.expire(keyb, liveTime);
}
//往其它list缓存里添加数据
//其它list缓存中删除该对象
String toAdd=targetName+".list.*";
Set<byte[]> keys= conn.keys(toAdd.getBytes());
Iterator<byte[]> it=keys.iterator() ;
while(it.hasNext()){
byte[] onelistKey = it.next();
byte[] oneListValue=conn.get(onelistKey);
List<Object> theList=(List<Object>)CacheSeriUtil.toObject(oneListValue);
theList.add(value);
//重新放回缓存中
oneListValue=CacheSeriUtil.toByteArray(theList);
conn.set(onelistKey, oneListValue);
if (liveTime > 0) {
conn.expire(onelistKey, liveTime);
}
System.out.println("***put后***"+new String(onelistKey)+"****list缓存中的数据更新了*********");
}
}
} catch (Exception e1) {
e1.printStackTrace();
}finally{
if(conn!=null){
conn.close();
}
}

}

/**
* 删除操作,如果只是根据id删除一个对象,则把其它缓存里面的该对象也删除
* 如果是删除很多对象(例如是根据性别删),则把该类下所有缓存全部清除
* @param key 形式"[className]@[methodName]@[tableName].id"
*/
public void evict(Object key) {
String srcKey = key.toString();
String methName=srcKey.substring(srcKey.indexOf('@')+1,srcKey.lastIndexOf('@'));
RedisConnection conn=null;
String targetName=srcKey.substring(0, srcKey.indexOf('@'));
String theId=srcKey.substring(srcKey.lastIndexOf('@')+1);//user.userId的形式
final long liveTime = 86400;    //默认缓存一天
String toDel="";
try {
conn=jedisConnectionFactory.getConnection();
//如果只是删除了一个对象或者更新了一个对象,则删除所有list缓存中的该对象
if(methName.equals("deleteById")||methName.startsWith("modify")||methName.startsWith("update")){
toDel=targetName+"."+theId;
conn.del(toDel.getBytes());
System.out.println("*********del_key:"+toDel+"==============");
//其它list缓存中删除该对象
toDel=targetName+".list.*";
Set<byte[]> keys= conn.keys(toDel.getBytes());
Iterator<byte[]> it=keys.iterator() ;
while(it.hasNext()){
byte[] onelistKey = it.next();
byte[] oneListValue=conn.get(onelistKey);
List<Object> theList=(List<Object>)CacheSeriUtil.toObject(oneListValue);
for(Object theObj : theList){
if(theObj.toString().startsWith(theId)){
theList.remove(theObj);
//找到了,list里面的对象被去掉了
break;
}
}
//重新放回缓存中
oneListValue=CacheSeriUtil.toByteArray(theList);
conn.set(onelistKey, oneListValue);
if (liveTime > 0) {
conn.expire(onelistKey, liveTime);
}
System.out.println("***del后***"+new String(onelistKey)+"****list缓存中的数据更新了*********");
}
}else{  //如果删除了很多对象,则目标类下所有的缓存全部删除
toDel=targetName+"*";
Set<byte[]> keys= conn.keys(toDel.getBytes());
Iterator<byte[]> it=keys.iterator() ;
while(it.hasNext()){
byte[] onekey = it.next();
conn.del(onekey);
System.out.println("***del key:"+new String(onekey));
}
}
} catch (Exception e) {
e.printStackTrace();
}finally{
if(conn!=null){
conn.close();
}
}
}

public void clear() {
System.out.println("clear key");
RedisConnection conn=null;
try {
conn=jedisConnectionFactory.getConnection();
conn.flushDb();
} catch (Exception e) {
e.printStackTrace();
}finally{
if(conn!=null){
conn.close();
}
}
}
}

实体类

==========================UserEntity===========================
package com.wx.entitys;

import java.io.Serializable;

public class UserEntity implements Serializable {
private static final long serialVersionUID = 1034088741557780953L;
private Integer userId;
private String userName;
private String passWord;
private String email;

public UserEntity() {
}
public UserEntity(String userName, String passWord) {
super();
this.userName = userName;
this.passWord = passWord;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassWord() {
return passWord;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
}

public void setEmail(String email) {
this.email = email;
}

public String getEmail() {
return email;
}

//缓存的删除逻辑中会依据[表名].[id]的形式查找对象,这里是自定的一个规范
public String toString() {
return "user."+this.userId+"@"+this.userName;
}
}


用到缓存逻辑的类
========================UserBiz=============================

package com.wx.biz;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.stereotype.Service;

import com.wx.dao.user.IUserDao;
import com.wx.entitys.UserEntity;

@Service("userBiz")
@EnableCaching
public class UserBiz {
@Autowired
private IUserDao userDao;

public boolean isLogin(UserEntity user){
return userDao.isLogin(user);
}

@CachePut(value="redis_cache",key="#root.targetClass.getName()+'@'+#root.methodName",unless="#result == null")
public UserEntity save(UserEntity user){
return userDao.add(user);
}

@Cacheable(value="redis_cache",key="#root.targetClass.getName()+'.user.'+#userId")
public UserEntity getById(String userId){
return userDao.getById(userId);
}

public boolean isUserExist(String userName){
return userDao.isUserExist(userName);
}

@Cacheable(value="redis_cache", key="#root.targetClass.getName()+'.list.'+#root.methodName")
public List<UserEntity> queryMany(UserEntity user){
return userDao.queryMany(user);
}

@CacheEvict(value="redis_cache",key="#root.targetClass.getName()+'@'+#root.methodName+'@user.'+#id")
public Integer deleteById(String id) {
return userDao.deleteById(id);
}

@Caching(
evict = {
@CacheEvict(value="redis_cache",key="#root.targetClass.getName()+'@'+#root.methodName+'@user.'+#user.userId")
},
put={
@CachePut(value="redis_cache",key="#root.targetClass.getName()+'@'+#root.methodName")
}
)
public UserEntity modify(UserEntity user) {
return userDao.modify(user);
}
}
spring核心配置文件
=============================applicationContext.xml========================

<?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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"> <!-- 扫描包含注解类所在的包 -->
<context:component-scan base-package="com.wx.dao.*"></context:component-scan>
<context:component-scan base-package="com.wx.biz"></context:component-scan>

<bean id="config" class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:configs/druidConfig.properties</value>
<value>classpath:configs/redis.properties</value>
</list>
</property>
</bean>
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="driverClassName" value="${driverClassName}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
<property name="filters" value="${filters}" />
<property name="initialSize" value="${initialSize}" />
<property name="maxActive" value="${maxActive}" />
<property name="minIdle" value="${minIdle}" />
<property name="maxWait" value="${maxWait}" />
<property name="validationQuery" value="${validationQuery}" />
<property name="testWhileIdle" value="${testWhileIdle}" />
<property name="testOnBorrow" value="${testOnBorrow}" />
<property name="testOnReturn" value="${testOnReturn}" />
<property name="maxPoolPreparedStatementPerConnectionSize" value="${maxPoolPreparedStatementPerConnectionSize}" />
<property name="removeAbandoned" value="${removeAbandoned}" />
<property name="removeAbandonedTimeout" value="${removeAbandonedTimeout}" />
<property name="timeBetweenEvictionRunsMillis" value="${timeBetweenEvictionRunsMillis}" />
<property name="minEvictableIdleTimeMillis" value="${minEvictableIdleTimeMillis}" />
</bean>
<bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="druidDataSource"></property>
<property name="configLocation">
<value>classpath:configs/mybatis-config.xml</value>
</property>
<property name="mapperLocations">
<list>
<value>classpath:configs/mappers/*.xml</value>
</list>
</property>
</bean>
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sessionFactory"></constructor-arg>
</bean>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="druidDataSource"></property>
</bean>
<tx:advice id="txAdvise" transaction-manager="txManager">
<tx:attributes>
<tx:method name="find*" read-only="true"/>
<tx:method name="search*" read-only="true"/>
<tx:method name="query*" read-only="true"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="do*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="del*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut expression="execution(* com.wx.biz.*.*(..))" id="myCut"/>
<aop:advisor advice-ref="txAdvise" pointcut-ref="myCut"/>
</aop:config>

<!-- 配置redis 单机版 -->
<!-- redis数据源 -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 最大空闲数 -->
<property name="maxIdle" value="${jedis.pool.maxIdle}" />
<!-- 最大活跃数 -->
<property name="maxTotal" value="${jedis.pool.maxActive}" />
<!-- 最大等待时间 -->
<property name="maxWaitMillis" value="${jedis.pool.maxWait}" />
<!-- 返回连接时,检测连接是否成功 -->
<property name="testOnBorrow" value="${jedis.pool.testOnBorrow}" />
</bean>

<!-- Spring-redis连接池管理工厂 -->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<!-- IP地址 -->
<property name="hostName" value="${redis.ip}" />
<!-- 端口号 -->
<property name="port" value="${redis.port}" />
<!-- 密码 -->
<property name="password" value="${redis.passWord}"/>
<!-- 超时时间 -->
<property name="timeout" value="${redis.timeout}" />
<property name="poolConfig" ref="poolConfig" />
</bean>
<!-- redis缓存配置结束 -->

<!-- spring自己的缓存管理器,这里定义了两个缓存位置名称 ,既注解中的value -->
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<!-- 集成第三方缓存配置 -->
<bean class="com.wx.utils.SpringRedisCache">
<property name="jedisConnectionFactory" ref="jedisConnectionFactory" />
<property name="name" value="redis_cache"/>
</bean>
</set>
</property>
</bean>
<!-- 开启Spring缓存 -->
<cache:annotation-driven cache-manager="cacheManager"/>

</beans>第一次访问用户管理界面:



会先从数据库查询,然后把查出来的结果添加进缓存



从redis desktop manager 上看也可以佐证



然后多次刷新当前页面,发现不会查询数据库了,而是从缓存里取数据,速度特别快



再来看添加数据,添加数据是从注册界面添加用户,然后回到查询界面,这里先要经过添加的数据库操作,然后再把添加的那个用户对象放到

缓存键【com.wx.biz.UserBiz.list.queryMany】中,回到查询界面就不需要从数据库查了,直接从缓存取数据



再来看修改,修改的话,要更新【com.wx.biz.UserBiz.list.queryMany】list里面的数据,而不是清空缓存。

这里采取的策略是先删除list里面的要修改的数据,然后把修改完了的数据放回list



看界面展示,修改成功了



最后来看删除

删除的逻辑是删除list里面的数据,也就是键【com.wx.biz.UserBiz.list.queryMany】中的数据,而不是清除整个缓存,然后回到用户管理首页



看界面,已经删除了



这样子,我们就实现了缓存的精确控制!!!

但是该代码比较难,所有的增删改都要单独写缓存注解,对开发人员要求高,一不小心就会出错!!!

其实我们可以采用切面编程来统一处理增删改的缓存策略!

请看下一篇博文!!!!

如果帮到了您,请点个赞吧!!!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: