关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » JAVA »Redis整合Spring结合使用缓存实例

    Redis整合Spring结合使用缓存实例

    2015-12-27 00:00:00 出处:陶邦仁
    分享

    微信扫一扫:分享

    Scan me!

    微信里点“发现”,扫一下

    二维码便可将本文分享至朋友圈。

    摘要:本文介绍了如何在Spring中配置redis,并通过Spring中AOP的思想,将缓存的方法切入到有需要进入缓存的类或方法前面。

    一、Redis介绍

    什么是Redis?

    redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set –有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

    它有什么特点?

    (1)Redis数据库完全在内存中,使用磁盘仅用于持久性。
    (2)相比许多键值数据存储,Redis拥有一套较为丰富的数据类型。
    (3)Redis可以将数据复制到任意数量的从服务器。

    Redis 优势?

    (1)异常快速:Redis的速度非常快,每秒能执行约11万集合,每秒约81000+条记录。
    (2)支持丰富的数据类型:Redis支持最大多数开发人员已经知道像列表,集合,有序集合,散列数据类型。这使得它非常容易解决各种各样的问题,因为我们知道哪些问题是可以处理通过它的数据类型更好。
    (3)操作都是原子性:所有Redis操作是原子的,这保证了如果两个客户端同时访问的Redis服务器将获得更新后的值。
    (4)多功能实用工具:Redis是一个多实用的工具,可以在多个用例如缓存,消息,队列使用(Redis原生支持发布/订阅),任何短暂的数据,应用程序,如Web应用程序会话,网页命中计数等。

    Redis 缺点?

    (1)单线程

    (2)耗内存

    二、使用实例

    本文使用maven+eclipse+sping

    1、引入jar包

          <!--Redis start -->
    		<dependency>
    			<groupId>org.springframework.data</groupId>
    			<artifactId>spring-data-redis</artifactId>
    			<version>1.6.1.RELEASE</version>
    		</dependency>
    		<dependency>
    			<groupId>redis.clients</groupId>
    			<artifactId>jedis</artifactId>
    			<version>2.7.3</version>
    		</dependency>
         <!--Redis end -->

    2、配置bean

    在application.xml加入如下配置

     <!-- jedis 配置 -->
        <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig" >
              <property name="maxIdle" value="${redis.maxIdle}" />
              <property name="maxWaitMillis" value="${redis.maxWait}" />
              <property name="testOnBorrow" value="${redis.testOnBorrow}" />
        </bean >
       <!-- redis服务器中心 -->
        <bean id="connectionFactory"  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.timeout}" ></property>
        </bean >
        <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" >
              <property name="connectionFactory" ref="connectionFactory" />
              <property name="keySerializer" >
                  <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
              </property>
              <property name="valueSerializer" >
                  <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
              </property>
        </bean >
    
         <!-- cache配置 -->
        <bean id="methodCacheInterceptor" class="com.mucfc.msm.common.MethodCacheInterceptor" >
              <property name="redisUtil" ref="redisUtil" />
        </bean >
        <bean id="redisUtil" class="com.mucfc.msm.common.RedisUtil" >
              <property name="redisTemplate" ref="redisTemplate" />
        </bean >

    其中配置文件redis一些配置数据redis.properties如下:

    #redis中心
    redis.host=10.75.202.11
    redis.port=6379
    redis.password=123456
    redis.maxIdle=100
    redis.maxActive=300
    redis.maxWait=1000
    redis.testOnBorrow=true
    redis.timeout=100000
    
    # 不需要加入缓存的类
    targetNames=xxxRecordManager,xxxSetRecordManager,xxxStatisticsIdentificationManager
    # 不需要缓存的方法
    methodNames=
    
    #设置缓存失效时间
    com.service.impl.xxxRecordManager= 60
    com.service.impl.xxxSetRecordManager= 60
    defaultCacheExpireTime=3600
    
    fep.local.cache.capacity =10000

    要扫这些properties文件,在application.xml加入如下配置

         <!-- 引入properties配置文件 -->  
         <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <list>
                   <value>classpath:properties/*.properties</value>
                    <!--要是有多个配置文件,只需在这里继续添加即可 -->
                </list>
            </property>
        </bean>

    3、一些工具类

    (1)RedisUtil

    上面的bean中,RedisUtil是用来缓存和去除数据的实例

    package com.mucfc.msm.common;
    
    import java.io.Serializable;
    import java.util.Set;
    import java.util.concurrent.TimeUnit;
    
    import org.apache.log4j.Logger;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.core.ValueOperations;
    
    /**
     * redis cache 工具类
     * 
     */
    public final class RedisUtil {
    	private Logger logger = Logger.getLogger(RedisUtil.class);
    	private RedisTemplate<Serializable, Object> redisTemplate;
    
    	/**
    	 * 批量删除对应的value
    	 * 
    	 * @param keys
    	 */
    	public void remove(final String... keys) {
    		for (String key : keys) {
    			remove(key);
    		}
    	}
    
    	/**
    	 * 批量删除key
    	 * 
    	 * @param pattern
    	 */
    	public void removePattern(final String pattern) {
    		Set<Serializable> keys = redisTemplate.keys(pattern);
    		if (keys.size() > 0)
    			redisTemplate.delete(keys);
    	}
    
    	/**
    	 * 删除对应的value
    	 * 
    	 * @param key
    	 */
    	public void remove(final String key) {
    		if (exists(key)) {
    			redisTemplate.delete(key);
    		}
    	}
    
    	/**
    	 * 判断缓存中是否有对应的value
    	 * 
    	 * @param key
    	 * @return
    	 */
    	public boolean exists(final String key) {
    		return redisTemplate.hasKey(key);
    	}
    
    	/**
    	 * 读取缓存
    	 * 
    	 * @param key
    	 * @return
    	 */
    	public Object get(final String key) {
    		Object result = null;
    		ValueOperations<Serializable, Object> operations = redisTemplate
    				.opsForValue();
    		result = operations.get(key);
    		return result;
    	}
    
    	/**
    	 * 写入缓存
    	 * 
    	 * @param key
    	 * @param value
    	 * @return
    	 */
    	public boolean set(final String key, Object value) {
    		boolean result = false;
    		try {
    			ValueOperations<Serializable, Object> operations = redisTemplate
    					.opsForValue();
    			operations.set(key, value);
    			result = true;
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		return result;
    	}
    
    	/**
    	 * 写入缓存
    	 * 
    	 * @param key
    	 * @param value
    	 * @return
    	 */
    	public boolean set(final String key, Object value, Long expireTime) {
    		boolean result = false;
    		try {
    			ValueOperations<Serializable, Object> operations = redisTemplate
    					.opsForValue();
    			operations.set(key, value);
    			redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
    			result = true;
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		return result;
    	}
    
    	public void setRedisTemplate(
    			RedisTemplate<Serializable, Object> redisTemplate) {
    		this.redisTemplate = redisTemplate;
    	}
    }

    (2)MethodCacheInterceptor

    切面MethodCacheInterceptor,这是用来给不同的方法来加入判断如果缓存存在数据,从缓存取数据。否则第一次从数据库取,并将结果保存到缓存 中去。

    package com.mucfc.msm.common;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.InputStream;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Properties;
    
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    import org.apache.log4j.Logger;
    
    public class MethodCacheInterceptor implements MethodInterceptor {
    	private Logger logger = Logger.getLogger(MethodCacheInterceptor.class);
    	private RedisUtil redisUtil;
    	private List<String> targetNamesList; // 不加入缓存的service名称
    	private List<String> methodNamesList; // 不加入缓存的方法名称
    	private Long defaultCacheExpireTime; // 缓存默认的过期时间
    	private Long xxxRecordManagerTime; //
    	private Long xxxSetRecordManagerTime; //
    
    	/**
    	 * 初始化读取不需要加入缓存的类名和方法名称
    	 */
    	public MethodCacheInterceptor() {
    		try {
    			 File f = new File("D:\lunaJee-workspace\msm\msm_core\src\main\java\com\mucfc\msm\common\cacheConf.properties"); 
    			 //配置文件位置直接被写死,有需要自己修改下
    		     InputStream in = new FileInputStream(f); 
    //			InputStream in = getClass().getClassLoader().getResourceAsStream(
    //					"D:\lunaJee-workspace\msm\msm_core\src\main\java\com\mucfc\msm\common\cacheConf.properties");
    			Properties p = new Properties();
    			p.load(in);
    			// 分割字符串
    			String[] targetNames = p.getProperty("targetNames").split(",");
    			String[] methodNames = p.getProperty("methodNames").split(",");
    
    			// 加载过期时间设置
    			defaultCacheExpireTime = Long.valueOf(p.getProperty("defaultCacheExpireTime"));
    			xxxRecordManagerTime = Long.valueOf(p.getProperty("com.service.impl.xxxRecordManager"));
    			xxxSetRecordManagerTime = Long.valueOf(p.getProperty("com.service.impl.xxxSetRecordManager"));
    			// 创建list
    			targetNamesList = new ArrayList<String>(targetNames.length);
    			methodNamesList = new ArrayList<String>(methodNames.length);
    			Integer maxLen = targetNames.length > methodNames.length ? targetNames.length
    					: methodNames.length;
    			// 将不需要缓存的类名和方法名添加到list中
    			for (int i = 0; i < maxLen; i++) {
    				if (i < targetNames.length) {
    					targetNamesList.add(targetNames[i]);
    				}
    				if (i < methodNames.length) {
    					methodNamesList.add(methodNames[i]);
    				}
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    
    	@Override
    	public Object invoke(MethodInvocation invocation) throws Throwable {
    		Object value = null;
    
    		String targetName = invocation.getThis().getClass().getName();
    		String methodName = invocation.getMethod().getName();
    		// 不需要缓存的内容
    		//if (!isAddCache(StringUtil.subStrForLastDot(targetName), methodName)) {
    		if (!isAddCache(targetName, methodName)) {
    			// 执行方法返回结果
    			return invocation.proceed();
    		}
    		Object[] arguments = invocation.getArguments();
    		String key = getCacheKey(targetName, methodName, arguments);
    		System.out.println(key);
    
    		try {
    			// 判断是否有缓存
    			if (redisUtil.exists(key)) {
    				return redisUtil.get(key);
    			}
    			// 写入缓存
    			value = invocation.proceed();
    			if (value != null) {
    				final String tkey = key;
    				final Object tvalue = value;
    				new Thread(new Runnable() {
    					@Override
    					public void run() {
    						if (tkey.startsWith("com.service.impl.xxxRecordManager")) {
    							redisUtil.set(tkey, tvalue, xxxRecordManagerTime);
    						} else if (tkey.startsWith("com.service.impl.xxxSetRecordManager")) {
    							redisUtil.set(tkey, tvalue, xxxSetRecordManagerTime);
    						} else {
    							redisUtil.set(tkey, tvalue, defaultCacheExpireTime);
    						}
    					}
    				}).start();
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    			if (value == null) {
    				return invocation.proceed();
    			}
    		}
    		return value;
    	}
    
    	/**
    	 * 是否加入缓存
    	 * 
    	 * @return
    	 */
    	private boolean isAddCache(String targetName, String methodName) {
    		boolean flag = true;
    		if (targetNamesList.contains(targetName)
    				|| methodNamesList.contains(methodName)) {
    			flag = false;
    		}
    		return flag;
    	}
    
    	/**
    	 * 创建缓存key
    	 *
    	 * @param targetName
    	 * @param methodName
    	 * @param arguments
    	 */
    	private String getCacheKey(String targetName, String methodName,
    			Object[] arguments) {
    		StringBuffer sbu = new StringBuffer();
    		sbu.append(targetName).append("_").append(methodName);
    		if ((arguments != null) && (arguments.length != 0)) {
    			for (int i = 0; i < arguments.length; i++) {
    				sbu.append("_").append(arguments[i]);
    			}
    		}
    		return sbu.toString();
    	}
    
    	public void setRedisUtil(RedisUtil redisUtil) {
    		this.redisUtil = redisUtil;
    	}
    }

    4、配置需要缓存的类或方法

    在application.xml加入如下配置,有多个类或方法可以配置多个

        <!-- 需要加入缓存的类或方法 -->
        <bean id="methodCachePointCut"  class="org.springframework.aop.support.RegexpMethodPointcutAdvisor" >
              <property name="advice" >
                  <ref local="methodCacheInterceptor" />
              </property>
              <property name="patterns" >
                  <list>
                   <!-- 确定正则表达式列表 -->
                     <value>com.mucfc.msm.service.impl...*ServiceImpl.*</value >
                  </list>
              </property>
        </bean >

    5、执行结果:

    写了一个简单的单元测试如下:

        @Test
        public void getSettUnitBySettUnitIdTest() {
            String systemId = "CES";
            String merchantId = "133";
            SettUnit configSettUnit = settUnitService.getSettUnitBySettUnitId(systemId, merchantId, "ESP");
            SettUnit configSettUnit1 = settUnitService.getSettUnitBySettUnitId(systemId, merchantId, "ESP");
            boolean flag= (configSettUnit == configSettUnit1);
            System.out.println(configSettUnit);
            logger.info("查找结果" + configSettUnit.getBusinessType());
    
          //  localSecondFIFOCache.put("configSettUnit", configSettUnit.getBusinessType());
         //  String string = localSecondFIFOCache.get("configSettUnit");
    //        logger.info("查找结果" + string);
        }

    这是第一次执行单元测试的过程:

    MethodCacheInterceptor这个类中打了断点,然后每次查询前都会先进入这个方法

    依次运行,发现没有缓存,所以会直接去查数据库

    打印了出来的SQL语句:

    第二次执行:

    因为第一次执行时,已经写入缓存了。所以第二次直接从缓存中取数据

    3、取两次的结果进行地址的对比:

    发现两个不是同一个对象,没错,是对的。如果是使用Ehcache的话,那么二者的内存地址会是一样的。那是因为redis和ehcache使用的缓存机制是不一样的。ehcache是基于本地电脑的内存使用缓存,所以使用缓存取数据时直接在本地电脑上取。转换成java对象就会是同一个内存地址,而redis它是在装有redis服务的电脑上(一般是另一台电脑),所以取数据时经过传输到本地,会对应到不同的内存地址,所以用==来比较会返回false。但是它确实是从缓存中去取的,这点我们从上面的断点可以看到。

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    抖音安全与信任开放日:揭秘推荐算法,告别单一标签依赖

    ultraedit编辑器打开文件时,总是提示是否转换为DOS格式,如何关闭?

    Cornell大神Kleinberg的经典教材《算法设计》是最好入门的算法教材

    从 Microsoft 下载中心安装 Windows 7 SP1 和 Windows Server 2008 R2 SP1 之前要执行的步骤

    Llama 2基于UCloud UK8S的创新应用

    火山引擎DataTester:如何使用A/B测试优化全域营销效果

    腾讯云、移动云继阿里云降价后宣布大幅度降价

    字节跳动数据平台论文被ICDE2023国际顶会收录,将通过火山引擎开放相关成果

    这个话题被围观超10000次,火山引擎VeDI如此解答

    误删库怎么办?火山引擎DataLeap“3招”守护数据安全

    IT头条

    平替CUDA!摩尔线程发布MUSA 4性能分析工具

    00:43

    三起案件揭开侵犯个人信息犯罪的黑灰产业链

    13:59

    百度三年开放2.1万实习岗,全力培育AI领域未来领袖

    00:36

    工信部:一季度,电信业务总量同比增长7.7%,业务收入累计完成4469亿元

    23:42

    Gartner:2024年全球半导体营收6559亿美元,AI助力英伟达首登榜首

    18:04

    技术热点

    iOS 8 中如何集成 Touch ID 功能

    windows7系统中鼠标滑轮键(中键)的快捷应用

    MySQL数据库的23个特别注意的安全事项

    Kruskal 最小生成树算法

    Ubuntu 14.10上安装新的字体图文教程

    Ubuntu14更新后无法进入系统卡在光标界面解怎么办?

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。