经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » Redis » 查看文章
ssm+reids缓存整合
来源:cnblogs  作者:繁声物语  时间:2019/8/12 8:40:01  对本文有异议

在说正文之前我们先介绍一下redis:

  redis是当今比较热门的非关系型数据库之一,他使用的是key-value的键值对来进行存储,是一个存在于内存之中的数据库,我们一般用于做数据缓存。当我们需要大量的数据查询时,如果我们都直接访问数据库时,会严重影响数据库性能。所以我们一般的操作就是在db层之上的各级使用多级的no-sql来为db提供缓冲。

  因为redis是存在于内存之中,那么问题来了当我们断电时或者宕机时就会产生数据丢失,所以redis为我们提供了rdb和aof的两种持久化保存的方式,这也是为什么同样是缓存数据库我们选择redis而不选择memcache的原因。而且为了在大流量下提供稳定业务,redis还提供了redis-cluster,twemproxy,codis等集群化方案,为我们搭建分布式系统提供了可能。废话说了这么多下面开始正文。

一.添加redis对应的依赖

  1. <!-- reids缓存-->
  2. <!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-redis -->
  3. <dependency>
  4. <groupId>org.springframework.data</groupId>
  5. <artifactId>spring-data-redis</artifactId>
  6. <version>${redis.data.version}</version>
  7. </dependency>
  8.  
  9. <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
  10. <dependency>
  11. <groupId>redis.clients</groupId>
  12. <artifactId>jedis</artifactId>
  13. <version>${redis.clients.version}</version>
  14. </dependency>

依赖版本大家可以下用的最多的比较稳定。

二.添加相应的spring配置

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
  7. <!--开启aop代理-->
  8. <aop:aspectj-autoproxy expose-proxy="true"/>
  9. <!--spring添加注解扫描-->
  10. <context:annotation-config></context:annotation-config>
  11. <!--spring 注解扫描但是要排除spring mvc的控制器-->
  12. <context:component-scan base-package="com">
  13. <context:exclude-filter type="annotation"
  14. expression="org.springframework.stereotype.Controller"></context:exclude-filter>
  15. </context:component-scan>
  16.  
  17. <!--加载资源文件,该标签全文只能有一个-->
  18. <context:property-placeholder location="classpath:jdbc.properties,classpath:redisconfig.properties"/>
  19.  
  20. <!--配置数据源,阿里数据连接池-->
  21. <bean id="dataSource" class="${jdbc.dataType}" destroy-method="close">
  22. <property name="driverClassName" value="${jdbc.driver}"/>
  23. <property name="url" value="${jdbc.url}"/>
  24. <property name="username" value="${jdbc.usename}"/>
  25. <property name="password" value="${jdbc.password}"/>
  26.  
  27. <!-- 数据库连接池配置 -->
  28. <property name="initialSize" value="20"/><!-- 初始化连接数量 -->
  29. <property name="minIdle" value="5"/> <!-- 最小空闲连接数量 -->
  30. <property name="maxActive" value="1500"/> <!-- 最大连接数量 -->
  31. <property name="maxWait" value="60000"/> <!-- 最大建立连接等待时间(毫秒)。如果超过此时间将接到异常。设为-1表示无限制-->
  32. <property name="timeBetweenEvictionRunsMillis" value="60000"/> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
  33. <property name="minEvictableIdleTimeMillis" value="300000"/> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
  34. <property name="validationQuery" value="SELECT 'x'"/>
  35. <property name="testWhileIdle" value="true"/> <!--空闲时是否进行验证,检查对象是否有效 -->
  36. <property name="testOnBorrow" value="false"/> <!--取得对象时是否进行验证,检查对象是否有效 -->
  37. <property name="testOnReturn" value="false"/> <!--返回对象时是否进行验证 -->
  38. <!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
  39. <property name="poolPreparedStatements" value="true"/> <!-- 表明是否开启statement cache,默认为false,也就是不开启 -->
  40. <property name="maxPoolPreparedStatementPerConnectionSize"
  41. value="20"/> <!-- statement cache的大小,默认为-1,也就是不限制 -->
  42.  
  43. <!-- 配置监控统计拦截的filters,去掉后监控界面sql无法统计 -->
  44. <property name="filters" value="stat"/>
  45. </bean>
  46.  
  47.  
  48. <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  49. <property name="configLocation" value="classpath:mybatis-config.xml"/>
  50. <property name="dataSource" ref="dataSource"/>
  51. <!-- 扫描entity包 使用别名 -->
  52. <property name="typeAliasesPackage" value="com.lwc.pojo"/>
  53. <!-- 扫描sql配置文件:mapper需要的xml文件 -->
  54. <property name="mapperLocations" value="classpath*:com/lwc/dao/mapper/*.xml"/>
  55. </bean>
  56. <!--根据接口名生成对应的代理类-->
  57. <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  58. <property name="basePackage" value="com.lwc.dao"/>
  59. <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
  60. </bean>
  61.  
  62.  
  63. <!-- 配置事务管理器 -->
  64. <bean id="transactionManager"
  65. class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  66. <!-- 注入数据库连接池 -->
  67. <property name="dataSource" ref="dataSource"/>
  68. </bean>
  69.  
  70. <!--配置事务切面-->
  71. <aop:config>
  72. <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.lwc.service.*.*(..))"></aop:advisor>
  73. </aop:config>
  74. <!-- 2 事务详情(事务通知) , 在aop筛选基础上,比如对ABC三个确定使用什么样的事务。例如:AC读写、B只读 等
  75. <tx:attributes> 用于配置事务详情(属性属性)
  76. <tx:method name=""/> 详情具体配置
  77. propagation 传播行为 , REQUIRED:必须;REQUIRES_NEW:必须是新的
  78. isolation 隔离级别
  79. -->
  80. <tx:advice id="txAdvice" transaction-manager="transactionManager">
  81. <tx:attributes>
  82. <tx:method name="*" propagation="REQUIRED"/>
  83. </tx:attributes>
  84. </tx:advice>
  85. <!--配置redis缓存-->
  86. <!--redis配置-->
  87. <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
  88.  
  89. <!--控制一个pool最多有多少个状态为空闲的jedis实例-->
  90. <property name="maxIdle" value="${redis.maxIdle}"></property>
  91. <!--当borrow(引入)一个实例时,最大的等待时间,如果超过则抛出jedisConnectionException-->
  92. <property name="maxWaitMillis" value="${redis.maxWaitMillis}"></property>
  93. <property name="maxTotal" value="${redis.maxTotal}"></property>
  94. <!-- 在在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的-->
  95. <property name="testOnBorrow" value="${redis.testOnBorrow}"></property>
  96.  
  97. </bean>
  98.  
  99. <!--redis连接池-->
  100. <bean id="jedisPool" class="redis.clients.jedis.JedisPool">
  101. <constructor-arg index="0" ref="jedisPoolConfig"></constructor-arg>
  102. <constructor-arg index="1" value="${redis.host}"></constructor-arg>
  103. <constructor-arg index="2" value="${redis.port}"></constructor-arg>
  104. <constructor-arg index="3" value="${redis.timeout}"></constructor-arg>
  105.  
  106. </bean>
  107. <!-- redis连接工厂 -->
  108. <bean id="JedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
  109. <property name="hostName" value="${redis.host}" ></property>
  110. <property name="port" value="${redis.port}"></property>
  111. <property name="poolConfig" ref="jedisPoolConfig"></property>
  112. </bean>
  113. <!-- redis操作模板,这里采用尽量面向对象的模板 -->
  114. <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
  115. <property name="connectionFactory" ref="JedisConnectionFactory"/>
  116. <property name="keySerializer" ref="stringReadisSerializer"/>
  117. <property name="valueSerializer" ref="stringReadisSerializer"/>
  118. <property name="hashKeySerializer">
  119. <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
  120. </property>
  121. <property name="hashValueSerializer">
  122. <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
  123. </property>
  124. <!--开启事务-->
  125. <property name="enableTransactionSupport" value="true"/>
  126. </bean>
  127.  
  128.  
  129. <!--使用字符串进行序列化-->
  130. <bean id="stringReadisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
  131. <!--使用JDK的序列化器进行转化-->
  132. <bean id="jdkSerializationRedisSerializer" class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
  133.  
  134. <bean id="getCache" class="com.lwc.redis.aspectj.GetCachAop">
  135. <property name="redisTemplate" ref="redisTemplate"/>
  136. </bean>
  137. </beans>

这里是spring的基本配置,这里我没有分开来,反正都有注释

三.redis的一些基本资源文件

  1. redis.host=localhost
  2. redis.port=6379
  3. redis.maxIdle=50
  4. redis.maxTotal=100
  5. redis.maxWaitMillis=3000
  6. redis.testOnBorrow=true
  7. redis.timeout=5000

这里我没有配置密码,所以就没有写pass,但是如果有需要的话可以找到redis的配置文件加上如下语句

这样就可以了,如果没有配置密码而配置了<property name="password" value="${redis.password}" />将会报错

四.利用aop和自定义注解来进行实现环绕

  下面是自定义注解,并且配上了一些注解的解释,还没有学过自定注解的小伙伴可以看看

  1. package com.lwc.redis.annotation;
  2. import java.lang.annotation.*;
  3. /**自定义注解
  4. *   2.1.1 Target:表示注解的作用目标 
  5. *
  6. *                @Target(ElementType.TYPE)   //接口、类、枚举、注解
  7. *
  8. *     @Target(ElementType.FIELD) //字段、枚举的常量
  9. *
  10. *     @Target(ElementType.METHOD) //方法
  11. *
  12. *     @Target(ElementType.PARAMETER) //方法参数
  13. *
  14. *     @Target(ElementType.CONSTRUCTOR)  //构造函数
  15. *
  16. *     @Target(ElementType.LOCAL_VARIABLE)//局部变量
  17. *
  18. *     @Target(ElementType.ANNOTATION_TYPE)//注解
  19. *
  20. *     @Target(ElementType.PACKAGE) ///包
  21. *
  22. *   2.1.2 @Documented:说明该注解将被包含在javadoc中;
  23. *
  24. *   2.1.3 @Inherited:说明子类可以继承父类中的该注解 ;
  25. *
  26. *   2.1.4 @Retention:注解的保留位置;
  27. *
  28. *                @Retention(RetentionPolicy.SOURCE)   //注解仅存在于源码中,在class字节码文件中不包含
  29. *
  30. *        @Retention(RetentionPolicy.CLASS)     // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,
  31. *
  32. *     @Retention(RetentionPolicy.RUNTIME)  // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
  33. */
  34. @Target(ElementType.METHOD)//目标为方法
  35. @Retention(RetentionPolicy.RUNTIME)//注解在类中存在,运行时通过反射获取
  36. @Documented
  37. @Inherited
  38. public @interface GetCache {
  39. String name() default "";
  40. String value() default "";
  41. }

定义这个注解是为了让aop可以直接根据注解来进行切面环绕,而不需要根据传统的方法来进行切点,这样会方便很多,否则的话就需要

定义接口然后对接口需要的方法进行定义切点,在实现该接口,这样也可以做到切面环绕,但是会更麻烦点,有兴趣的小伙伴可以自己试试

 通知类:

  1. package com.lwc.redis.aspectj;
  2. import com.lwc.redis.annotation.GetCache;
  3. import com.lwc.util.RedisCache;
  4. import org.aspectj.lang.ProceedingJoinPoint;
  5. import org.aspectj.lang.annotation.Around;
  6. import org.aspectj.lang.annotation.Aspect;
  7. import org.aspectj.lang.annotation.Pointcut;
  8. import org.aspectj.lang.reflect.MethodSignature;
  9. import org.springframework.beans.factory.annotation.Autowired;
  10. import org.springframework.data.redis.core.RedisTemplate;
  11. import org.springframework.stereotype.Component;
  12. import java.io.Serializable;
  13. import java.lang.reflect.Method;
  14. @Component
  15. @Aspect
  16. public class GetCachAop {
  17. @Autowired
  18. private RedisTemplate<Serializable,Object> redisTemplate;
  19. private RedisCache redisCache=new RedisCache();
  20. //定义切点
  21. @Pointcut("@annotation(com.lwc.redis.annotation.GetCache)")
  22. public void getCache(){
  23. System.out.println("获取内存数据切入点");
  24. }
  25. /*在所有标注了GetCache的地方切入*/
  26. @Around("getCache()")
  27. public Object beforeExec(ProceedingJoinPoint joinPoint){
  28. //生成redis中的id,根据自己指定的格式
  29. String redisKey=getCacheKey(joinPoint);
  30. //获取从redis中查询得到的对象
  31. Object object=redisCache.getDataFromRedis(redisKey);
  32. //如果查询到了
  33. if(null!=object){
  34. System.out.println("从redis中获取到了数据");
  35. return object;
  36. }else{
  37. System.out.println("从数据库中查询数据");
  38. //如果没有查询到,则在数据库中进行查询
  39. try {
  40. object=joinPoint.proceed();
  41. } catch (Throwable throwable) {
  42. throwable.printStackTrace();
  43. }
  44. }
  45. //在目标方法执行完之后:将查到的数据放入到redis中
  46. redisCache.setDataToRedis(redisKey,object);
  47. return object;
  48. }
  49. /**
  50. * 根据方法名参数名类名获取唯一的一个键值
  51. * 格式为yourpackage.classname.methodname(int).argsvalue123
  52. * @param joinPoint
  53. * @return
  54. */
  55. //变量没有用到时不让警告
  56. @SuppressWarnings("unused")
  57. private String getCacheKey(ProceedingJoinPoint joinPoint){
  58. //获取切入方法的一些相关的信息
  59. MethodSignature ms=(MethodSignature) joinPoint.getSignature();
  60. Method method=ms.getMethod();
  61. //获取注解中设置的对应参数
  62. String ActionName=method.getAnnotation(GetCache.class).value();
  63. String fieldList=method.getAnnotation(GetCache.class).name();
  64. for(String field:fieldList.split("."))
  65. ActionName+="."+field;
  66. //获取切点方法的参数
  67. String id=null;
  68. Object[] args=joinPoint.getArgs();
  69. if(args!=null && args.length>0)
  70. id=String.valueOf(args[0]);
  71. ActionName+="="+id;
  72. String redisKey=ms+"."+ActionName;
  73. return redisKey;
  74. }
  75. public void setRedisTemplate( RedisTemplate<Serializable, Object> redisTemplate){
  76. this.redisTemplate = redisTemplate;
  77. }
  78. }

上面代码都有详细注释这里就不重复了

下面是序列化的工具类:

  1. package com.lwc.util;
  2. import java.io.*;
  3. public class SerializableUtil {
  4. //将字节数组反序列化为对象
  5. public static Object toObject(byte[] bytes){
  6. Object obj=null;
  7. try{
  8. ByteArrayInputStream bis=new ByteArrayInputStream(bytes);
  9. ObjectInputStream ois=new ObjectInputStream(bis);
  10. obj=ois.read();
  11. ois.close();
  12. bis.close();
  13. } catch (IOException e) {
  14. e.printStackTrace();
  15. }
  16. return obj;
  17. }
  18. //将对象序列化为字节数组
  19. public static byte[] toByteArray(Object obj){
  20. byte[] bytes =null;
  21. ByteArrayOutputStream bos =new ByteArrayOutputStream();
  22. try{
  23. ObjectOutputStream oos=new ObjectOutputStream(bos);
  24. oos.writeObject(obj);
  25. oos.flush();
  26. bytes=bos.toByteArray();
  27. oos.close();
  28. bos.close();
  29. } catch (IOException e) {
  30. e.printStackTrace();
  31. }
  32. return bytes;
  33. }
  34. }

下面是缓存工具类:

  1. package com.lwc.util;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.context.support.ClassPathXmlApplicationContext;
  4. import org.springframework.stereotype.Component;
  5. import redis.clients.jedis.Jedis;
  6. import redis.clients.jedis.JedisPool;
  7. /**
  8. * redis缓存工具类
  9. */
  10.  
  11. public class RedisCache {
  12. private static JedisPool jedisPool;
  13. //静态类进行参数的初始化
  14. static {
  15. ClassPathXmlApplicationContext cxac=new ClassPathXmlApplicationContext("applicationContext.xml");
  16. jedisPool=(JedisPool) cxac.getBean("jedisPool");
  17. }
  18. //从缓存中读取数据,进行反序列化
  19. public Object getDataFromRedis(String redisKey){
  20. Jedis jedis=jedisPool.getResource();
  21. byte[] result=jedis.get(redisKey.getBytes());
  22. //如果没有查询到,就返回空
  23. if(null==result)
  24. return null;
  25. return SerializableUtil.toObject(result);
  26. }
  27. //将数据库中查到的数据放入redis中
  28. public void setDataToRedis(String redisKey,Object obj){
  29. byte[] bytes =SerializableUtil.toByteArray(obj);
  30. Jedis jedis=jedisPool.getResource();
  31. String sucess=jedis.set(redisKey.getBytes(),bytes);
  32. if("OK".equals(sucess)){
  33. System.out.println("数据保存成功");
  34. }
  35. }
  36. }

当然我也看到有人继承cach类来使用redis缓存,下面贴出别人的代码并进行解释:

  1. package com.ssm.redis;
  2. import java.util.concurrent.locks.ReadWriteLock;
  3. import java.util.concurrent.locks.ReentrantReadWriteLock;
  4. import org.apache.ibatis.cache.Cache;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import redis.clients.jedis.Jedis;
  7. import redis.clients.jedis.JedisPool;
  8. import redis.clients.jedis.JedisPoolConfig;
  9. public class RedisCache implements Cache {
  10. private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
  11. /**
  12. * Jedis客户端
  13. */
  14. @Autowired
  15. private Jedis redisClient = createClient();
  16. private String id;
  17. public RedisCache(final String id) {
  18. if (id == null) {
  19. throw new IllegalArgumentException("必须传入ID");
  20. }
  21. System.out.println("MybatisRedisCache:id=" + id);
  22. this.id = id;
  23. }
  24. @Override
  25. public void clear() {
  26. redisClient.flushDB();
  27. }
  28. @Override
  29. public String getId() {
  30. return this.id;
  31. }
  32. @Override
  33. public Object getObject(Object key) {
  34. byte[] ob = redisClient.get(SerializeUtil.serialize(key.toString()));
  35. if (ob == null) {
  36. return null;
  37. }
  38. Object value = SerializeUtil.unSerialize(ob);
  39. return value;
  40. }
  41. @Override
  42. public ReadWriteLock getReadWriteLock() {
  43. return readWriteLock;
  44. }
  45. @Override
  46. public int getSize() {
  47. return Integer.valueOf(redisClient.dbSize().toString());
  48. }
  49. @Override
  50. public void putObject(Object key, Object value) {
  51. redisClient.set(SerializeUtil.serialize(key.toString()), SerializeUtil.serialize(value));
  52. }
  53. @Override
  54. public Object removeObject(Object key) {
  55. return redisClient.expire(SerializeUtil.serialize(key.toString()), 0);
  56. }
  57. protected static Jedis createClient() {
  58. try {
  59. @SuppressWarnings("resource")
  60. JedisPool pool = new JedisPool(new JedisPoolConfig(), "127.0.0.1", 6379);
  61. return pool.getResource();
  62. } catch (Exception e) {
  63. e.printStackTrace();
  64. }
  65. throw new RuntimeException("初始化连接池错误");
  66. }
  67. }

这个其实是使用mybatis自带的二级缓存,从写mybatis中的缓存类来进行实现使用这种方法就不需要额外的添加aop之类的只需要在映射的dao文件中添加<cache type="packagename.RedisCache" />这样便可以直接使用redis而不需要aop配置

言归正传,我们接着使用aop来实现redis整合,下面是service使用注解来进行实现redis存取

  1. package com.lwc.service;
  2. import com.lwc.dao.UserDao;
  3. import com.lwc.pojo.User;
  4. import com.lwc.redis.annotation.GetCache;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.stereotype.Service;
  7. @Service
  8. public class UserService {
  9. @Autowired
  10. private UserDao userDao;
  11. @GetCache(name = "user" ,value = "id")
  12. public User getUser(Integer id){
  13. return userDao.selectUser(id);
  14. }
  15. }

这样我们每次调用service的这个方法就可以实现从redis存取数据了。其他的UserDao和对应的映射文件我这里就不贴出来了

值得一说的是这里的实体类要继承Serializable 接口,不然会报错,因为他时间实例化对象进行序列化存入内存之中。

以上就完成了基本的redis的使用,下篇博文将会介绍redis的持久化

原文链接:http://www.cnblogs.com/sank/p/11326842.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号