spring-projects / spring-boot

Spring Boot
https://spring.io/projects/spring-boot
Apache License 2.0
74.91k stars 40.62k forks source link

@Qualifier DI Error #31510

Closed cwangg897 closed 2 years ago

cwangg897 commented 2 years ago

I defined it via bean name and used @Qulifier annotation. But it sends the following message

@Configuration
public class RedisConfig {

    @Value("${spring.redis.session.host}")
    private String host;

    @Value("${spring.redis.session.port}")
    private int sessionPort;

    @Bean(name = "redisSessionConnectionFactory")
    public RedisConnectionFactory redisSessionConnectionFactory() {
        RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
        configuration.setHostName(host);
        configuration.setPort(sessionPort);
        return new LettuceConnectionFactory(configuration);
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(@Qualifier(value = "redisSessionConnectionFactory") RedisConnectionFactory redisSessionConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setConnectionFactory(redisSessionConnectionFactory);
        return redisTemplate;
    }
}
@Configuration
public class CacheConfig {

    @Value("${spring.redis.cache.host}")
    private String cacheHost;

    @Value("${spring.redis.cache.port}")
    private int cachePort;

    @Bean(name = "redisCacheConnectionFactory")
    public RedisConnectionFactory redisCacheConnectionFactory() {
        RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
        configuration.setHostName(cacheHost);
        configuration.setPort(cachePort);
        return new LettuceConnectionFactory(configuration);
    }

    @Bean
    public RedisCacheManager cacheManager(@Qualifier(value = "redisCacheConnectionFactory") RedisConnectionFactory redisCacheConnectionFactory) {

        RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig()
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

        Map<String, RedisCacheConfiguration> redisCacheConfigMap = new HashMap<>();
        redisCacheConfigMap.put("board", defaultConfig.entryTtl(Duration.ofHours(1)));
        redisCacheConfigMap.put("user", defaultConfig.entryTtl(Duration.ofSeconds(30L)));

        RedisCacheManager redisCacheManager = RedisCacheManager.builder(redisCacheConnectionFactory)
                .withInitialCacheConfigurations(redisCacheConfigMap)
                .build();

        return redisCacheManager;
    }
}

Error Message

Parameter 1 of method sessionRepositoryFilterRegistration in org.springframework.boot.autoconfigure.session.SessionRepositoryFilterConfiguration required a single bean, but 2 were found:
    - redisCacheConnectionFactory: defined by method 'redisCacheConnectionFactory' in class path resource [com/cwg/test/config/CacheConfig.class]
    - redisSessionConnectionFactory: defined by method 'redisSessionConnectionFactory' in class path resource [com/cwg/test/config/RedisConfig.class]

Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
snicoll commented 2 years ago

What version of Spring Boot are you using?

cwangg897 commented 2 years ago

What version of Spring Boot are you using?

Spring Boot 2.5.0 version

cwangg897 commented 2 years ago

What version of Spring Boot are you using?

Spring Boot 2.5.0 version

plugins {
    id 'org.springframework.boot' version '2.5.0'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
}

group = 'com.cwg'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.2'
    compileOnly 'org.projectlombok:lombok'
    runtimeOnly 'mysql:mysql-connector-java'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'

    // spring에서 redis에 대한 의존성
    implementation 'org.springframework.boot:spring-boot-starter-data-redis'
    // spring에서 redis를 session storage로 사용하기 위한 의존성
    implementation 'org.springframework.session:spring-session-data-redis'

}

tasks.named('test') {
    useJUnitPlatform()
}
mdeinum commented 2 years ago

The error is about an autoconfiguration class SessionRepositoryFilterConfiguration which expects a single RedisConnectionFactory or at least one marked as @Primary. The error isn't on the 2 custom @Configuration classes. The one named redisSessionConnectionFactory in RedisConfig probably needs to be marked with @Primary or the SessionRepositoryFilterConfiguration needs to be excluded from the auto-configuration when not needed.

cwangg897 commented 2 years ago

The error is about an autoconfiguration class SessionRepositoryFilterConfiguration which expects a single RedisConnectionFactory or at least one marked as @Primary. The error isn't on the 2 custom @Configuration classes. The one named redisSessionConnectionFactory in RedisConfig probably needs to be marked with @Primary or the SessionRepositoryFilterConfiguration needs to be excluded from the auto-configuration when not needed.

I use a separate Redis cache server and a separate session store, is there any other way?

snicoll commented 2 years ago

You need to mark the one that Spring Boot should use using @Primary. I wonder if failing is what we should be doing vs. changing the condition to @ConditionalOnSingleCandidate.

cwangg897 commented 2 years ago

Um Thank you Your Reply But I wish I could write it down in the Spring Boot documentation

mdeinum commented 2 years ago

You need to mark the one that Spring Boot should use using @Primary. I wonder if failing is what we should be doing vs. changing the condition to @ConditionalOnSingleCandidate.

That would be more consistent with other datastore driven configuration (at least for JDBC/JPA).

wilkinsona commented 2 years ago

I wonder if we should try to refine the action in the failure analysis as two of the three suggestions aren't possible due to the consumer being in Boot's own code. I've opened https://github.com/spring-projects/spring-boot/issues/31514.

wilkinsona commented 2 years ago

The injection point identified by the failure analysis is incorrect. The failure's actually occurring in Spring Session's RedisHttpSessionConfiguration.setRedisConnectionFactory(ObjectProvider<RedisConnectionFactory>, ObjectProvider<RedisConnectionFactory>) where it's calling getObject() on the second ObjectProvider<RedisConnectionFactory>. The first is annotated with @SpringSessionRedisConnectionFactory which opens another way to fix the problem.

@cwangg897 You can annotate your redisSessionConnectionFactory bean with @SpringSessionRedisConnectionFactory to indicate that it's the Redis connection factory that Spring Session should use. The annotation isn't mentioned in the Spring Session reference documentation. I've opened https://github.com/spring-projects/spring-session/issues/2102 for that.

cwangg897 commented 2 years ago

Thank you so much

wilkinsona commented 2 years ago

We can't fix this with @ConditionalOnSingleCandidate as the injection point is in Spring Session. https://github.com/spring-projects/spring-session/issues/2102 will hopefully result in @SpringSessionRedisConnectionFactory being documented. #31514 and #31515 are tracking some failure analysis improvements. I don't think there's anything more that we can do here.

cwangg897 commented 2 years ago

We can't fix this with @ConditionalOnSingleCandidate as the injection point is in Spring Session. spring-projects/spring-session#2102 will hopefully result in @SpringSessionRedisConnectionFactory being documented. #31514 and #31515 are tracking some failure analysis improvements. I don't think there's anything more that we can do here.

If @SpringSessionRedisConnectionFactory is written as a comment in the documentation, could you please let me know? If it is written in the document, other people can get help because of me, so I want to keep it because I think I contributed even 1%. Thank you, have a good day

wilkinsona commented 2 years ago

If @SpringSessionRedisConnectionFactory is written as a comment in the documentation, could you please let me know?

Please subscribe to https://github.com/spring-projects/spring-session/issues/2102 for that.