【java小记】springboot 通过注解使用redis,多redis,动态注册Bean,自动注入

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");
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值