4

I have noticed that even classes not annotated with @Component are registered into the Spring context when they are declared in the @Import annotation.

@Configuration
@Import({MyBean.class})
class MyConfig {
    @Bean
    Object object(MyBean myBean) { // this works
        return new Object();
    }
}

class MyBean {} // no annotation here

This behavior is not very clear from the @Import documentation.

Is this wanted? Is it documented somewhere? Are there any differences between importing a non-@component and a @Component class?

The documentation says:

Allows for importing @Configuration classes, ImportSelector and ImportBeanDefinitionRegistrar implementations, as well as regular component classes

What is a "regular component class"? Any class or a class annotated with @Component?

ttulka
  • 10,309
  • 7
  • 41
  • 52

3 Answers3

2

Summary

The @Import annotation is designed to turn the referenced classes into bean definitions. If a "regular" component is imported with @Import, there is usually no reason to annotate it with @Component as this annotation does not prescribe any kind of behavior for the resulting bean. @Component does not primarily mark classes as Spring beans but as classes to be picked up by classpath scanning.

Usually, the imported classes are configuration classes that are annotated with @Configuration. And the configuration classes will also be included in the final application context as beans. The word "regular" in this context is meant to refer to beans (or components) that are not also configuration classes or other kinds of beans that add themselves other bean definitions to the application context. The word "regular" here could also be replaced with e.g. "normal" or "plain-vanilla".

In fact, configuration classes that are imported with @Import also don't need to be annotated with @Configuration. But in this case, there is a functional difference as the beans from the @Bean annotated factory methods will be produced in "bean lite" mode if the annotation @Configuration is missing.

Details

The annotation @Component does not describe any kind of behavior of the beans that are instances of the annotated class. It is really just a marker for classes. If classpath scanning is used to configure the application context, then any class annotated with @Component will be turned into a bean definition that will yield a bean that is an instance of the annotated class.

This is also stated in the javadoc of the annotation:

Indicates that an annotated class is a "component". Such classes are considered as candidates for auto-detection when using annotation-based configuration and classpath scanning.

Today, annotation-based configuration and classpath scanning are the dominant way today to configure Spring application contexts. This wasn't true in the past, which also saw e.g. a lot of XML configuration. In fact, the Spring framework is older than Java 5, which was the first Java that introduced annotations to the language.

If annotation-based configuration is used but not classpath scanning, the annotation @Import allows to create references from central configuration classes to other configuration classes or in particular "regular" component classes. This is also how the reference documentation talks about this feature:

As of Spring Framework 4.2, @Import also supports references to regular component classes, analogous to the AnnotationConfigApplicationContext.register method. This is particularly useful if you want to avoid component scanning, by using a few configuration classes as entry points to explicitly define all your components.

So if classpath scanning is not used, then there is basically no point in marking classes with @Component as it does not prescribe any behavior. This is not only true for "regular" beans that are imported with @Import but also for beans that come from <bean> elements in an XML configuration or functionally registered beans.

Before Spring 4.2 the annotation @Import could only import non-regular beans like e.g. @Configuration classes. This was because @Import can be thought of as the equivalent of the <import> element in XML configuration, which is used to include e.g. other XML files that then again contain <bean> elements.

The word "regular" in this context refers to classes that I would normally annotate with @Component if classpath scanning were used. The word is only used here to differentiate such classes from "non-regular" components like @Configuration classes, ImportSelector and ImportBeanDefinitionRegistrar implementations.

Henning
  • 3,055
  • 6
  • 30
0

It states in java docs to use ImportResource for importing classes which aren't @Configuration

If XML or other non-@Configuration bean definition resources need to be imported, use the @ImportResource annotation instead.

Ori Marko
  • 56,308
  • 23
  • 131
  • 233
  • So the answer is: With "regular component class" is meant any class. Right? – ttulka Dec 09 '19 at 12:17
  • @ttulka defacto `@Configuration` can hold non managed beans (any class), but the practice is to hold `@Component` classes – Ori Marko Dec 09 '19 at 12:27
0

While this maybe "works":

@Configuration
@Import({MyBean.class})
class MyConfig {
    @Bean
    Object object(MyBean myBean) { // this works
        return new Object();
    }
}

class MyBean {} // no annotation here

This (bean) won't (unless spring can "resolve arguments"):

package io.external;
//no spring annotations
public class MyBean {
    public MyBean(String gotcha) { // ! ...
      // just a demo
    }
} 

The context will fail to start with:

APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of constructor in io.external.MyBean required a bean of type 'java.lang.String' that could not be found.


Action:

Consider defining a bean of type 'java.lang.String' in your configuration.

By "regular component classes" spring documentation must refer to classes annotated:

  • @Component
  • @Repository (@NoRepositoryBean!;)
  • @Service
  • even @Configuration (, @SpringBootApplication, @TestConfiguration(?), ...) <- most typical and documented for @Import
  • and their descendants/inheritors ...

... as described by beans-stereotype-annotations.


On the other hand, when we try without @Import({MyBean.class}), it also fails, due to:

Parameter 0 of method object in com.example.... required a bean of type 'io.external.MyBean' that could not be found.

This, above observations and further tests (with spring non-/resolvable constructors) make me conclude, that:

  • @Import(MyBean.class) (on a "non component class") behaves in the same kind as @Bean MyBean myBean(...) { return new MyBean(...)}.
  • Whereas the instantiation of MyBean is implemented by SimpleInstantiationStrategy ... (currently) so:
    • When MyBean has a (i.e. no args) "default constructor", this will be used.
    • Otherwise, when there is:
      • No unique constructor, resolution/instantiation fails with No default constructor found.
      • A unique (non default) constructor, spring will try to resolve (auto-wire) constructor arguments ... with two possible outcomes (success or not).

Please experiment with different constructor combinations (& order):

package io.external;

public class MyBean {
    //public MyBean(String gotcha) {
    //    System.err.println("a");
    //}

    public MyBean(org.springframework.core.env.Environment fIoc) {
        System.err.println("b");
    }

    //public MyBean() { // <- this will pass!
    //    System.err.println("c");
    //}
}

For details and current documentation, please refer to:

xerx593
  • 12,237
  • 5
  • 33
  • 64