Spring Boot 2 从 Spring Boot 1.5 迁移到 redis 问题

try*_*ard 5 java spring tomcat redis spring-boot

我需要更改 Redis 代码,以便它可以与新的 SpringBoot 2.0.3 一起使用,目前在启动 Tomcat 9.0.12 时(不是将 SpringBoot 作为 Jar 启动 - 业务需求的原因)我收到以下错误:

 ***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of method vehicleHistoryCacheManager in somePath.config.UfCacheConfig 
required a bean of type 'org.springframework.data.redis.connection.RedisConnectionFactory' that could not be found.
    - Bean method 'redisConnectionFactory' in 'JedisConnectionConfiguration' not loaded because @ConditionalOnMissingBean 
    (types: org.springframework.data.redis.connection.RedisConnectionFactory; SearchStrategy: all) 
    beans of type 'org.springframework.data.redis.connection.RedisConnectionFactory' redisConnectionFactory
    - Bean method 'redisConnectionFactory' in 'LettuceConnectionConfiguration' not loaded because @ConditionalOnMissingBean 
    (types: org.springframework.data.redis.connection.RedisConnectionFactory; SearchStrategy: all) 
    found beans of type 'org.springframework.data.redis.connection.RedisConnectionFactory' redisConnectionFactory


Action:

Consider revisiting the conditions above or defining a bean of type 'org.springframework.data.redis.connection.RedisConnectionFactory' in your configuration.

19-Oct-2018 13:43:22.142 SEVERE [RMI TCP Connection(3)-127.0.0.1] org.apache.catalina.core.ContainerBase.addChildInternal ContainerBase.addChild: start: 
 org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/motoPolicy]]
(...)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'technicalController' 
defined in file [somePath\TecController.class]: 
Unsatisfied dependency expressed through constructor parameter 0; 
nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: 
Error creating bean with name 'ufCacheServiceImpl' 
defined in URL [jar:file:/someName.jar!/somePath/UfCacheServiceImpl.class]: 
Unsatisfied dependency expressed through constructor parameter 0; 
nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: 
Error creating bean with name 'vehicleHistoryCacheManager' defined in class path resource 
[somePath/config/UfCacheConfig.class]: 
Unsatisfied dependency expressed through method 'vehicleHistoryCacheManager' parameter 0; 
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: 
No qualifying bean of type 'org.springframework.data.redis.connection.RedisConnectionFactory' available: 
expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier(value=vehicleTemplate)}
(...)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'ufgCacheServiceImpl' 
defined in URL [jar:file:/uf.jar!/somePath/UfgCacheServiceImpl.class]: 
Unsatisfied dependency expressed through constructor parameter 0; 
nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException:
 Error creating bean with name 'vehicleHistoryCacheManager' defined in class path resource
 [somePath/config/UfCacheConfig.class]: 
 Unsatisfied dependency expressed through method 'vehicleHistoryCacheManager' parameter 0; 
 nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: 
 No qualifying bean of type 'org.springframework.data.redis.connection.RedisConnectionFactory' available: 
 expected at least 1 bean which qualifies as autowire candidate. 
 Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier(value=vehicleTemplate)}
(...)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: 
Error creating bean with name 'vehicleHistoryCacheManager' defined in class path resource 
[somePath/config/UfCacheConfig.class]: 
Unsatisfied dependency expressed through method 'vehicleHistoryCacheManager' parameter 0; 
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: 
No qualifying bean of type 'org.springframework.data.redis.connection.RedisConnectionFactory' available: 
expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier(value=vehicleTemplate)}
(...)
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: 
No qualifying bean of type 'org.springframework.data.redis.connection.RedisConnectionFactory' available: 
expected at least 1 bean which qualifies as autowire candidate. 
Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier(value=vehicleTemplate)}
(...)

19-Oct-2018 13:43:22.162 SEVERE [RMI TCP Connection(3)-127.0.0.1] org.apache.tomcat.util.modeler.BaseModelMBean.invoke Exception invoking method manageApp
 java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: 
 Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/motoPolicy]]
(...)
[2018-10-19 01:43:22,176] Artifact motoPolicy-web:war exploded: Error during artifact deployment. See server log for details.
(...)

19-Oct-2018 13:43:22.162 SEVERE [RMI TCP Connection(3)-127.0.0.1] org.apache.tomcat.util.modeler.BaseModelMBean.invoke Exception invoking method createStandardContext
 javax.management.RuntimeOperationsException: Exception invoking method manageApp
(...)
Caused by: java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: 
Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/motoPolicy]]
(...)
Run Code Online (Sandbox Code Playgroud)

我在 pom 中对 Redis 的依赖是:

    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-redis</artifactId>
        <version>2.0.3.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>io.lettuce</groupId>
        <artifactId>lettuce-core</artifactId>
        <version>5.0.3.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>2.9.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
        <version>2.6.0</version>
    </dependency>
Run Code Online (Sandbox Code Playgroud)

这是redis配置:

@Configuration
class RedisConfig {

    @Value("${ufg.redis.host}") // 127.0.0.1
    private String hostName;

    @Value("${ufg.redis.port}") // 6379
    private int port;

    @Bean
    LettuceConnectionFactory redisConnectionFactory() {
        LettuceConnectionFactory redisConnectionFactory = new LettuceConnectionFactory();
        redisConnectionFactory.setHostName(hostName);
        redisConnectionFactory.setPort(port);
        return redisConnectionFactory;
    }

    @Bean
    ObjectMapper redisObjectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.registerModule(new Jdk8Module());
        objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+1:00"));
        objectMapper.setDateFormat(new ISO8601DateFormat());
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        return objectMapper;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是配置类:

@Slf4j
@Configuration
@EnableCaching
public class UfCacheConfig {
    public static final String VEHICLE_HISTORY_CACHE_MANAGER = "vehicleHistoryCacheManager";
    public static final String VEHICLE_HISTORY_CACHE = "vehicleHistoryCache";
    public static final String VEHICLE_GENERATOR_NAME = "vehicleKeyGenerator";
    public static final String PERSON_HISTORY_CACHE_MANAGER = "personHistoryCacheManager";
    public static final String PERSON_HISTORY_CACHE = "personHistoryCache";
    public static final String PERSON_GENERATOR_NAME = "personKeyGenerator";

    @Value("${ufg.cache.expiration.validity.minutes}")
    private int expirationValidityMinutes;

    @Bean(value ="vehicleTemplate")
    public RedisTemplate<String, GetVehicleInsuranceHistoryResponse> vehicleTemplate(RedisConnectionFactory redisConnectionFactory,
                                                                                     @Qualifier("redisObjectMapper") ObjectMapper objectMapper) {
        RedisTemplate<String, GetVehicleInsuranceHistoryResponse> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        return redisTemplate;
    }

    @Primary
    @Bean(name = VEHICLE_HISTORY_CACHE_MANAGER)
    public CacheManager vehicleHistoryCacheManager(@Qualifier("vehicleTemplate") RedisConnectionFactory redisConnectionFactory) {
        Duration expiration = Duration.ofSeconds(expirationValidityMinutes * 60);
        return RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig().entryTtl(expiration)).build();
    }

    @Bean(value ="personTemplate")
    public RedisTemplate<String, GetPersonInsuranceHistoryResponse> redisTemplate(RedisConnectionFactory redisConnectionFactory,
                                                                                  @Qualifier("redisObjectMapper") ObjectMapper objectMapper) {
        RedisTemplate<String, GetPersonInsuranceHistoryResponse> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        return redisTemplate;
    }

    @Bean(name = PERSON_HISTORY_CACHE_MANAGER)
    public CacheManager personHistoryCacheManager(@Qualifier("personTemplate") RedisConnectionFactory redisConnectionFactory) {
        Duration expiration = Duration.ofSeconds(expirationValidityMinutes * 60);
        return RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig().entryTtl(expiration)).build();
    }

    @Bean(name = VEHICLE_GENERATOR_NAME)
    public KeyGenerator vehicleKeyGenerator() {
        return (o, method, objects) -> new VehicleKeyGenerator((GetVehicleInsuranceHistoryRequest) objects[0]).generate();
    }

    @Bean(name = PERSON_GENERATOR_NAME)
    public KeyGenerator personKeyGenerator() {
        return (o, method, objects) -> new PersonKeyGenerator((GetPersonInsuranceHistoryRequest) objects[0]).generate();
    }

    private static <T> RedisTemplate<String, T> createTemplate(Class<T> clazz, RedisConnectionFactory redisConnectionFactory, ObjectMapper objectMapper) {
        RedisTemplate<String, T> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        Jackson2JsonRedisSerializer<T> serializer = new Jackson2JsonRedisSerializer<>(clazz);
        serializer.setObjectMapper(objectMapper);
        redisTemplate.setValueSerializer(serializer);
        return redisTemplate;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是堆栈跟踪中提到的服务类:

@Slf4j
@Service
public class UfCacheServiceImpl implements UfCacheService {
    private CacheManager vehicleCacheManager;
    private CacheManager personCacheManager;

    @Autowired
    public UfCacheServiceImpl(@Qualifier(VEHICLE_HISTORY_CACHE_MANAGER) CacheManager vehicleCacheManager,
                               @Qualifier(PERSON_HISTORY_CACHE_MANAGER) CacheManager personCacheManager) {
        this.vehicleCacheManager = vehicleCacheManager;
        this.personCacheManager = personCacheManager;
    }

    @Override
    public void clear() {
        log.info("Clearing ufg cache");
        vehicleCacheManager.getCache(VEHICLE_HISTORY_CACHE).clear();
        personCacheManager.getCache(PERSON_HISTORY_CACHE).clear();
    }
}
Run Code Online (Sandbox Code Playgroud)

如您所见,我使用了类似的东西,但 redis 不起作用:

@Bean(value ="vehicleTemplate")
@Qualifier("vehicleTemplate")
Run Code Online (Sandbox Code Playgroud)

编辑:

关于Boris答案:我能找到(在整个项目中)的唯一内容是 pom 依赖项jedisJedispom 依赖项。当我删除它们时,输出更改为:

Parameter 0 of method vehicleHistoryCacheManager in somePackages.config.UfCacheConfig required a bean of type 'org.springframework.data.redis.connection.RedisConnectionFactory' that could not be found.
- Bean method 'redisConnectionFactory' not loaded because @ConditionalOnClass did not find required class 'redis.clients.jedis.Jedis'
- Bean method 'redisConnectionFactory' in 'LettuceConnectionConfiguration' not loaded because @ConditionalOnMissingBean (types: org.springframework.data.redis.connection.RedisConnectionFactory; 
SearchStrategy: all) found beans of type 'org.springframework.data.redis.connection.RedisConnectionFactory' redisConnectionFactory
Run Code Online (Sandbox Code Playgroud)

我找不到这样的东西想JedisConnectionConfiguration删除它。

在 pom 中评论依赖项后,我jedis在外部库中找不到短语。

spring-boot-starter-parentExternal Libraries我只有spring-data-redis2.0.8 的版本中看到了(在主要的父 pom 中)。

Bor*_*ris 5

您已放置@ConditionalOnMissingBean注释,该注释仅RedisConnectionFactory在 BeanFactory 中尚未包含类的 Bean 时才匹配。查看日志,我们可以看到您正在尝试配置 Lettuce 和 Jedis 客户端库:

- Bean method 'redisConnectionFactory' in 'JedisConnectionConfiguration' not loaded because @ConditionalOnMissingBean
- Bean method 'redisConnectionFactory' in 'LettuceConnectionConfiguration' not loaded because @ConditionalOnMissingBean
Run Code Online (Sandbox Code Playgroud)

但您一次应该只使用一个。删除JedisConnectionConfiguration并移除jedis依赖项,反之亦然。

不过我建议改用 Redis Starter:

1.添加spring-boot-starter-data-redismaven依赖

- Bean method 'redisConnectionFactory' in 'JedisConnectionConfiguration' not loaded because @ConditionalOnMissingBean
- Bean method 'redisConnectionFactory' in 'LettuceConnectionConfiguration' not loaded because @ConditionalOnMissingBean
Run Code Online (Sandbox Code Playgroud)

2.删除jedismavenspring-data-redis依赖

3.删除@​​Qualifier注释,因为您不需要对自动装配选择过程进行更多控制

4.使用setConnectionFactory()设置连接工厂

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Run Code Online (Sandbox Code Playgroud)

5.使用Redis Starter提供的基本自动配置

消除redisConnectionFactory()方法。

像注入任何其他 Spring Bean 一样注入自动配置RedisConnectionFactoryRedisTemplate实例以连接到 Redis。

6.使用Spring Boot Redis属性

Spring Boot 使用这些默认的 Redis 属性,您可以在属性文件中覆盖这些属性:

spring.redis.host=localhost
spring.redis.port=6379
Run Code Online (Sandbox Code Playgroud)

请参阅此处了解更多详细信息。