1、说明
通过配置文件配置任意redis,通过注解指定使用的redis
2、引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
3、配置文件
因为需要使用多个redis,所以稍微修改一下redis的配置格式,整体符合原有规范
spring:
redis:
# 唯一名称
my-redis1:
host: 192.168.120.1
port: 6379
database: 1
is-default: true
password:
# 唯一名称
my-redis2:
host: 192.168.120.2
port: 6379
database: 2
password:
为配置文件准备一个实体类RedisInfo和config类MyRedisConfig.java
public class RedisInfo {
private String host;
private Integer port;
private String password;
private Integer database;
private Boolean isDefault;
public String getHost() { return host; }
public void setHost(String host) { this.host = host; }
public void setDatabase(int database) { this.database = database;}
public Integer getPort() { return port; }
public void setPort(Integer port) { this.port = port; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public Integer getDatabase() { return database; }
public void setDatabase(Integer database) { this.database = database; }
public Boolean getDefault() { return isDefault == null ? false : isDefault; }
public void setIsDefault(Boolean aDefault) { isDefault = aDefault; }
}
@Configuration
@ConfigurationProperties(prefix = "spring")
public class MyRedisConfig {
public static final String DEFAULT_BEAN_NAME = "MyReservedDefaultRedisService";
private Map<String, RedisInfo> redis;
public Map<String, RedisInfo> getRedis() {
return redis;
}
public void setRedis(Map<String, RedisInfo> redis) {
this.redis = redis;
}
}
4、Redis服务类
写一个简单的redis服务类
public class RedisService{
public RedisTemplate redisTemplate;
public RedisService() {
}
public RedisService(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
//存入redis
public <T> void setCacheObject(final String key, final T value)
{
redisTemplate.opsForValue().set(key, value, 30, TimeUnit.MINUTES);
}
//从redis取出
public <T> T getCacheObject(final String key)
{
ValueOperations<String, T> operation = redisTemplate.opsForValue();
return operation.get(key);
}
}
因为需要手动构建服务类的Bean,所以为其配置一个简单的构建类。
先写一个简单的工具类RedisTemplateUtil,用于创建RedisTemplate相关的配置。
public class RedisTemplateUtil {
//配置redis
public static RedisStandaloneConfiguration buildRedisConfiguration(String host,
Integer port ,
Integer database,
String password){
RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
configuration.setHostName(host);
configuration.setPort(port);
if(database != null)
configuration.setDatabase(database);
if(password != null)
configuration.setPassword(RedisPassword.of(password));
return configuration;
}
//配置连接池参数
public static GenericObjectPoolConfig buildPoolConfig(){
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setMaxIdle(30); //连接池中的最大空闲连接
poolConfig.setMinIdle(10); //连接池中的最小空闲连接
poolConfig.setMaxTotal(3000); // 连接池最大连接数(使用负值表示没有限制)
poolConfig.setMaxWaitMillis(Duration.ofMillis(3000).toMillis());//连接池最大阻塞等待时间(使用负值表示没有限制)
return poolConfig;
}
//获取ConnectionFactory
public static LettuceConnectionFactory getConnectionFactory(GenericObjectPoolConfig poolConfig,
RedisStandaloneConfiguration configuration){
LettuceClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder()
.poolConfig(poolConfig)
.build();
LettuceConnectionFactory factory = new LettuceConnectionFactory(configuration, clientConfig);
factory.afterPropertiesSet();
return factory;
}
//获取redisTemplate
public static RedisTemplate<Object, Object> getRedisTemplate(RedisConnectionFactory connectionFactory){
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
// 使用 GenericFastJsonRedisSerializer 替换默认序列化
GenericFastJsonRedisSerializer serializer = new GenericFastJsonRedisSerializer();
// 设置key和value值的序列化规则
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
// 设置hashKey和hashValue值的序列化规则
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
}
Bean的构建类
public class RedisServiceBeanBuilder {
private String host;
private Integer port;
private String password;
private Integer database;
public static RedisServiceBeanBuilder builder(){
return new RedisServiceBeanBuilder();
}
public RedisServiceBeanBuilder setHost(String host) {
this.host = host;
return this;
}
public RedisServiceBeanBuilder setPort(Integer port) {
this.port = port;
return this;
}
public RedisServiceBeanBuilder setPassword(String password) {
this.password = password;
return this;
}
public RedisServiceBeanBuilder setDatabase(Integer database) {
this.database = database;
return this;
}
public RedisService build(){
GenericObjectPoolConfig poolConfig = RedisTemplateUtil.buildPoolConfig();
RedisStandaloneConfiguration configuration = RedisTemplateUtil
.buildRedisConfiguration(host, port, database, password);
LettuceConnectionFactory connectionFactory = RedisTemplateUtil.getConnectionFactory(poolConfig, configuration);
RedisTemplate redisTemplate = RedisTemplateUtil.getRedisTemplate(connectionFactory);
return new RedisService(redisTemplate);
}
/通过BeanDefinitionBuilder 手动创建Redis服务的Bean
public BeanDefinition buildBean(){
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
.genericBeanDefinition(RedisService.class, () -> build());
return beanDefinitionBuilder.getBeanDefinition();
}
}
5、将Bean注册到IOC容器
通过实现BeanDefinitionRegistryPostProcessor接口将手动创建的Bean注册的容器中,通过实现EnvironmentAware接口从配置文件获取config。因为是postProcessBeanDefinitionRegistry()方法,所以要手动获取配置文件。
@Component
public class RedisServiceBeanFactory implements EnvironmentAware, BeanDefinitionRegistryPostProcessor {
private MyRedisConfig config;
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
//默认redis服务
BeanDefinition defaultBean = null;
//第一个普通的redis服务
BeanDefinition firstCommonBean = null;
Map<String, RedisInfo> infoMap = config.getRedis();
if(infoMap == null)
return;
for (Map.Entry<String, RedisInfo> entry : infoMap.entrySet()) {
RedisInfo info = entry.getValue();
BeanDefinition common = RedisServiceBeanBuilder.builder()
.setHost(info.getHost())
.setPort(info.getPort())
.setDatabase(info.getDatabase())
.setPassword(info.getPassword())
.buildBean();
//如果默认redis服务不存在,且这个服务设置为默认服务
if (defaultBean == null && info.getDefault())
defaultBean = common;
//设置第一个通用redis服务
if (firstCommonBean == null)
firstCommonBean = common;
//注册common服务
registry.registerBeanDefinition(entry.getKey(), common);
}
//如果最后没有设置默认redis服务,就使用第1个
if(defaultBean == null && firstCommonBean != null)
defaultBean = firstCommonBean;
//注册默认
if(defaultBean != null)
registry.registerBeanDefinition(MyRedisConfig.DEFAULT_BEAN_NAME, defaultBean);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
@Override
public void setEnvironment(Environment environment) {
//手动获取配置文件
this.config = Binder.get(environment).bind("spring", MyRedisConfig.class).get();
}
}
6、自定义注解
自定义一个注解用于注入Bean,类似于@Resource。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface AutoRedisService {
//如果不指定redis, 就使用默认redis
String value() default MyRedisConfig.DEFAULT_BEAN_NAME;
//当指定redis不存在,是否自动切换为默认redis
boolean autoDefault() default true;
}
通过实现BeanPostProcessor接口将Bean注入。其流程就是在一个Bean(如果controller)实例化之前,扫描其所有的成员变量,如果带有@AutoRedisService 注解就手动给该成员变量赋值。
@Component
public class AutoRedisServiceBeanPostRegister implements BeanPostProcessor, ApplicationContextAware {
private GenericApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = (GenericApplicationContext) applicationContext;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Class<?> beanClass = AopUtils.getTargetClass(bean);
//获取当前Bean的成员变量
Field[] fields = beanClass.getDeclaredFields();
int length = fields.length;
for( int i = 0; i < length; i++){
Field field = fields[i];
//判断成员变量是否带有@AutoRedisService注解
AutoRedisService annotation = field.getAnnotation(AutoRedisService.class);
if(annotation == null)
continue;
//如果Bean不存在,且不允许使用默认Bean就抛出异常
if(!this.applicationContext.containsBean(annotation.value()) && !annotation.autoDefault())
throw new RedisServiceBeanNotFoundException(annotation.value()); //自定义异常
Object redisServiceBean;
try{
//如果指定bean不存在,使用默认bean
redisServiceBean = !this.applicationContext.containsBean(annotation.value()) ?
this.applicationContext.getBean(MyRedisConfig.DEFAULT_BEAN_NAME, RedisService.class) :
this.applicationContext.getBean(annotation.value(), RedisService.class);
//注入bean
field.setAccessible(true);
field.set(bean, redisServiceBean);
}catch (Exception e){
throw new RedisServiceBeanException("Auto RedisService ean error.", e);//自定义异常
}
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
7、使用注解
@RestController
public class DemoController {
@AutoRedisService
private RedisService redisService;
@GetMapping("/set")
public void set(String value){
redisService.setCacheObject("test", value);
}
@GetMapping("/get")
public String get(){
return redisService.getCacheObject("test");
}
}