@Configuration 是强制性的吗?

Is @Configuration mandatory?

在某些情况下,我有一个无法用 @Configuration 注释的 Spring 配置 class。一开始我以为不行,因为Spring配置必须有@Configuration注解。然而,在做了一些测试之后,我才意识到 @Configuration 不是强制性的。

例如,这是配置 Java class 没有 @Configuration:

public class NotAnnotatedConfiguration {

    @Bean
    public DemoService demoService() {
        return new DemoService();
    }

}

我尝试在以下测试中加载此配置:

@ContextConfiguration(classes = NotAnnotatedConfiguration.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class NotAnnotatedConfigurationTest {

    @Autowired DemoService demoService;

    @Test
    public void load() {
        assertNotNull(this.demoService);
    }

}

而且有效!我在实际应用中得到了相同的结果,而不仅仅是在测试中。

根据我的测试,我认为只有当你想让class被Spring扫描时才需要@Configuration,否则你可以给Spring,它将加载并扫描其中定义的所有 bean。然而,AnnotationConfigApplicationContext 的 javadoc 对我来说似乎不太清楚(可能我误解了什么):

/**
 * Create a new AnnotationConfigApplicationContext, deriving bean definitions
 * from the given annotated classes and automatically refreshing the context.
 * @param annotatedClasses one or more annotated classes,
 * e.g. {@link Configuration @Configuration} classes
 */
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
    this();
    register(annotatedClasses);
    refresh();
}

我已经用我的测试创建了一个 github 存储库来分享我的结论:https://github.com/itelleria/configuration-annotation

问题是,尽管进行了我的测试,是否允许不使用 @Configuration 注释 Spring 中的 java 配置 class?

提前致谢。

你的情况可能不需要Configuration,但首先你可以遵循注释class作为设计的约定,正如你提到的,当有人使用组件扫描时,就会成为一个问题,因为您需要添加 @Component 或其他元注释:

@Configuration is meta-annotated with @Component, therefore @Configuration classes are candidates for component scanning

您还可以在配置中包含组件扫描:

@Configuration classes may not only be bootstrapped using component scanning, but may also themselves configure component scanning using the @ComponentScan annotation:

 @Configuration
 @ComponentScan("com.acme.app.services")
 public class AppConfig {
     // various @Bean definitions ...
 }

我认为您是对的,Spring 将加载配置 class 并创建实例,但不会将实例视为 beans。含义:使用 DemoService 的其他服务将为每次使用创建多个实例,而不是作为一个单独的实例,当创建为 bean 时,这将是默认范围。

public class DemoServiceUsage {

    DemoService demoService;

    public DemoServiceUsage(DemoService demoService) {
        this.demoService = demoService;
    }
}

public class NotAnnotatedConfiguration {
    @Bean
    public DemoService demoService() {
        DemoService demoService = new DemoService();
        System.out.println("demoService " + demoService.hashCode());
        return demoService;
    }

    @Bean
    public DemoServiceUsage demoServiceUsage1() {
        return new DemoServiceUsage(demoService());
    }

    @Bean
    public DemoServiceUsage demoServiceUsage2() {
        return new DemoServiceUsage(demoService());
    }
}

@Configuration
@Import({
    NotAnnotatedConfiguration.class
})
public class ApplicationConfig {
}

@ContextConfiguration(classes = ApplicationConfiguration.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class NotAnnotatedConfigurationTest {

    @Autowired
    DemoServiceUsage demoServiceUsage1;

    @Autowired
    DemoServiceUsage demoServiceUsage2;

    @Test
    public void load() {
        assertNotNull(this.demoServiceUsage1);
        assertNotNull(this.demoServiceUsage2);
    }
}

在这里你会看到你得到了多个具有不同 hashCodes 的 demoService 输出。如果 demoService 是一个 bean,我们应该只看到一个实例,因为它应该有一个单例范围。

只是引用 Spring 文档所说的

Full @Configuration vs “lite” @Bean mode?

When @Bean methods are declared within classes that are not annotated with @Configuration, they are referred to as being processed in a “lite” mode. Bean methods declared in a @Component or even in a plain old class are considered to be “lite”, with a different primary purpose of the containing class and a @Bean method being a sort of bonus there. For example, service components may expose management views to the container through an additional @Bean method on each applicable component class. In such scenarios, @Bean methods are a general-purpose factory method mechanism.

Unlike full @Configuration, lite @Bean methods cannot declare inter-bean dependencies. Instead, they operate on their containing component’s internal state and, optionally, on arguments that they may declare. Such a @Bean method should therefore not invoke other @Bean methods. Each such method is literally only a factory method for a particular bean reference, without any special runtime semantics. The positive side-effect here is that no CGLIB subclassing has to be applied at runtime, so there are no limitations in terms of class design (that is, the containing class may be final and so forth).

In common scenarios, @Bean methods are to be declared within @Configuration classes, ensuring that “full” mode is always used and that cross-method references therefore get redirected to the container’s lifecycle management. This prevents the same @Bean method from accidentally being invoked through a regular Java call, which helps to reduce subtle bugs that can be hard to track down when operating in “lite” mode.

有关详细信息,请访问 Basic Concepts: @Bean and @Configuration