自定义缓存工具框架common-cache使用demo

本项目目标是针对cn.dyaoming.common:common-cache包进行使用展示。

项目地址:https://github.com/dym270307872/demo-parent/tree/master/demo-common-cache

1、首先准备一个springmvc的项目作为基础框架。

由于项目目的比较简单,所有我选用的项目没有连接数据库的逻辑,使用了随机数逻辑模拟数据操作。随机数每次都不一样,等同于数据库数据变化。

随机数有一个参数num是随机数的最大值[或者理解成随机范围],相当于数据查询的参数。

2、引入jar包

<dependency>
	<groupId>cn.dyaoming.common</groupId>
	<artifactId>common-cache</artifactId>
	<version>0.0.1</version>
</dependency>

3、编写CacheInterface实现类RedisTemplateImp。

package cn.dyaoming.demo.dao;


import java.util.*;

import cn.dyaoming.cache.interfaces.CacheInterface;
import cn.dyaoming.errors.AppDaoException;
import cn.dyaoming.utils.AesUtil;
import cn.dyaoming.utils.SerializeUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;


// @Component("cacheDao") TODO为了便于理解,此处单独配置到spring文件中,使用自动注解效果一致
/**
 * <p>
 * 使用redis的实现类
 * </p>
 * 
 * @author DYAOMING
 * @since 2019-05-15
 * @version V1.0
 */
public class RedisTemplateImp implements CacheInterface {

	private static final Logger	LOGGER	= LogManager.getLogger(RedisTemplateImp.class);

	@Autowired
	private RedisTemplate		redisTemplate;



	public RedisTemplate getRedisTemplate() {
		return redisTemplate;
	}



	public void setRedisTemplate(RedisTemplate redisTemplate) {
		this.redisTemplate = redisTemplate;
	}



	/**
	 * 功能描述:判断是否存在键值。
	 * 
	 * @param key String类型 键
	 * @return boolean类型 返回结果
	 */
	@Override
	public boolean exists(Object key) throws AppDaoException {
		boolean rv = false;

		try {
			if (!StringUtils.isEmpty(key)) {
				final byte[] finalKey = key.toString().getBytes("utf-8");
				Object obj = redisTemplate.execute(new RedisCallback<Boolean>(){
					@Override
					public Boolean doInRedis(RedisConnection connection)
							throws DataAccessException {
						return connection.exists(finalKey);
					}
				});
				rv = (Boolean) obj;
			}

		} catch(Exception e) {
			LOGGER.error("异常:exists()方法出现异常,异常详细信息:" + e.getMessage() + "。");
			throw new AppDaoException("判断缓存内容是否存在异常!", e);
		}

		return rv;
	}



	/**
	 * 功能描述:设置缓存对象类型内容。
	 * 
	 * @param key String类型 键
	 * @param value Object类型 内容
	 * @return boolean类型 返回结果
	 */
	@Override
	public boolean setCacheObjectData(Object key, Object value) throws AppDaoException {
		return this.setCacheObjectData(key, value, DEFALUTTIME, DEFALUTSECRET);
	}



	/**
	 * 功能描述:设置缓存对象类型内容。
	 *
	 * @param key String类型 键
	 * @param value Object类型 内容
	 * @param validTime long类型 有效时间(单位:秒)
	 * @return boolean类型 返回结果
	 */
	@Override
	public boolean setCacheObjectData(Object key, Object value, final long validTime)
			throws AppDaoException {
		return this.setCacheObjectData(key, value, validTime, DEFALUTSECRET);
	}



	@Override
	public boolean setCacheObjectData(Object key, Object value, boolean secret)
			throws AppDaoException {
		return this.setCacheObjectData(key, value, DEFALUTTIME, secret);
	}



	@Override
	public boolean setCacheObjectData(Object key, Object value, long validTime,
			boolean secret) throws AppDaoException {
		boolean rv = false;
		try {
			if (!StringUtils.isEmpty(key)) {
				final byte[] finalKey = key.toString().getBytes("utf-8");
				byte[] valueByte = SerializeUtil.serialize(value);
				if (secret) {
					valueByte = AesUtil.encrypt(valueByte);
					int length_byte = DEFALUTHEAD.length + valueByte.length;
					byte[] all_byte = new byte[length_byte];

					System.arraycopy(DEFALUTHEAD, 0, all_byte, 0,
							DEFALUTHEAD.length);
					System.arraycopy(valueByte, 0, all_byte, DEFALUTHEAD.length,
							valueByte.length);
					valueByte = all_byte;
				}
				final byte[] finalValue = valueByte;
				redisTemplate.execute(new RedisCallback<Boolean>(){
					@Override
					public Boolean doInRedis(RedisConnection connection) {
						connection.set(finalKey, finalValue);
						// 设置超时间
						if (validTime > 0L) {
							connection.expire(finalKey, validTime);
						}
						return true;
					}
				});
			}
		} catch(Exception e) {
			LOGGER.error("异常:setCacheObjectData()方法出现异常,异常详细信息:" + e.getMessage() + "。");
			throw new AppDaoException("缓存对象类型内容出现异常!", e);
		}

		return rv;
	}



	/**
	 * 功能描述:删除缓存内容。
	 * 
	 * @param key String类型 键
	 * @return boolean类型 返回结果
	 */
	@Override
	public boolean deleteCacheData(Object key) throws AppDaoException {
		boolean rv = false;

		try {
			if (!StringUtils.isEmpty(key)) {
				final byte[] finalKey = key.toString().getBytes("utf-8");
				redisTemplate.execute(new RedisCallback<Long>(){
					@Override
					public Long doInRedis(RedisConnection connection)
							throws DataAccessException {
						return connection.del(finalKey);
					}
				});

				rv = true;
			}
		} catch(Exception e) {
			LOGGER.error("异常:deleteCacheData()方法出现异常,异常详细信息:" + e.getMessage() + "。");
			throw new AppDaoException("删除缓存内容出现异常!", e);
		}

		return rv;
	}



	/**
	 * 功能描述:获取缓存内容。
	 * 
	 * @param key String类型 键
	 * @return Object类型 返回结果
	 */
	@Override
	public Object getCacheData(Object key) throws AppDaoException {
		Object rv = null;

		try {

			if (!StringUtils.isEmpty(key)) {
				final byte[] finalKey = key.toString().getBytes("utf-8");
				final Object object = redisTemplate.execute(new RedisCallback<Object>(){
					@Override
					public Object doInRedis(RedisConnection connection) throws DataAccessException {
						byte[] value = connection.get(finalKey);
						if (value == null){
							return null; 
						}
						byte[] head = new byte[DEFALUTHEAD.length];
						System.arraycopy(value, 0, head, 0, DEFALUTHEAD.length);
						if (Arrays.equals(head, DEFALUTHEAD)) {
							byte[] body = new byte[value.length - DEFALUTHEAD.length];
							System.arraycopy(value, DEFALUTHEAD.length, body, 0,
									value.length - DEFALUTHEAD.length);
							body = AesUtil.decrypt(body);
							return SerializeUtil.unSerialize(body);
						}

						return SerializeUtil.unSerialize(value);
					}
				});
				rv = object;
			}
		} catch(Exception e) {
			LOGGER.error("异常:getCacheData()方法出现异常,异常详细信息:" + e.getMessage() + "。");
			throw new AppDaoException("获取缓存内容出现异常!", e);
		}

		return rv;
	}



	/**
	 * 功能描述:获取缓存内容。
	 * 
	 * @param key String类型 键
	 * @param type Class<T>类型 内容类型
	 * @return T类型 返回结果
	 */
	@Override
	public <T> T getCacheTData(String key, Class<T> type) throws AppDaoException {
		if (StringUtils.isEmpty(key) || null == type) {
			return null;
		} else {
			final String finalKey;
			final Class<T> finalType = type;
			if (key instanceof String) {
				finalKey = key;
			} else {
				finalKey = key.toString();
			}
			final Object object = redisTemplate.execute(new RedisCallback<Object>(){
				@Override
				public Object doInRedis(RedisConnection connection) throws DataAccessException {
					byte[] key = (finalKey).getBytes();
					byte[] value = connection.get(key);
					if (value == null){
						return null; 
						}
					return SerializeUtil.unSerialize(value);
				}
			});
			if (finalType != null && finalType.isInstance(object) && null != object) {
				return (T) object;
			} else {
				return null;
			}
		}
	}



	/**
	 * 描述:清空缓存
	 * 
	 */
	@Override
	public void clear() throws AppDaoException {
		redisTemplate.discard();
	}

}

4、增加配置文件

增加配置文件redis-config.properties

#redis\u670D\u52A1\u5730\u5740
redis.host=127.0.0.1
#redis\u7AEF\u53E3\u53F7
redis.port=6379

#\u5176\u4ED6\u9644\u5C5E\u53C2\u6570
redis.pool.maxTotal=2048
redis.pool.maxActive=2048
redis.pool.maxIdle=256
redis.pool.minIdle=128
redis.pool.maxWait=10000
redis.pool.timeout=30000
redis.pool.maxWaitMillis=10000
redis.pool.testOnBorrow=true
redis.pool.testOnReturn=true
redis.pool.timeBetweenEvictionRunsMillis=30000
redis.pool.testWhileIdle=true
redis.pool.numTestsPerEvictionRun=50

spring-cache.xml

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:cache="http://www.springframework.org/schema/cache" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
       http://www.springframework.org/schema/cache 
       http://www.springframework.org/schema/cache/spring-cache-4.2.xsd">
    
    <cache:annotation-driven key-generator="cacheKeyGenerator"/>

	<bean id="cacheKeyGenerator" class="cn.dyaoming.cache.CacheKeyGenerator"/>

    <!-- 缓存管理器 -->
    <bean id="cacheManager" class="cn.dyaoming.cache.CacheManager">
        <property name="timeout" value="60" />
        <property name="caches">
            <set>                
                <bean class="cn.dyaoming.cache.SystemCache">
                    <property name="name" value="publicInfo" />
                    <property name="timeout" value="6000" />
                    <property name="cacheDao" ref="cacheDao" />
                </bean>
                <bean class="cn.dyaoming.cache.SystemCache">
                    <property name="name" value="userInfo" />
                    <property name="timeout" value="6000" />
                    <property name="secret" value="true" />
                    <property name="cacheDao" ref="cacheDao" />
                </bean>
                <bean class="cn.dyaoming.cache.SystemCache">
                    <property name="name" value="businessInfo" />
                    <property name="timeout" value="6000" />
                    <property name="secret" value="true" />
                    <property name="cacheDao" ref="cacheDao" />
                </bean>
            </set>
        </property>
    </bean>
   
	
</beans>


此配置中,publicinfo、userinfo、businessinfo是自定义命名空间,可以随意添加或删除。timeout是当前命名空间缓存时长。timeout输入0,表示不缓存,-1表示永久缓存。

secret是加密标识,如果为true,则系统缓存时自动进行加密。

cacheDao是实现类,可以参考下面的配置文件。

spring-redis.xml

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans" 
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:jee="http://www.springframework.org/schema/jee"  
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:c="http://www.springframework.org/schema/c"
    xmlns:p="http://www.springframework.org/schema/p" 
    xmlns:util="http://www.springframework.org/schema/util" 
    xmlns:cache="http://www.springframework.org/schema/cache" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/jee 
       http://www.springframework.org/schema/jee/spring-jee.xsd  
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd
       http://www.springframework.org/schema/util  
       http://www.springframework.org/schema/util/spring-util.xsd
       http://www.springframework.org/schema/cache 
       http://www.springframework.org/schema/cache/spring-cache.xsd">


	<!-- redis Spring 配置(YCL)-->

    <!-- jedis 连接池配置 -->
    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <!-- 最大活动对象数   -->    
        <property name="maxTotal" value="${redis.pool.maxTotal}"/>
        <!-- 最大能够保持idel状态的对象数  -->
        <property name="maxIdle" value="${redis.pool.maxIdle}" />  
        <!-- 最小能够保持idel状态的对象数  -->
        <property name="minIdle" value="${redis.pool.minIdle}"/>  
        <!-- 当池内没有返回对象时,最大等待时间    -->
        <property name="maxWaitMillis" value="${redis.pool.maxWaitMillis}"/>  
        <!-- 当调用borrow Object方法时,是否进行有效性检查   -->
        <property name="testOnBorrow" value="${redis.pool.testOnBorrow}" />  
        <!-- 当调用return Object方法时,是否进行有效性检查    -->
        <property name="testOnReturn" value="${redis.pool.testOnReturn}" />  
        <!-- 向调用者输出“链接”对象时,是否检测它的空闲超时;   -->
        <property name="testWhileIdle" value="${redis.pool.testWhileIdle}" />  
        <!-- 对于“空闲链接”检测线程而言,每次检测的链接资源的个数。默认为3.  -->
        <property name="numTestsPerEvictionRun" value="${redis.pool.numTestsPerEvictionRun}" />  
        <!-- 空闲链接”检测线程,检测的周期,毫秒数。如果为负值,表示不运行“检测线程”。默认为-1.   -->
        <property name="timeBetweenEvictionRunsMillis" value="${redis.pool.timeBetweenEvictionRunsMillis}" />   
    </bean>

    <!-- redis 连接工厂 -->
    <bean id="redisConnectionFactory"
        class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="poolConfig" ref="poolConfig" />
        <property name="port" value="${redis.port}" />
        <property name="hostName" value="${redis.host}" />
        <!-- <property name="password" value="${redis.password}" /> -->
        <property name="timeout" value="${redis.pool.timeout}"></property>
    </bean>
    
    <!-- redis 操作模版  -->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="redisConnectionFactory" />
        <property name="keySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
        </property>
        <property name="valueSerializer">
            <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
        </property>        
        <property name="hashKeySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"></bean>
        </property>  
        <property name="hashValueSerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"></bean>
        </property> 
    </bean>
    
    
    
	<!-- spring RedisBaseDcoImp 配置 -->
	<bean id="cacheDao" class="cn.dyaoming.demo.dao.RedisTemplateImp">
		<property name="redisTemplate">
			<ref bean="redisTemplate" />
		</property>
	</bean>
    
    
</beans>


至此,项目结构完整。

5、如果需要使用临时缓存命名空间的话,需要增加上扫描配置

<!-- 自动扫描且只扫描@Component-->
	<context:component-scan
		base-package="cn.dyaoming" use-default-filters="false">
		<context:include-filter type="annotation"
			expression="org.springframework.stereotype.Component" />
	</context:component-scan>

用于扫描common-util包里边的springUtil工具类,实现spring的静态控制。

6、修改代码,启动项目。

在业务层方法上加上缓存配置。

/**
	 * <p>默认缓存业务层</p>
	 * @param num 随机范围
	 * @return 返还值
	 */
	@Cacheable("default")
	public String defaultService(int num) {
		return "默认缓存方法返回随机值:" + new Random().nextInt(num);
	}

括号里是命名空间的名称,可以输入配置的命名空间,也可以随便填写。

不在配置中的命名空间视为临时命名空间,采用配置是default的配置,记缓存时长和加密标识。

其他例子:

/**
	 * <p>用户缓存业务层</p>
	 * @param num 随机范围
	 * @return 返还值
	 */
	@Cacheable("userInfo")
	public String userService(int num) {
		return "用户缓存方法返回随机值:" + new Random().nextInt(num);
	}

如此,开发项目时可以快速完成,增加一行注解即可,方便快捷,不需要每个业务都控制缓存时长,键值设置等逻辑。

 

7、测试访问

浏览器输入:http://localhost:8080/demo-common-cache/demo/business?num=101

返回结果:

再次刷新页面,返回结果不变。

浏览器输入:http://localhost:8080/demo-common-cache/demo/default?num=101

返回结果:

默认缓存方法返回随机值:75

查看redis保存的内容。

可以看到TTL时间小于设置的缓存时间,这是因为我截图操作慢了,实际上缓存时间跟设置时间一致。

返回的内容是明文,中文是编码后的值,通过字符处理,可以全部变成明文。

在测试下userinfo类型的缓存,由于设置是加密的,所有结果应该是加密的内容。

至此,演示项目完成,证明相关common-cache的确可以快速实现缓存。如果有特殊情况需要优化缓存时长或者关闭缓存时,只需要修改配置文件即可,不需要修改源代码。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值