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

请注意:该博文的环境架构和我前面的博文【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】中的数据,而不是清除整个缓存,然后回到用户管理首页


看界面,已经删除了


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

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

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

请看下一篇博文!!!!

如果帮到了您,请点个赞吧!!!!!
































  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

御前两把刀刀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值