从Spring Boot 1.5迁移到Redis的Spring Boot 2迁移

时间:2018-10-19 12:03:12

标签: java spring spring-boot tomcat redis

我需要更改Redis代码,以便它可以与新的SpringBoot 2.0.3一起使用,当前在启动Tomcat 9.0.12时(未以Jar形式启动SpringBoot-业务需求),我得到以下错误:

 ***************************
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]]
(...)

我对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>

这里是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;
    }
}

这是配置类:

@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;
    }
}

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

@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();
    }
}

如您所见,我使用了类似的东西,但是redis无法正常工作:

@Bean(value ="vehicleTemplate")
@Qualifier("vehicleTemplate")

编辑:

关于Boris答案: 在整个项目中,我只能找到jedisJedis这样的短语,这是pom依赖项。当我删除它们时,输出更改为:

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

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

注释了pom中的依赖项后,我在外部库中找不到短语jedis

我看到在spring-boot-starter-parent中带有External Libraries(在主父pom中)的版本中,我只有spring-data-redis 2.0.8。

1 个答案:

答案 0 :(得分:3)

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

- Bean method 'redisConnectionFactory' in 'JedisConnectionConfiguration' not loaded because @ConditionalOnMissingBean
- Bean method 'redisConnectionFactory' in 'LettuceConnectionConfiguration' not loaded because @ConditionalOnMissingBean

但是您一次只能使用一个。 如果要使用生菜,请删除JedisConnectionConfiguration并删除jedis依赖性。

但是我建议改为使用Redis Starter:

1。。添加spring-boot-starter-data-redis maven依赖项

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2。。删除jedisspring-data-redis maven依赖项

3。。删除@Qualifier批注,因为您无需进一步控制autowiring selection process

4。。使用setConnectionFactory()

设置连接工厂。
redisTemplate.setConnectionFactory(connectionFactory);

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

删除redisConnectionFactory()方法。

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

6。。使用Spring Boot Redis属性

Spring Boot使用以下默认Redis属性,您可以在属性文件中覆盖它们:

spring.redis.host=localhost
spring.redis.port=6379

有关更多详细信息,请参见here