首页 > 解决方案 > 尊重非@Primary @Bean 上的@Lazy 注释

问题描述

当Spring配置为使用不同的@Lazy方法返回标记为.@Bean@Bean@Primary

具体来说,我有一个带@Configuration注释的类,其中有几个@Bean方法都返回相同的接口。其中许多@Bean方法是@Lazy,因为它们联系应用程序当前可能未使用的外部服务。@Primarybean 不是@Lazy,因为它查看运行时配置以确定要返回的实现。

这是该配置类的一个人为示例,围绕一个虚构的ThingService接口:

@Configuration
@ComponentScan(basePackages = { "com.things" })
public class ThingConfiguration {

    @Bean
    public ThingOptions thingOptions() {
        ThingOptions options = new ThingOptions();
        options.sharing = true;
        return options;
    }

    @Primary
    @Bean
    public ThingService primaryThing(ThingOptions options, ApplicationContext context) {
        System.out.println("PrimaryThing -- Initialized");

        if (options.sharing) {
            return context.getBean("OurThing", ThingService.class);
        } else {
            return context.getBean("YourThing", ThingService.class);
        }
    }

    @Lazy
    @Bean(name = "YourThing")
    public ThingService yourThing() {
        System.out.println("YourThingService -- Initialized");
        return new YourThingService();
    }

    @Lazy
    @Bean(name = "OurThing")
    public ThingService ourThing() {
        System.out.println("OurThingService -- Initialized");
        return new OurThingService();
    }

}

然后我有一个@Component依赖于这个接口的@Primary注解将确保将正确的实现注入到对象中。这是该下游的示例@Component

@Component
public class ThingComponent {

    private final ThingService thingService;

    @Inject
    public ThingComponent(ThingService thingService) {
        this.thingService = thingService;
    }

}

@Lazy然后我建立了一个小测试,以确保@Primary所有这些都得到尊重。

public class ThingTest {

    @Test
    public void TestLazyAndPrimary() {

        // Arrange

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(ThingConfiguration.class);
        context.refresh();

        // Act

        ThingComponent component = context.getBean(ThingComponent.class);

        // Assert

        Assert.assertNotNull(component);
    }

}

然而,当我运行这个测试时,我发现它@Lazy被忽略了。以下文本被发送到控制台:

PrimaryThing -- Initialized
OurThingService -- Initialized
YourThingService -- Initialized

"YourThing" @Bean不应该被初始化,因为它是并且没有在运行时通过该方法@Lazy加载。ApplicationContext.getBean()然而,当它ThingComponent被解决时,它会导致在选择平均值之前@Bean返回一个实现的方法ThingService被水合。@Primary

如何在不导致所有带注释的非实现被水合@Primary的情况下,尊重接口的注释实现?@Primary@Lazy

标签: javaspring

解决方案


我一直无法阻止@Primary注释强制对所有返回该接口的方法进行急切的水合@Bean,即使这些信息似乎可用而无需从排他性的注释中强制水合。我通过对@Bean方法使用命名约定来解决这个问题。

具体来说,我改变了我的@Primary注释@Bean方法,包括name这样的:

@Configuration
@ComponentScan(basePackages = { "com.things" })
public class ThingConfiguration {

    // @Primary -- I don't want someone to accidentally use this without a @Qualifier!
    @Bean(name = "PrimaryThingService")
    public ThingService primaryThing(ThingOptions options, ApplicationContext context) {
        System.out.println("PrimaryThing -- Initialized");

        if (options.sharing) {
            return context.getBean("OurThing", ThingService.class);
        } else {
            return context.getBean("YourThing", ThingService.class);
        }
    }

    // ... the rest of the methods removed for clarity ...

}

然后我@QualifierThingService被注入的对象上放置了一个@Component这样的:

@Component
public class ThingComponent {

    private final ThingService thingService;

    @Inject
    public ThingComponent(@Qualifier("PrimaryThingService") ThingService thingService) {
        this.thingService = thingService;
    }

}

现在,当我重新运行测试时,我得到以下输出:

PrimaryThing -- Initialized
OurThingService -- Initialized

因此,这删除了注释,而不是使用遵循约定的@Primary命名,绕过 Spring 对非注释方法的过度水化。@Bean"Primary{Interface}"@Primary@Bean


推荐阅读