Spring自动配置Redis

原理参考ImportBeanDefinitionRegistrar+SPI简化Spring开发
本文描述通过注解方式在spring中自动配置RedisTemplate,并支持启用注解方式的redis cache

首先约定redis的配置文件如下:

redis:
  host: 127.0.0.1
  port: 6379
  password: passwd
  db: 0
  timeout: 5000
  pool:
    max_active: 60
    max_idle: 30
    max_wait: 30
    test_on_borrow: true
    test_on_return: true

其次我们定义集成Redis的注解EnableRedisIntegration

/**
 * EnableRedisIntegration redis集成配置,所需redis配置参考RedisIntegrationConfiguration
 * 

是否开启Redis缓存

*

是否开启RedisMessage

* * @version 1.0 * Date: 2020-03-20 */
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @EnableAutoRegistrar @Import(RedisIntegrationConfiguration.class) public @interface EnableRedisIntegration { /** * 是否启用RedisCache */ boolean enableRedisCache() default true; /** * RedisCache Key前缀,仅在启用RedisCache时生效 */ String redisCacheKeyPrefix() default "RE:CH"; /** * RedisCache 缓存默认过期时间,仅在启用RedisCache时生效 */ long defaultRedisCacheRxpire() default 60; /** * 本地缓存CacheManager的beanName,仅在启用RedisCache时生效 */ String localCacheManager() default ""; /** * 是否启用RedisMessageListener */ boolean enableRedisMessageListener() default false; }

集成Redis注入相关bean主要在RedisIntegrationConfiguration中:

@Configuration
public class RedisIntegrationConfiguration implements EnvironmentAware {

    @Setter
    private Environment environment;

    @Lazy
    @Bean("defaultJedisPoolConfig")
    public JedisPoolConfig jedisPoolConfig() {
        final JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(environment.getProperty("redis.pool.max_active", Integer.class));
        config.setMaxIdle(environment.getProperty("redis.pool.max_idle", Integer.class));
        config.setMaxWaitMillis(environment.getProperty("redis.pool.max_wait", Integer.class));
        config.setTestOnBorrow(environment.getProperty("redis.pool.test_on_borrow", Boolean.class));
        config.setTestOnReturn(environment.getProperty("redis.pool.test_on_return", Boolean.class));
        return config;
    }

    @Lazy
    @Bean("defaultRedisStandaloneConfiguration")
    public RedisStandaloneConfiguration redisStandaloneConfiguration() {
        final RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
        configuration.setHostName(environment.getProperty("redis.host"));
        configuration.setPort(environment.getProperty("redis.port", Integer.class));
        configuration.setDatabase(environment.getProperty("redis.db", Integer.class));
        configuration.setPassword(RedisPassword.of(environment.getProperty("redis.password")));
        return configuration;
    }

    @Lazy
    @Bean("defaultJedisConnectionFactory")
    public JedisConnectionFactory jedisConnectionFactory() {
        JedisClientConfiguration jedisClientConfiguration = JedisClientConfiguration
                .builder()
                .usePooling()
                .poolConfig(jedisPoolConfig())
                .and()
                .connectTimeout(Duration.ofMillis(environment.getProperty("redis.timeout", Long.class)))
                .readTimeout(Duration.ofMillis(environment.getProperty("redis.timeout", Long.class)))
                .build();
        return new JedisConnectionFactory(redisStandaloneConfiguration(), jedisClientConfiguration);
    }

    @Lazy
    @Bean("stringRedisTemplate")
    public RedisTemplate stringRedisTemplate() {
        return new StringRedisTemplate(jedisConnectionFactory());
    }

    @Lazy
    @Bean("defaultJedisPool")
    public JedisPool jedisPool() {
        return new JedisPool(jedisPoolConfig(),
                environment.getProperty("redis.host"),
                environment.getProperty("redis.port", Integer.class),
                environment.getProperty("redis.timeout", Integer.class),
                environment.getProperty("redis.password"));
    }

    @Lazy
    @Bean("defaultRedisBean")
    public RedisBean redisBean() {
        final RedisBean redisBean = new RedisBean();
        redisBean.setPool(jedisPool());
        redisBean.setDb(environment.getProperty("redis.db", Integer.class));
        return redisBean;
    }

}

对是否启用redis缓存的处理以及是否开启redis message的处理是通过Handler实现:

@Slf4j
public class EnableRedisIntegrationHandler implements ConfigurationRegisterHandler {

    @Override
    public void registerBeanDefinitions(RegisterBeanDefinitionContext context) {
        AnnotationAttributes attributes = AnnotationAttributes
                .fromMap(context.getImportingClassMetadata()
                        .getAnnotationAttributes(EnableRedisIntegration.class.getName()));
        if (attributes == null || attributes.isEmpty()) {
            return;
        }
        handleRedisCache(attributes, context);
        handleRedisMessageListener(attributes, context);
    }

    @Override
    public int getOrder() {
        return 0;
    }

    private void handleRedisCache(AnnotationAttributes attributes, RegisterBeanDefinitionContext context) {
        if (!attributes.getBoolean("enableRedisCache")) {
            return;
        }
        // 注册CacheConfig
        BeanDefinitionBuilder cacheConfigDefinitionBuilder =
                BeanDefinitionBuilder.genericBeanDefinition(CacheConfig.class);
        cacheConfigDefinitionBuilder.addPropertyValue("redisTemplateName", "stringRedisTemplate");
        cacheConfigDefinitionBuilder.addPropertyValue("expire",
                attributes.getNumber("defaultRedisCacheRxpire").longValue());
        cacheConfigDefinitionBuilder.addPropertyValue("name",
                attributes.getString("redisCacheKeyPrefix"));
        context.getRegistry().registerBeanDefinition("defaultRedisCacheConfig",
                cacheConfigDefinitionBuilder.getBeanDefinition());
        // 注册RedisCacheAspect
        BeanDefinitionBuilder annotationRedisCacheAspectBuilder =
                BeanDefinitionBuilder.genericBeanDefinition(RedisCacheAspect.class);
        annotationRedisCacheAspectBuilder.addPropertyReference("cacheConfig", "defaultRedisCacheConfig");
        if (StringUtils.isNotBlank(attributes.getString("localCacheManager"))) {
            annotationRedisCacheAspectBuilder.addPropertyReference("localCacheManager",
                    attributes.getString("localCacheManager"));
        } else {
            try {
                final CacheManager cacheManager = context.getBeanFactory().getBean(CacheManager.class);
                annotationRedisCacheAspectBuilder.addPropertyValue("localCacheManager", cacheManager);
            } catch (BeansException e) {
                log.warn("can not wire CacheManager for localCache");
            }
        }
        context.getRegistry().registerBeanDefinition("annotationRedisCacheAspect",
                annotationRedisCacheAspectBuilder.getBeanDefinition());
        // 注册AOP
        String pointcutTemplate = "@within(%s)\n"
                + "                        || @annotation(%s)\n"
                + "                        || @within(%s)\n"
                + "                        || @annotation(%s)\n"
                + "                        || @within(%s)\n"
                + "                        || @annotation(%s)";
        final AspectJParams params = new AspectJParams()
                .setProxyTargetClass(true)
                .setAopRefBean("annotationRedisCacheAspect")
                .setAopOrder(1)
                .setAopAdvice("around")
                .setAopAdviceMethod("doCacheAspect")
                .setAopPointcut(String.format(pointcutTemplate,
                        RedisCacheable.class.getName(), RedisCacheable.class.getName(),
                        RedisCachePut.class.getName(), RedisCachePut.class.getName(),
                        RedisCacheEvict.class.getName(), RedisCacheEvict.class.getName()));
        try {
            AopBeanDefinitionRegistry.loadBeanDefinitions(context.getRegistry(), params);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void handleRedisMessageListener(AnnotationAttributes attributes, RegisterBeanDefinitionContext context) {
        if (!attributes.getBoolean("enableRedisMessageListener")) {
            return;
        }
        // 注册RedisMessageListenerContainer
        BeanDefinitionBuilder containerDefinitionBuilder =
                BeanDefinitionBuilder.genericBeanDefinition(RedisMessageListenerContainer.class);
        containerDefinitionBuilder.addPropertyReference("connectionFactory",
                "defaultJedisConnectionFactory");
        final int availableProcessors = Runtime.getRuntime().availableProcessors();
        containerDefinitionBuilder.addPropertyValue("taskExecutor",
                MDCThreadPoolTaskExecutor.builder()
                        .corePoolSize(availableProcessors * 2)
                        .maxPoolSize(availableProcessors * 2)
                        .keepAliveSeconds(300)
                        .namedThreadFactory("RedisMsgTask")
                        .build());
        context.getRegistry().registerBeanDefinition("redisMessageListenerContainer",
                containerDefinitionBuilder.getBeanDefinition());
        // 注册RedisMessageListenerAutoConfig
        BeanDefinitionBuilder autoConfigDefinitionBuilder =
                BeanDefinitionBuilder.genericBeanDefinition(RedisMessageListenerAutoConfig.class);
        context.getRegistry().registerBeanDefinition("redisMessageListenerAutoConfig",
                autoConfigDefinitionBuilder.getBeanDefinition());
    }

}

其中对是否开启Redis Cache的处理,原理参考Spring自定义注解定义AOP配置去xml,通过xml模板方式动态生成xml配置的AOP配置并注入到容器。
而对是否开启Redis Message的处理,则是定义了两个bean:RedisMessageListenerContainer、RedisMessageListenerAutoConfig来实现。也定义了注解来简化Redis Message的使用,有兴趣的也可以参考。
在@Configuration的class上引入@EnableRedisIntegration注解来启用自动集成:

@Configuration
@EnableRedisIntegration(redisCacheKeyPrefix = "RDS:CH", enableRedisMessageListener = true)
@PropertySource(value = {"classpath:redis.yml"}, factory = AutoPropertySourceFactory.class)
public class EnableRedisIntegrationConfiguration {
}

你可能感兴趣的:(Java,spring,java,redis)