Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring boot 2 migration from Spring boot 1.5 issue with redis

Tags:

i need to change Redis code so it will work with new SpringBoot 2.0.3, currently when starting Tomcat 9.0.12 (not starting SpringBoot as Jar - cause of business needs) i get following error:

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

My dependecies about Redis in pom are:

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

Here's redis config:

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

Here's configuration class:

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

Here's service class that is mentioned in the stack trace:

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

As you can see i used stuff like but redis does not work:

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

EDIT:

Regarding Boris answer: The only stuff that I can find (in whole project) with phrases like jedis or Jedis are pom dependencies. When i remove them the output changes to:

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

I could not find such a thing like JedisConnectionConfiguration to delete it.

After commenting the dependencies in pom I could not find phrase jedis in External Libraries.

I saw that in version withspring-boot-starter-parent (in the main parent pom) in External Libraries i have only spring-data-redis 2.0.8.

like image 427
tryingHard Avatar asked Oct 19 '18 12:10

tryingHard


People also ask

What does spring boot properties Migrator do?

In this article, we explored the spring-boot-properties-migrator. It's a handy tool that scans our properties file and gives easily actionable scan reports.


1 Answers

You have placed @ConditionalOnMissingBean annotation that only matches when no beans of RedisConnectionFactory classes are already contained in the BeanFactory. Looking at the log we can see that you are trying to configure both the Lettuce and Jedis client libraries:

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

But you should be using only one at a time. Delete JedisConnectionConfiguration and remove the jedis dependency if you want to use Lettuce and vice versa.

However I recommend to use a Redis Starter instead:

1. Add spring-boot-starter-data-redis maven dependency

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

2. Remove jedis and spring-data-redis maven dependencies

3. Remove @Qualifier annotations since you don't need more control over the autowiring selection process

4. Set the connection factory by using setConnectionFactory()

redisTemplate.setConnectionFactory(connectionFactory);

5. Use Redis Starter offered basic auto-configuration

Remove redisConnectionFactory() method.

Inject an auto-configured RedisConnectionFactory or RedisTemplate instance as you would any other Spring Bean in order to connect to Redis.

6. Use Spring Boot Redis properties

Spring Boot uses these default Redis properties which you can overwrite in the properties file:

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

See here for more details.

like image 186
Boris Avatar answered Oct 04 '22 01:10

Boris