preface

In the process of using SpringBoot, the @enablexxx annotation is often used, and along with an @import annotation, this time to look at the @import source code

The body of the

Let’s copy some of its English notes:

/** * // Indicates one or more <em>component classes</em> to import &mdash; /** * / Indicates one or more <em>component classes</em> to import &mdash; typically * {@link Configuration @Configuration} classes. * * // provides the same functionality as the import tag in Spring's XML. You can import the configuration, ordinary, ImportSelector or ImportBeanDefinitionRegistrar interface implementation class * < p > Provides functionality equivalent to the {@code <import/>} element in Spring XML.
 * Allows for importing {@code @Configuration} classes, {@link ImportSelector} and
 * {@link ImportBeanDefinitionRegistrar} implementations, as well as regular component
 * classes (as of 4.2; analogous to {@link AnnotationConfigApplicationContext#register}).
 *
 * <p>{@code @Bean} definitions declared in imported {@code @Configuration} classes should be
 * accessed by using {@link org.springframework.beans.factory.annotation.Autowired @Autowired}
 * injection. Either the bean itself can be autowired, or the configuration class instance
 * declaring the bean can be autowired. The latter approach allows for explicit, IDE-friendly
 * navigation between {@code @Configuration} class methods.
 *
 * <p>May be declared at the class level or as a meta-annotation.
 *
 * <p>If XML or other non-{@code @Configuration} bean definition resources need to be
 * imported, use the {@link ImportResource @ImportResource} annotation instead.
 *
 */

Copy the code

It probably means:

  1. You can import a class or classes into a container, just as the import tag does in XML
  2. Can be introduced into ordinary classes, Configuration class, ImportSelector or ImportBeanDefinitionRegistrar interface implementation class
  3. Import a configuration file that can be injected by other beans and other beans can be injected into the configuration.

example

  1. Create a newspringbootproject
  2. Create the same level of packages on top of the startup class. For example, the startup classtest.javaina.b.c.dDown here, so incPackage buildePackage,eanddIn the same level of directory.

The reason for the second point is that SpringBoot scans for files in the same directory as the boot class. If you’re in the same directory, just use @componment. So I think @import is importing classes that are not in the same directory as the project.

Importing a common class

Create two new classes in package E, TestImportA and TestImportB:


    public class TestImportA {
        public void test(a){
            System.out.println("TestImportB run"); }}public class TestImportB {
        public void test(a){
            System.out.println("TestImportB run"); }}Copy the code

Then add the following to the startup class:


    @SpringBootApplication
    @Import({TestImportA.class, TestImportB.class})
    public class SpringbootOneApplication {
        public static void main(String[] args) { ConfigurableApplicationContext run = SpringApplication.run(SpringbootOneApplication.class, args); TestImportB testImportB = run.getBean(TestImportB.class); testImportB.test(); }}Copy the code

In this way, TestImportA. Class, testimPortb. class can be scanned through the @import annotation and put into the container.

Import configuration class

Create a configuration class TestImportConfig in package E


    @Configuration
    public class TestImportConfig {
        @Bean
        public TestImportA testImportA(a) {
            return new TestImportA();
        }
    
        @Bean
        public TestImportB testImportB(a) {
            return newTestImportB(); }}Copy the code

Then add the following to the startup class:


    @SpringBootApplication
    @Import(TestImportConfig.class)
    public class SpringbootOneApplication {
        public static void main(String[] args) { ConfigurableApplicationContext run = SpringApplication.run(SpringbootOneApplication.class, args); TestImportB testImportB = run.getBean(TestImportB.class); testImportB.test(); }}Copy the code

The effect is the same as the first one.

Import the implementation class of ImportSelector

  1. Create a new annotation class TestImport

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ImportSelectorImpl.class) // Note that the ImportSelectorImpl class is created with this part of annotation
public @interface TestImport {
    String value(a) default "value";
    boolean flag(a) default false;
}

Copy the code

Explain why you want to create a new annotation class. ImportSelector interface String[] selectImports(AnnotationMetadata importingClassMetadata). This interface receives an AnnotationMetadata This class contains the annotation information where the @import annotation is located (class, interface, enumeration), so that we can get the required information when starting the configuration. The most typical is the @enablexxx annotation. Import the ImportSelector implementation class in @enablexxx using @import, so you can get some information about the @enablexxx annotation.

  1. Create a new ImportSelectorImpl to implement the ImportSelector interface

    public class ImportSelectorImpl implements ImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            // Get the annotation collection
            MergedAnnotations annotations = importingClassMetadata.getAnnotations();
            // Get the TestImport annotation
            MergedAnnotation<TestImport> testImportMergedAnnotation = annotations.get(TestImport.class);
            / / print
            System.out.println("= = = ="+testImportMergedAnnotation.getString("value"));
            return new String[]{"a.b.c.e.TestImportA"."a.b.c.e.TestImportB"}; }}Copy the code
  1. Use the @tESTIMPort annotation on the startup class instead

    @SpringBootApplication
    @TestImport
    public class SpringbootOneApplication {
        public static void main(String[] args) { ConfigurableApplicationContext run = SpringApplication.run(SpringbootOneApplication.class, args); TestImportB testImportB = run.getBean(TestImportB.class); testImportB.test(); }}Copy the code

Import ImportBeanDefinitionRegistrar implementation class

The usage and ImportSelector usage is the same, there is not all shows, directly on the TestImport annotations into @ Import (ImportBeanDefinitionRegistrarImpl. Class).

  1. New ImportBeanDefinitionRegistrar implementation class

    public class ImportBeanDefinitionRegistrarImpl implements ImportBeanDefinitionRegistrar {
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            // Get the annotation collection
            MergedAnnotations annotations = importingClassMetadata.getAnnotations();
            // Get the TestImport annotation
            MergedAnnotation<TestImport> testImportMergedAnnotation = annotations.get(TestImport.class);
            // Print the value of the value attribute
            System.out.println("= = = ="+testImportMergedAnnotation.getString("value"));
            // Registration information
            registry.registerBeanDefinition("testImportB".newRootBeanDefinition(TestImportB.class)); }}Copy the code

The last

In a recent interview, I was asked how to register beans with the Spring container and my response was:

  1. @Component, @Service, @Controller, and @repository. The last three layers use @Component.
  2. @Configuration, using @Bean registration in the Configuration class
  3. BeanFactoryAware, ApplicationContextAware, implements the two interfaces and then manually registers them with the container.

This can add another one @ Import, ImportBeanDefinitionRegistrar and ImportSelector.