说明: 1. 此项目为Spring-data-redis采用声明式缓存的方式进行演示
2. 此项目的精要之处:一是从零开始搭建环境并解决各路异常, 二是配置文件中的解析(具体解析未贴出于博文中)。
3. 环境中的各种jar版本和jdk版本一定要看清,这个项目演示要求版本之间要匹配。
=环境================
=代码==============
===>> User.java
package com.it.po;
import java.io.Serializable;
/**
*
* @author 拈花为何不一笑
*
*/
public class User implements Serializable{
/**
* serialVersionUID
*/
private static final long serialVersionUID = 2115183521540767080L;
private int id;
private String name;
private String password;
private String sex;//male, femal
public User(){}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "user={"
+ "id:" + id +","
+ "name:" + name +","
+ "sex:" + sex +","
+ "password:" + password
+ "}";
}
}
===>> SpringDataRedis.java
package com.it.service;
import org.springframework.cache.annotation.Cacheable;
import com.it.po.User;
import com.it.util.PassUtil;
/**
* Service层。
*
* Spring声明式缓存,避开采用编程式操作Redis
* 达到简化开发的目的即不需要显示的在代码中写java代码来操作Redis的存取,序列化等操作。
*
* 这也应证了Spring诞生之初的设计理念:简化。
*
* @author 拈花为何不一笑
*
* Spring中几个常用的缓存注解(搜索引擎中检索具体用法,这里不详述)
* @Cacheable
* @CacheEvict
* @CachePut
* @Caching
* 比如:@CacheEvict(value={"k1","k2"},allEntries=true)
* 表示执行注解上的方法后,清除redis中key名称为k1,k2的数据。
*
* 注意:使用缓存的前提条件,并不是什么数据都可以放到缓存中的。
* 可以设置缓存时间,长时间缓存某个数据,当数据被修改了就会变成"脏数据"。
*
*/
public class SpringDataRedis {
/**
* 这个方法相当于Service层方法
* @param id
* @return
*/
//"redis_2.com.it.service.findUserById"为key,存到Redis缓存中,下次再查询此数据就从Redis缓存中查询
@Cacheable(value = "redis_2.com.it.service.findUserById")
public User findUserById(int id){
//模拟从关系型数据库(比如:mysql,oracle等)中获取数据
User user = getUserById(id);
return user;
}
/**
* 这个方法相当于Dao层方法,操作关系型数据库
* @param id
* @return
*/
private User getUserById(int id) {
System.out.println("===>>getUserById查询数据库...");
User user = new User();
if(id==6){
user.setId(id);
user.setName("拈花为何不一笑");
user.setPassword(PassUtil.md5Hex("admin123"));
user.setSex("male");
}
return user;
}
}
===>> SpringDataRedisTest.java
package com.it.test;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.it.po.User;
import com.it.service.SpringDataRedis;
/**
* JUnit测试:Spring声明式缓存Redis
*
* @author 拈花为何不一笑
*
*/
public class SpringDataRedisTest {
private SpringDataRedis springDataRedis;
//使用完后,要进行关闭,否则内存泄漏
private AbstractApplicationContext ctx;
@Before//注意:注解@BeforeClass修饰的方法为静态方法
public void init(){
//初始化Spring容器
ctx = new ClassPathXmlApplicationContext("classpath:applicationContext-redisCache.xml");
System.out.println("=====>>>初始Spring容器,并连接Redis服务器成功...");
springDataRedis = (SpringDataRedis) ctx.getBean("springDataRedis");
}
//Spring容器关闭
@After
public void destroy(){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("destroy...");
ctx.close();
}
/**
* 遇到异常:
* 1.Caused by: java.lang.NoClassDefFoundError: org/springframework/aop/config/AopNamespaceUtils
* 解决方案:原因是缺少spring-aop.jar,导入此jar包到类路径下即可,若使用的是maven工程则在相应的pom.xml中配置即可。
* 2.Caused by: java.lang.ClassNotFoundException: org.aopalliance.intercept.MethodInterceptor
* 同上解决方案
* 3.Caused by: java.lang.UnsupportedClassVersionError:
* org/springframework/data/redis/cache/RedisCacheManager : Unsupported major.minor version 52.0
* 解决方案:jdk版本过低,把jdk改1.8
*
* 4.Caused by: java.lang.ClassNotFoundException: org.springframework.cache.transaction.AbstractTransactionSupportingCacheManager
* 解决方案:这个异常是由于缺少sping-context-support.jar包,导入此jar包即可。
*
* 5.Caused by: java.lang.ClassNotFoundException: org.springframework.cache.support.AbstractValueAdaptingCache
* 解决方案:此类位于spring-context.jar中,
* 由于3.2版本没有这个类,高版本spring-context.jar才有,所以版本升级不能用spring3.2,笔者这里换成Spring4.39。
* 换成spring-context-4.39.jar后其它的spring jar包也要全换成这个版本的。因为它们是一整套。
*
* 6.Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'redisCacheManager' defined in class path resource [applicationContext-redisCache.xml]: Cannot resolve reference to bean 'jedisConnectionFactory' while setting constructor argument; nested exception is
* org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jedisConnectionFactory' defined in class path resource [applicationContext-redisCache.xml]: Cannot create inner bean 'org.springframework.data.redis.connection.RedisStandaloneConfiguration#12325ad' of type
* [org.springframework.data.redis.connection.RedisStandaloneConfiguration] while setting constructor argument; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.data.redis.connection.RedisStandaloneConfiguration#12325ad'
* defined in class path resource [applicationContext-redisCache.xml]:
* Unsatisfied dependency expressed through constructor parameter 1: Could not convert argument value of type [java.lang.String] to required type [int]: Failed to convert value of type 'java.lang.String' to required type 'int'; nested exception is java.lang.NumberFormatException: For input string: "${redis.port}"
* 解决方案:数据类型转换异常,哈哈!大家看出来哪里有问题了嘛?使用字符串"${redis.port}",${}语法是从哪里取值?明白了吧,由于redis.proerties没有引入,取不到值把这个表达式"${redis.port}"当作了字符串,而实际注入的类型要求是int类型
*
* 7.Caused by: org.xml.sax.SAXParseException; lineNumber: 14; columnNumber: 85; 元素
* "context:property-placeholder" 的前缀 "context" 未绑定。
* 解决方案:schema约束,命名空间没有指定,xsd也没有指定。简单加上就可以了,如下:
* xmlns:context="http://www.springframework.org/schema/context"
* http://www.springframework.org/schema/context
* http://www.springframework.org/schema/context/spring-context.xsd"
*
* 8.Caused by: org.xml.sax.SAXParseException; lineNumber: 13; columnNumber: 11;
* 与元素类型 "beans" 相关联的属性名 "http:" 必须后跟 ' = ' 字符。
* 解决方案:粗心导致,由于多个双引号,导致xsi:schemaLocation="..."..." 语法异常。去掉多出来的就OK了
*
* 9.(a)异常Failed to instantiate [org.springframework.data.redis.connection.RedisStandaloneConfiguration]:
* Constructor threw exception; nested exception is java.lang.NoSuchMethodError:
* org.springframework.util.Assert.isTrue(ZLjava/util/function/Supplier;)V
*
* 解决方法:Spring-data-redis-2.0.6.jar包中的类 RedisStandaloneConfiguration引用不到Spring3.9 jar中的Assert类的isTrue方法(查看jar包发现有这个类和方法,就是引用不到)
* (Assert断言,这个概念首次接触是在玩C语言的时候...)。
* 第一种方案:想到的是jar顺序问题,可能导致访问不到Assert.java类,通过调整顺序后问题依旧存在,说明不是jar顺序问题。
* 第二种方案:Spring-data-redis-2.0.6.jar与Spring4.3.9版本不兼空。
* 大意了!前面的异常5,解决方案中随便使用了一个Spring版本(Spring4.3.9),熟悉Maven的同学估计都能够处理各种框架和jar包版本的匹配。
* 说一下笔者的思路:Spring-data-redis-2.0.6.jar找到pom.xml-->找到它的父项目pom.xml-->找到依赖Srping的版本号,一看为Spring5.0.5
* 换上Spring5.0.5(要求jdk1.8) 再解决下面的(b)异常,对于这里的异常就解决了。
*
*
* (b)异常:
* The project was not built since its build path is incomplete.
* Cannot find the class file for org.springframework.context.support.AbstractApplicationContext.
* Fix the build path then try building this project
* 原因:Spring5.x + jkd1.8在MyEclipse6.5空间中不能建立完整的空间,因为该IDE:MyEclipse6.5空间不支持jdk1.8环境
* (同时:JedisConnectionFactory.java和RedisStandaloneConfiguration.java都加载不了到MyEclipse6.5的空间中
* 切换空间后,Ctrl+左键都能显示"下划线连接")
* 解决方案:换个能够使用jdk1.8的IDE空间或环境,换成Eclipse4.5成功解决。(笔者机器上多种IDE切换方便)
*
* 10.序列化异常:缓存的对象被要求序列化,那么User对象就需要实现Serializable接口。
*
* 11.异常时:Caused by: java.net.SocketTimeoutException: connect timed out
* 解决方案:这个原因有多种,要根据自己的环境来定。
* 笔者这里就遇到二种:一是密码未设置,二是主机地址填写错了。但是不会报密码为空或地址有误,这个要你自己来调试了。
*
*
*
* 这么多异常能够快速解决也需要一定的功力的,你说是不是?哈哈!
*/
@Test
public void testFindUserById(){
//第二次再执行此方法testFindUserById(),
//通过 debug跟踪一下看看是不是从缓存中取的数据或者看看有没有再调用dao层访问数据库的日志信息
//此演示设置的日志信息为"===>>getUserById查询数据库...", 如果输出这条日志,说明是查询数据库而不是查询Redis缓存
//通过日志查看发现,是从Redis缓存中获取数据的
User user = springDataRedis.findUserById(6);
System.out.println("user:" + user);
}
}
===>> PassUtil
package com.it.util;
import org.apache.commons.codec.digest.DigestUtils;
public class PassUtil {
public static String md5Hex(String msg){
return DigestUtils.md5Hex(msg);
}
public static void main(String[] args) {
System.out.println(md5Hex("123"));
}
}
=配置文件==============
===>>redis.properties配置文件
#最大分配的对象数
redis.maxActive=600
#最大能够保持idel状态的对象数
redis.maxIdle=300
redis.minIdle=10
#当池内没有返回对象时,最大等待时间
redis.maxWait=1000
redis.testOnBorrow=true
redis.host=127.0.0.1
redis.port=6379
redis.pass=foo666k
#连接并使用Redis的数据库0,Redis数据库名称用数字表示0,1,2...
redis.dbIndex=0
#redis 缓存数据过期时间单位秒
redis.expiration=3000
===>>applicationContext-redisCache.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:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd">
<!-- 引入redis.properties文件 -->
<context:property-placeholder location="classpath:properties/redis.properties"/>
<!-- 开启缓存注解功能 -->
<cache:annotation-driven cache-manager="redisCacheManager"/>
<!-- spring-data-redis相关配置如下 ,这里使用的Spring配置是不是与大家平常的玩法有点不同,熟悉下这些方式。
下面配置的bean(比如:RedisPassword)同时还涉及到了jdk1.8的新特性"lambda表达式"。
-->
<!--1.redis缓存管理器 -->
<bean id="redisCacheManager" class="org.springframework.data.redis.cache.RedisCacheManager"
factory-method="create" c:connection-factory-ref="jedisConnectionFactory"/>
<!--2.Redis服务器配置的密码 -->
<bean id="redisPassword" class="org.springframework.data.redis.connection.RedisPassword"
c:thePassword="${redis.pass}" />
<!--3.jedis连接工厂 -->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<constructor-arg>
<bean class="org.springframework.data.redis.connection.RedisStandaloneConfiguration"
c:host-name="${redis.host}" c:port="${redis.port}" p:password-ref="redisPassword" />
</constructor-arg>
</bean>
<!-- 4.序列化 -->
<bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
<!-- 5.模板,类比JdbcTemplate或HibernateTemplate,可以得出这个模板相当于"操作Redis的dao层模板" -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory"/>
<property name="keySerializer" ref="stringRedisSerializer"/>
<property name="hashKeySerializer" ref="stringRedisSerializer"/>
</bean>
<bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate"
p:connection-factory-ref="jedisConnectionFactory"/>
<!-- service层bean -->
<bean id="springDataRedis" class="com.it.service.SpringDataRedis" />
</beans>
@拈花为何不一笑, 谢谢大家阅读。还有一年就要毕业了,工作好找嘛?嘟嘟……