持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情
第三方框架整合Spring之FactoryBean(二)
前言
上一篇文章中我们介绍了Mybaits是如何利用FactoryBean与Spring整合的(第三方框架整合Spring的源码包都是以 xx-spring命名的,
如: mybatis-spring, shiro-spring, dubbo-spring等), 并参考着写了一个小demo进行测试,
下面我们来实现一个实用的小工具
通过读取yaml配置, 将不同定义信息的RedisTemplate注入到Spring容器中, 使用时, 只需要注入不同名称的RedisTemplate即可
区分不同redis 数据库 或者不同的 redis 连接,
我们此次使用Spring提供的另外一个接口BeanDefinitionRegistryPostProcessor, 该接口属于BeanDefinition的后置处理器,
Spring允许使用者在所有常规BeanDefinition都已加载,但是还没有被实例化的时候修改内部的BeanDefinition注册表,
在进入下一个处理阶段前, 添加自定义的BeanDefinition
思路
首先先整理一下具体的步骤
- 定义
MyRedisTemplateFactoryBean实现FactoryBean接口 - 定义
MyBeanDefinitionRegistryRedis实现BeanDefinitionRegistryPostProcessor,EnvironmentAware(用于获取环境变量和配置信息)接口 - 注册Bean的定义信息
代码
相关依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
</dependency>
MyRedisTemplateFactoryBean具体代码
public class MyRedisTemplateFactoryBean implements FactoryBean {
private RedisStandaloneConfiguration standaloneConfiguration;
public MyRedisTemplateFactoryBean(RedisStandaloneConfiguration redisStandaloneConfiguration) {
this.standaloneConfiguration = redisStandaloneConfiguration;
}
@Override
public Object getObject() {
// 通过jedisConnectionFactory实例化一个RedisTemplate
RedisTemplate redisTemplate = new RedisTemplate();
RedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(this.standaloneConfiguration);
redisTemplate.setConnectionFactory(jedisConnectionFactory);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Override
public Class<?> getObjectType() {
return RedisTemplate.class;
}
}
MyBeanDefinitionRegistryRedis具体代码
public class MyBeanDefinitionRegistryRedis implements BeanDefinitionRegistryPostProcessor, EnvironmentAware {
private Environment environment;
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// 解析配置文件, 将以multi-redis开头的配置信息封装到RedisConfig
BindResult<RedisConfig> bindResult = Binder.get(environment).bind("multi-redis", RedisConfig.class);
bindResult.ifBound(redisTemplateConfig -> registerBeanDefinition(redisTemplateConfig, registry));
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
/**
* 注册Bean的定义信息
*/
private void registerBeanDefinition(RedisConfig redisTemplateConfigs, BeanDefinitionRegistry registry) {
Map<String, MyRedisStandaloneConfiguration> multiRedisTemplateConfigs = redisTemplateConfigs.getRedisConfigs();
for (Map.Entry<String, MyRedisStandaloneConfiguration> redisStandaloneConfiguration : multiRedisTemplateConfigs.entrySet()) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(RedisTemplate.class);
GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
// 调用构造函数并传入redisStandaloneConfiguration参数
definition.getConstructorArgumentValues().addGenericArgumentValue(redisStandaloneConfiguration.getValue());
definition.setBeanClass(MyRedisTemplateFactoryBean.class);
// 注入方式AUTOWIRE_BY_NAME, 通过名称注入
definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_NAME);
// 名称为yaml中配置的名称
registry.registerBeanDefinition(redisStandaloneConfiguration.getKey(), definition);
}
}
}
Redis相关配置类
@Data
public class RedisConfig {
private Map<String, MyRedisStandaloneConfiguration> redisConfigs;
}
/**
* redis基础信息配置
*
*/
public class MyRedisStandaloneConfiguration extends RedisStandaloneConfiguration {
public MyRedisStandaloneConfiguration(String hostName, String password, Integer port, Integer database) {
Optional.ofNullable(hostName).ifPresent(e -> super.setHostName(hostName));
Optional.ofNullable(password).ifPresent(e -> super.setPassword(password));
Optional.ofNullable(port).ifPresent(e -> super.setPort(port));
Optional.ofNullable(database).ifPresent(e -> super.setDatabase(database));
}
}
/**
* redisTemplate配置
*
*/
@Configuration
public class RedisTemplateConfig {
@Primary
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
redis yaml
多redisTemplate实例配置, 根据名称注入, 默认配置 hostName: localhost; password: none; port: 6379; database: 0
EnableMultiRedisTemplate
@Retention(RetentionPolicy.RUNTIME)
@Import(MyBeanDefinitionRegistryRedis.class)
public @interface EnableMultiRedisTemplate {
}
测试
启动类加上
@EnableMultiRedisTemplate注解开启多实例RedisTemplate支持
//================================ 测试注入redisTemplate ======================
/**
* 自动注入redisTemplate, @Autowired默认按类型注入, 类型有多个或未找到按名称注入
* FactoryBean方式注入redisTemplate, @Resource默认按名称注入, 名称未找到按类型注入
* 但是RedisTemplate类型默认有多个<RedisTemplate, StringRedisTemplate> 会报错
* 需要指定@Primary
* 当前指定RedisTemplate为主, 即按类型注入默认使用redisTemplate
* 注释 multi-redis 配置, 则会使用默认redisTemplate注入
*/
@Autowired
private StringRedisTemplate strRedisTemplate;
@Autowired
private RedisTemplate redisTemplate;
@Resource
private RedisTemplate redisTemplate0;
@Resource
private RedisTemplate redisTemplate1;
@Resource
private RedisTemplate redisTemplate2;
@PostConstruct
public void testRedisTemplate() {
strRedisTemplate.opsForValue().set("stringRedisTemplate", "stringRedisTemplate");
redisTemplate.opsForValue().set("redisTemplate", "redisTemplate");
redisTemplate0.opsForValue().set("test0", "test0");
redisTemplate1.opsForValue().set("test1", "test1");
redisTemplate2.opsForValue().set("test2", "test2");
}
可以看到测试数据已经根据不同配置set到不同的redis database中了
至此我们已经完成了整合Spring实现多redisTemplate实例配置, 根据名称注入的功能!!!