背景:由于项目需要引入多个
Redis提供数据访问,手写RedisTemplate导致代码冗余且维护效率低下,无法统一管理,基于此,实现Redis starter,开箱即用。
一、实现思路:
- 读取Redis连接信息
- 构建连接池
- 注册bean到Springboot工厂
- 扫描自定义注解并注入对应Redis Bean对象
二、实现过程:
1)读取Redis连接信息
- Redis Properties配置属性如下:
Spring:
multi:
redisMaster:
host: *.*.*.*
port: 6379
database: 1
password: xxx
redisSlave:
host: *.*.*.*
port: 6379
database: 1
password: xxx
- 实现类属性
private int database;
private String host;
private String password;
private int port;
- 由于actuator对redis健康性检查,自定义配置后,导致redis无法通过actuator检查,导致启动报错,需要手动关闭,这里采用以代码方式关闭
@Primary
@Bean
@ConditionalOnEnabledHealthIndicator("redis")
public HealthIndicator redisHealthIndicator()
// 返回健康状态
return () -> Health.up().build();
}
- 使用EnvironmentAware读取Redis连接属性
//1.读取以spring.multi-redis为前缀的配置属性
//2.形成Map数据
Map<String, RedisProperties> redisPropertiesMap = Binder.get(environment).bind("spring.multi", Bindable.mapOf(String.class, RedisProperties.class)).get();
2)构建连接池
public class RedisBeanBuilder {
public static RedisBeanBuilder builder() {
return new RedisBeanBuilder();
}
public RedisTemplate buildRedisTemplate(RedisProperties redisProperties) {
GenericObjectPoolConfig poolConfig = RedisConfig.buildPoolConfig();
RedisStandaloneConfiguration configuration = RedisConfig.buildRedisConfiguration(redisProperties.getHost(),redisProperties.getDatabase(), redisProperties.getPassword());
LettuceConnectionFactory connectionFactory = RedisConfig.getConnectionFactory(poolConfig, configuration);
RedisTemplate redisTemplate = RedisConfig.getRedisTemplate(connectionFactory);
return redisTemplate;
}
public BeanDefinition buildRedisBean(RedisProperties redisProperties) {
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(RedisTemplate.class, () -> buildRedisTemplate(redisProperties));
return beanDefinitionBuilder.getBeanDefini
}
}
3)注册bean到Springboot工厂
//遍历redisPropertiesMap,注册Redis Bean对象
public class DynamicRegisterRedisBean implements BeanDefinitionRegistryPostProcessor, , EnvironmentAware {
//动态注册成bean对象
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
for (Map.Entry<String, RedisProperties> entry : redisPropertiesMap.entrySet()) {
BeanDefinition beanDefinition = RedisBeanBuilder.builder().buildRedisBean(redisSecurity.get(entry.getKey()));
beanDefinitionRegistry.registerBeanDefinition(entry.getKey(), beanDefinition);
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
@Override
public void setEnvironment(Environment eenvironment) {
redisPropertiesMap = Binder.get(environment).bind("spring.multi", Bindable.mapOf(String.class,RedisProperties.class)).get();
}
}
4)自定义注解注入Redis
通过使用AutoRedis注解注入Redis属性被调用,类似@Autowired注解,用法如下
@AutoRedis
private RedisService redisService;
@Component
public class AutoInjectRedisServiceBean 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 {
List<Field> allFields = ReflectUtils.getAllFields(bean);
for (Field field : allFields) {
AutoRedis annotation = field.getAnnotation(AutoRedis.class);
if (StringUtils.isNull(annotation)) {
continue;
}
String redisName = annotation.value().getName();
// 如果Bean不存在,且不允许使用默认Bean就抛出异常
if (!this.applicationContext.containsBean(redisName)) {
throw new CustomException(annotation.value().name());
}
try {
Object redisServiceBean;
// 如果指定bean不存在,使用默认bean
redisServiceBean = !this.applicationContext..containsBean(redisName) ? this.applicationContext.getBean(RedisType.REDIS.getName(), RedisService.class) : this.applicationContext.getBean(redisName,
RedisService.class);
// 注入bean
ReflectUtils.setFieldValue(bean, field.getName(), redisServiceBean);
} catch (Exception e) {
throw new CustomException("注入Redis Bean异常", e);
}
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}