jimmayhem
jimmayhem

Reputation: 405

How to forbid Spring Boot injecting both fields of the same type with the same bean instance?

I have a class with two final fields of the same type, and I need to make second field injected with null if property props.enabled in application.yml is false. However, if it's false Spring Boot injects both fields with the same bean instance.

How to forbid Spring Boot injecting both fields of the same type with the same bean instance?

@AllArgsConstructor
public class MySettings {
    private int val;
}

My configuration class

@Configuration
public class MySpringConfig {

    @Bean
    public MySettings settingsA() { 
        return new MySettings(1);    
    } 
    
    @Bean
    @ConditionalOnProperty(prefix = "props", name = "enabled")
    public MySettings settingsB() {
        return new MySettings(2);
    }
}

And this is my class

@Component
@RequiredArgsConstructor
public class MyClass {
    
    private final MySettings settingsA; // MySettings(1)
    private final MySettings settingsB; // also MySettings(1) but must be null if props.enabled=false

    @Value("${props.enabled}")
    private boolean enabled;

    ...
}

This is part of the real project, so I have very little space to deviate from

UPDATE

I came up with solution of constructor injection but the code starts to look ugly

@Component
public class MyClass {

    private final MySettings settingsA;
    private final MySettings settingsB;

    private boolean enabled;

    public MyClass(MySettings settingsA, 
                   @Nullable @Qualifier("settingsB") MySettings settingsB, 
                   @Value("${props.enabled}") boolean enabled) {
        this.settingsA = settingsA;
        this.settingsB = settingsB;
        this.enabled = enabled;
    }
    ...

Upvotes: 1

Views: 400

Answers (1)

João Dias
João Dias

Reputation: 17460

You need a qualifier to tell Spring which bean to inject:

@Configuration
public class MySpringConfig {

    @Bean(name = "SettingsA")
    public MySettings settingsA() { 
        return new MySettings(1);    
    } 
    
    @Bean(name = "SettingsB")
    @ConditionalOnProperty(prefix = "props", name = "enabled")
    public MySettings settingsB() {
        return new MySettings(2);
    }
}

And now in MyClass:

@Component
public class MyClass {
    
    private final MySettings settingsA;
    private final MySettings settingsB;

    @Value("${props.enabled}")
    private boolean enabled;

    public MyClass(@Qualifier("SettingsA") MySettings settingsA, @Qualifier("SettingsB") MySettings settingsB) {
        this.settingsA = settingsA;
        this.settingsB = settingsB;
    }

    ...
}

However, since one of those Beans might not be available I believe you need to not include it in the constructor injection otherwise you will get an error. In that case you need to do the following:

@Component
public class MyClass {

    @Qualifier("SettingsA") 
    @Autowired
    private MySettings settingsA;

    @Qualifier("SettingsB")
    @Autowired(required = false)
    private MySettings settingsB;

    @Value("${props.enabled}")
    private boolean enabled;

    ...
}

Upvotes: 1

Related Questions