前言
- Redis缓存实现
1.1 自定义Redis缓存实现类
1.2 创建生成Spring工厂工具类 - 小结
前言
1.项目场景:开发中遇到使用Mybatis实现二级缓存。项目中涉及>到使用Redis来存储二次查询的数据,但对于存储的对象类型我们需要去自定义一个RedisCache类并实现’'Cache"以此来重新里面>的"put"和“get”方法。
2 但是出现一个问题,我们在自定义这个RedisCache中使用@Autowired注解注入操作Redis的"RedisTemplate对象"的时候,
显示为null,通过研究发现我发先’非Spring容器管理的类中’去使用
/注入由Spring容器管理的RedisTemplate对象。
3.解决方案为:创建一个Spring工厂,然后在RedisCache这个自定义类中引入此工厂,在通过工厂的getBean() 获取到RedisTemplate这个对象即可。
1. Redis缓存实现
1.1 自定义Redis缓存实现类
//自定义Redis缓存实现
public class RedisCache implements Cache {
//当前放入缓存的mapper的namespace
private final String id;
//必须存在构造方法
public RedisCache(String id) {
System.out.println("id:=====================> " + id);
this.id = id;
}
//返回cache唯一标识
@Override
public String getId() {
return this.id;
}
//缓存放入值 redis RedisTemplate StringRedisTemplate
@Override
public void putObject(Object key, Object value) {
System.out.println("key:" + key.toString());
System.out.println("value:" + value);
// //通过application工具类获取redisTemplate
// RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
// redisTemplate.setKeySerializer(new StringRedisSerializer());
// redisTemplate.setHashKeySerializer(new StringRedisSerializer());
//使用redishash类型作为缓存存储模型 key hashkey value
getRedisTemplate().opsForHash().put(id.toString(),getKeyToMD5(key.toString()),value);
//根据UserDAO模块,设置对应的缓存超时时间
if(id.equals("com.zk.dao.UserDAO")){
//缓存超时 client 用户 client 员工
getRedisTemplate().expire(id.toString(),1, TimeUnit.HOURS);
}
//若是CityDAO模块,设置另一个超时时间
if(id.equals("com.zk.dao.CityDAO")){
//缓存超时 client 用户 client 员工
getRedisTemplate().expire(id.toString(),30, TimeUnit.MINUTES);
}
//.....根据不同业务模块,设置不同缓存超时时间
}
//获取中获取数据
@Override
public Object getObject(Object key) {
System.out.println("key:" + key.toString());
// //通过application工具类获取redisTemplate
// RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
// redisTemplate.setKeySerializer(new StringRedisSerializer());
// redisTemplate.setHashKeySerializer(new StringRedisSerializer());
//根据key 从redis的hash类型中获取数据
return getRedisTemplate().opsForHash().get(id.toString(), getKeyToMD5(key.toString()));
}
/**
* 1. removeObject()此烦啊发暂时没什么用
* 注意:这个方法为mybatis保留方法 默认没有实现 后续版本可能会实现
*/
@Override
public Object removeObject(Object key) {
System.out.println("根据指定key删除缓存");
return null;
}
/**
* 1. 我们执行redis的删除方法,默认走的是clear()这个方法
* 对于redis执行增删改操作,都会走此方法将redis的缓存清空掉
*/
@Override
public void clear() {
System.out.println("清空缓存~~~");
//清空namespace
getRedisTemplate().delete(id.toString());//清空缓存
}
//用来计算缓存数量
@Override
public int getSize() {
//获取hash中key value数量
return getRedisTemplate().opsForHash().size(id.toString()).intValue();
}
//封装redisTemplate。定义成私有,表示该方法只能内部被调用使用
private RedisTemplate getRedisTemplate(){
//通过application工具类获取redisTemplate
RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
return redisTemplate;
}
}
1.2 创建生成Spring工厂工具类
package com.zk.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
//用来获取springboot创建好的工厂
/**
* 1. 由于我们在使用分布式缓存时,需要用到'RedisTemplate'或'StringRedisTemplate'其中的一个对象。
* 我们这里分布式缓存用到对象,所以这里使用"RedisTemplate"这个对象。
* ➀. 如果RedisTemplate这个类是工厂管理,我们就可以直接使用@Autowired注入这个对象,但是这个类的实例化
* 是由mybatis实例化的(创建的)-->而mybatis中的Cache对象不是由工厂去管理的对象,我们要在自定义的
* RedisCache这个类下面去注入并使用'RedisTemplate',是无法注入的。
*
* ➁. 因此若要拿到'RedisTemplate'这个对象,就需要到Spring的工厂中去获取,而"ApplicationContext这个工厂"是
* 我们最大的工厂,因此我们就需要获取到ApplicationContext这个工厂,然后根据这个工厂中的"getbean(String bean)"
* 传入要获取的对象字符串名,去工厂中获取'RedisTemplate'这个对象。
*
*
* 2. 所以我们可以去创建类,比如创建一个(ApplicationContextUtils)类,并让这个类去实现'ApplicationContextAware接口',
* 此时当我们去实现'ApplicationContextAware接口后'就代表springboot它会自动帮我们创建一个工厂,当springboot帮我们创建号
* 工厂后,springboot会通过"setApplicationContext()"这个方法以参数的形式给我们返回创建号的这个工厂。
*
* 3. 之后我们就可以获取到这个"ApplicationContext工厂"-->并调用getBean()获取我们需要的"RedisTemplate对象"执行
* redis中的方法。
* --->你日后如果给我传进来的是"RedisTemplate",那么入参就是小写的'redisTemplate'。
*
*
*/
@Component
public class ApplicationContextUtils implements ApplicationContextAware {
//
/**
* 1. 将springboot给我们创建的工厂定义为全局静态变量,将此工厂对象保留下来
* 2. 下面"setApplicationContext()"给我们返回的工厂我们就用此对象去接受springboot
* 给我们返回的这个'ApplicationContext'工厂。
*/
private static ApplicationContext applicationContext;
//将创建好工厂以参数形式传递给这个类
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/**
* 1. 日后我们可以通过springboot给我们创建的这个工厂,去获取对应我们所需要的
* 对象的方法,通过getBean()去获取
* 2. 下面方法的入参我们定义称我们锁需要获取的对象的小写形式,他就会给我们返回具体的对象。
*/
//提供在工厂中获取对象的方法 //RedisTemplate redisTemplate
public static Object getBean(String beanName){
return applicationContext.getBean(beanName);
}
}
2. 问题小结
1. 在不被Spring容器管理的对象中无法注入Spring管理的对象问题
的小结。
⑴ 简单来说就是:对象RedisCache独立于spring容器(RedisCache实际
上是由mybatis框架实现初始化,myabtis所管理的),而处理Redis的
RedisTemplate实例由spring容器管理。如果直接在RedisCache类中使
用@Autowired注入RedisTemplate对象,在运行时RedisTemplate实例
对象将是null。
⑵ 导致错误。因为RedisTemplate实例对象只有在spring容器中才能使
用,那么在RedisCache这个非spring容器管理的对象想要使用的话,就
要先取得spring的上下文对象,然后再通过上下文对象去获取
RedisTemplate实例对象即可。
1.1 上下文对象的获取,项目采用的是springboot,可以通过以下方式拿到spring的上下对象。
⑴ 方式一:定义一个持有上下文对象的类,将该类交由spring容器管理,然后通过该类去获取上下文对象
@Component
public class ApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static ApplicationContext getContext() {
return context;
}
}
⑶ 目标对象的生成
➀ 通过调用context的getBean方法获取在spring容器中管理的对
象。下面获取RedisTemplate对象的方法。
# 通过方法ApplicationContextHolder中的静态context对象:
private static SimpMessagingTemplate simpMessagingTemplate = ApplicationContextHolder.getContext()
.getBean(RedisTemplate.class);
-