[springboot] Springboot annotation configuration class add components

Article directory

  • foreword
  • First, the configuration class of springboot
  • 2. @Bean annotation
    • 1. Use @Bean annotation
    • 2. Properties of @Bean
    • 3. Singleton of @Bean
  • 3. @Configuration attribute
  • 3. Use spring commonly used annotations
    • 1. @SpringBootApplication annotation on the startup class
    • 2. @SpringBootApplication annotation attribute
  • 4. Use @Import to add components
  • 5. Use @ImportResource to import spring configuration files
  • Summarize

Foreword

When using spring before, there are two ways to add objects to the IOC container:

  1. An xml file needs to be written, and the bean tag is configured in the file.
  2. way of using annotations.

And springboot continues to add beans in the way of spring annotations. Next, let’s understand how springboot adds an object in IOC.

1. Springboot configuration class

When using spring, objects that need to be configured are usually configured using xml. In springboot, the configuration class is used by default to configure objects or integrate with other frameworks.
Configuration classes are annotated with the @Configuration annotation. Conversely, classes annotated with @Configuration are considered configuration classes in springboot.
Configuration classes are also stored in the IOC container as component beans.

// @Configuration annotation indicates that this class is a configuration class, which is equivalent to a configuration file in spring.
// The configuration class itself is also a bean component.
@Configuration
public class BeanConfig {<!-- -->

}

test:

@SpringBootApplication
public class SpringbootFunctionApplication {<!-- -->

    public static void main(String[] args) {<!-- -->
        // return IOC container
        ConfigurableApplicationContext run = SpringApplication.run(SpringbootFunctionApplication.class, args);
        // See if the configuration class is a bean in the container
        BeanConfig config = run. getBean(BeanConfig. class);
        System.out.println("BeanConfig:" + config);
    }
}

The result is as follows:

BeanConfig: com.study.springbootfunction.config.BeanConfig$$EnhancerBySpringCGLIB$$235caeaa@4a7fd0c9

2. @Bean annotation

1. Use @Bean annotation

Marking the @Bean annotation on the method of the configuration class means adding a bean to the IOC container.
The bean id is the method name; the return type is the bean type; the returned value is the object instance in the container.

//Add a test entity object
@Data
@Accessors(chain = true)
@ToString
public class User implements Serializable {<!-- -->

    private String name;

    private String password;

    private Integer age;

    private String gender;

    private String phone;

    private LocalDate birthday;
}

// configuration class
@Configuration
public class BeanConfig {<!-- -->
// Add a bean object with id user01 to the IOC container
    @Bean
    public User user01() {<!-- -->
        return new User()
                .setName("Zhang San")
                .setPassword("123456")
                .setAge(18)
                .setGender("Male")
                .setPhone("18812341234")
                .setBirthday(LocalDate.now().minusYears(18));
    }
}

test:

@SpringBootApplication
public class SpringbootFunctionApplication {<!-- -->

    public static void main(String[] args) {<!-- -->
        // return IOC container
        ConfigurableApplicationContext run = SpringApplication.run(SpringbootFunctionApplication.class, args);
        // Get the component from the container
        User user01 = run. getBean("user01", User. class);
        System.out.println("user01:" + user01);
    }
}

The result is as follows:

user01: User(name=Zhang San, password=123456, age=18, gender=male, phone=18812341234, birthday=2005-03-22)

2. @Bean attribute

There is a name attribute in the @Bean annotation, which indicates the id of the bean if it has a value.

@Configuration
public class BeanConfig {<!-- -->
// Add a bean object with id user02 to the IOC container
    @Bean(name = "user02")
    public User user() {<!-- -->
        return new User()
                .setName("Li Si")
                .setPassword("123456")
                .setAge(18)
                .setGender("Female")
                .setPhone("18812341234")
                .setBirthday(LocalDate.now().minusYears(18));
    }
}

test:

@SpringBootApplication
public class SpringbootFunctionApplication {<!-- -->

    public static void main(String[] args) {<!-- -->
        // return IOC container
        ConfigurableApplicationContext run = SpringApplication.run(SpringbootFunctionApplication.class, args);
        // Get the component from the container
        User user02 = run. getBean("user02", User. class);
        System.out.println("user02:"+user02);
    }
}

The result is as follows:

user02: User(name=Lisi, password=123456, age=18, gender=female, phone=18812341234, birthday=2005-03-22, car=null)

3. @Bean Singleton

The beans added by @Bean annotation are single instance.

@Configuration
public class BeanConfig {<!-- -->
    @Bean
    public User user01() {<!-- -->
        return new User()
                .setName("Zhang San")
                .setPassword("123456")
                .setAge(18)
                .setGender("male")
                .setPhone("18812341234")
                .setBirthday(LocalDate.now().minusYears(18));
    }
}

test:

@SpringBootApplication
public class SpringbootFunctionApplication {<!-- -->

    public static void main(String[] args) {<!-- -->
        // return IOC container
        ConfigurableApplicationContext run = SpringApplication.run(SpringbootFunctionApplication.class, args);
        // Get the component from the container
        User user01 = run. getBean("user01", User. class);
        User user01New = run. getBean("user01", User. class);
        System.out.println(MessageFormat.format("Is the bean obtained from the container a single instance: {0}", user01 == user01New));
    }
}

The result is as follows:

Whether the bean obtained from the container is a single instance: true

3. @Configuration attribute

The proxyBeanMethods attribute in the @Configuration annotation defaults to true. If true, methods annotated with @Bean return the same object no matter how many times they are called.
Let’s first look at the case where proxyBeanMethods is false

// proxyBeanMethods attribute is false
@Configuration(proxyBeanMethods = false)
public class BeanConfig {<!-- -->
    @Bean
    public User user01() {<!-- -->
        return new User()
                .setName("Zhang San")
                .setPassword("123456")
                .setAge(18)
                .setGender("male")
                .setPhone("18812341234")
                .setBirthday(LocalDate.now().minusYears(18));
    }
}

test:

@SpringBootApplication
public class SpringbootFunctionApplication {<!-- -->

    public static void main(String[] args) {<!-- -->
        // return IOC container
        ConfigurableApplicationContext run = SpringApplication.run(SpringbootFunctionApplication.class, args);
        // 5. See if the configuration class is called multiple times by the @Bean marked method and returns the same object
        BeanConfig config = run. getBean(BeanConfig. class);
        System.out.println("BeanConfig:" + config);
        System.out.println(MessageFormat.format("The method marked by @bean in the configuration class is called multiple times and returns the same object: {0}", config.user() == config.user())) ;
    }
}

The result is as follows:

BeanConfig: com.study.springbootfunction.config.BeanConfig@6d5c2745
Whether the configuration class is called multiple times by @bean marked methods returns the same object: false

It can be seen from here that when the proxyBeanMethods attribute is false, each call to the method marked by @Bean will generate a different object.
Next, let’s look at when the proxyBeanMethods property is true.

// The proxyBeanMethods attribute is true by default, and it can be left blank
@Configuration(proxyBeanMethods = true)
public class BeanConfig {<!-- -->
    @Bean
    public User user01() {<!-- -->
        return new User()
                .setName("Zhang San")
                .setPassword("123456")
                .setAge(18)
                .setGender("male")
                .setPhone("18812341234")
                .setBirthday(LocalDate.now().minusYears(18));
    }
}

test:

@SpringBootApplication
public class SpringbootFunctionApplication {<!-- -->

    public static void main(String[] args) {<!-- -->
        // return IOC container
        ConfigurableApplicationContext run = SpringApplication.run(SpringbootFunctionApplication.class, args);
        // 5. See if the configuration class is called multiple times by the @Bean marked method and returns the same object
        BeanConfig config = run. getBean(BeanConfig. class);
        System.out.println("BeanConfig:" + config);
        System.out.println(MessageFormat.format("The method marked by @bean in the configuration class is called multiple times and returns the same object: {0}", config.user() == config.user())) ;
    }
}

The result is as follows:

BeanConfig: com.study.springbootfunction.config.BeanConfig$$EnhancerBySpringCGLIB$$858dbcbb@7a360554
Whether the configuration class is called multiple times by the @bean marked method returns the same object: true

Through the results, we can see that when the value of proxyBeanMethods is true, how many times we configure the class object to call the @Bean marked method returns an object. As for the reason, we can also see from the first line of the returned result that the configuration class object obtained from the IOC container is an object proxied by CGLIB.
When the value of proxyBeanMethods is true, the object returned by the configuration class object calling the method annotated by @Bean is the same object. Then the method inside the configuration class, the call also returns the same object? Let’s take a look.

// proxyBeanMethods property is true by default, you can leave it blank
@Configuration
public class BeanConfig {<!-- -->
    @Bean
    public User user01() {<!-- -->
        return new User()
                .setName("Zhang San")
                .setPassword("123456")
                .setAge(18)
                .setGender("male")
                .setPhone("18812341234")
                .setBirthday(LocalDate.now().minusYears(18));
    }
// The user03 method calls the user01 method to see if the bean user01 and user03 are an object
    @Bean
    public User user03() {<!-- -->
        return user01();
    }
}

test:

@SpringBootApplication
public class SpringbootFunctionApplication {<!-- -->

    public static void main(String[] args) {<!-- -->
        // return IOC container
        ConfigurableApplicationContext run = SpringApplication.run(SpringbootFunctionApplication.class, args);
        // Whether the internal method in the configuration class calls the @Bean marked method returns the same object
        System.out.println(MessageFormat.format("The internal method in the configuration class calls the @Bean marked method and returns the same object: {0}",
                run.getBean("user03", User.class) == run.getBean("user01", User.class)));
    }
}

The result is as follows:

 Whether the method returned by the internal method call @Bean annotation in the configuration class is the same object: true

Through the results, we can see that even if the internal method of the configuration class is called, an object will still be returned. So what is the use of this function? Mainly for bean dependency injection, it can ensure that the attribute object is the same as the object in the IOC container.

//Add a new object instance
@Data
@ToString
@Accessors(chain = true)
public class Car {<!-- -->
    private String name;
}
// add Car as a property to the class
@Data
@Accessors(chain = true)
@ToString
public class User implements Serializable {<!-- -->

    private String name;

    private String password;

    private Integer age;

    private String gender;

    private String phone;

    private LocalDate birthday;

    private Car car;
}

@Configuration
public class BeanConfig {<!-- -->
// Add a car bean to the container
    @Bean
    public Car car() {<!-- -->
        return new Car().setName("BenBen");
    }
    // inject car into user04
    @Bean
    public User user04() {<!-- -->
        return new User()
                .setName("Wang Wu")
                .setPassword("123456")
                .setAge(18)
                .setGender("Male")
                .setPhone("18812341234")
                .setBirthday(LocalDate.now().minusYears(18))
                .setCar(car());
    }
}

test:

@SpringBootApplication
public class SpringbootFunctionApplication {<!-- -->

    public static void main(String[] args) {<!-- -->
        // return IOC container
        ConfigurableApplicationContext run = SpringApplication.run(SpringbootFunctionApplication.class, args);
        // Whether the internal method in the configuration class calls the @Bean marked method returns the same object
        System.out.println(MessageFormat.format("The internal method in the configuration class calls the @Bean marked method and returns the same object: {0}",
                run.getBean("user04", User.class).getCar() == run.getBean(Car.class)));
    }

}

The result is as follows:

 Whether the method returned by the internal method call @Bean annotation in the configuration class is the same object: true

After understanding the proxyBeanMethods attribute of @Configuration, how should we use it?

  1. Full mode(proxyBeanMethods = true) If there is a dependency relationship between the configuration class components, the method will call the container to query the previous single-instance components, using the Full mode.
  2. Lite mode(proxyBeanMethods = false) There is no dependency between configuration class components. Using Lite mode can reduce judgment and speed up the container startup process.

3. Use common annotations in spring

When the spring IOC container adds beans using annotations, we often use annotations such as @Controller, @Service, @Component, @Repository, etc.

1. @SpringBootApplication annotation on the startup class

When we use the spring and springMVC framework, we need to configure the tag in xml when adding components using annotations.
In springboot this tag is replaced by @ComponentScan annotation. The attribute of basePackages in this annotation can configure the path for package scanning.
But when we built the simplest springboot project, we didn’t see where we needed to add the configuration @ComponentScan annotation.
At this time, we need to understand the most basic annotation @SpringBootApplication on the springboot startup class. Let’s look at the source code of the annotation.

@Target({<!-- -->ElementType.TYPE})
@Retention(RetentionPolicy. RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {<!-- -->@Filter(
    type = FilterType. CUSTOM,
    classes = {<!-- -->TypeExcludeFilter.class}
), @Filter(
    type = FilterType. CUSTOM,
    classes = {<!-- -->AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {<!-- -->
}

You can see that in addition to the four meta-annotations @Target, @Retention, @Target, and @Documented, there are three annotations on the annotation: @SpringBootConfiguration, @EnableAutoConfiguration, and @ComponentScan.
@SpringBootConfiguration marks that this class is a configuration class; @EnableAutoConfiguration introduces the importselector specific to springboot; I won’t explain too much here, mainly the @ComponentScan annotation, indicating that the @SpringBootApplication annotation marked on the startup class already has package scanning @Controller and other commonly used annotations are added to the IOC container function.
So what is the scan path? The default path in springboot is the same level of the startup class and all classes under its subpackages. like:

2. @SpringBootApplication annotation attribute

If our project is the following situation how to solve.

@Component
public class Entity {<!-- -->
}

In the project, we want to add the Entity object in the exclude package to the IOC container, how to do it?
At this time we need to use the scanBasePackages attribute in the @SpringBootConfiguration annotation.

@SpringBootApplication(scanBasePackages = {<!-- -->"com.study"})
public class SpringbootFunctionApplication {<!-- -->

    public static void main(String[] args) {<!-- -->
        // return IOC container
        ConfigurableApplicationContext run = SpringApplication.run(SpringbootFunctionApplication.class, args);
        // Check if the Entity is added to the IOC container
        System.out.println(MessageFormat.format("Entity is added to the IOC container: {0}", run.containsBean("entity")));
    }
}

The returned results are as follows:

Whether the Entity is added to the IOC container: true

From here we see that the scanBasePackages attribute can configure the basic path of package scanning, and we can also configure the scanning path we want through this attribute. The value type of the scanBasePackages attribute is an array, we can add multiple configuration paths so we can also write like this.

@SpringBootApplication(scanBasePackages = {<!-- -->"com.study.springbootfunction", "com.study.exclude"})
public class SpringbootFunctionApplication {<!-- -->

    public static void main(String[] args) {<!-- -->
        // return IOC container
        ConfigurableApplicationContext run = SpringApplication.run(SpringbootFunctionApplication.class, args);
        // Check if the Entity is added to the IOC container
        System.out.println(MessageFormat.format("Entity is added to the IOC container: {0}", run.containsBean("entity")));
    }
}

4. Use @Import to add components

The @Import annotation in springboot is used in the same way as in spring. Just add it to the class added to the container.

// Add a test @Import object
@Data
@ToString
@Accessors(chain = true)
public class ImportTest {<!-- -->
    private String property;
}
// @Import annotation adds ImportTest and User classes
@Import({<!-- -->ImportTest. class, User. class})
@Configuration(proxyBeanMethods = true)
public class BeanConfig {<!-- -->
}

test:

@SpringBootApplication
public class SpringbootFunctionApplication {<!-- -->

    public static void main(String[] args) {<!-- -->
        // return IOC container
        ConfigurableApplicationContext run = SpringApplication.run(SpringbootFunctionApplication.class, args);
        // View the beans added by the @Import annotation
        System.out.println(MessageFormat.format("View the bean added by @Import annotation: {0}", run.getBean(ImportTest.class)));
        String[] beanNamesForType = run.getBeanNamesForType(User.class);
        System.out.println(MessageFormat.format("View the name of the bean of type user: {0}", Arrays.toString(beanNamesForType)));
    }
}

The result is as follows:

View the beans added by the @Import annotation: ImportTest(property=null)
View the name of the bean of type user: [com.study.springbootfunction.entity.User]

It can also be seen from the results that @Import can also add components to the IOC container.
The difference between @Import and @Bean is

  1. @Bean can only add one bean at a time, @Import can add components in batches more convenient
  2. The attribute of the component bean added by @Import is null, and the default value can be injected into the attribute dependency when @Bean is initialized
  3. The bean id of @Import is the full class name of the object and cannot be modified. @Bean can define the bean id by itself

5. Use @ImportResource to import spring configuration files

We can also import some xml files of spring to add bean components.

//Add a test object
public class ImportResourceTest {<!-- -->
}


Add ApplicationContext.xml file

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

<!-- Add bean importResourceTest in the ApplicationContext.xml file -->
    <bean id="importResourceTest" class="com.study.springbootfunction.entity.ImportResourceTest" scope="singleton"/>

</beans>
// Add @ImportResource annotation to any bean component object class and add the path of the xml file
@ImportResource("classpath:xml/**/*.xml")
@Configuration
public class BeanConfig {<!-- -->
}

test:

@SpringBootApplication
public class SpringbootFunctionApplication {<!-- -->

    public static void main(String[] args) {<!-- -->
        // return IOC container
        ConfigurableApplicationContext run = SpringApplication.run(SpringbootFunctionApplication.class, args);
        // Check if ImportResourceTest is added to the IOC container
        System.out.println(MessageFormat.format("ImportResourceTest is added to the IOC container: {0}", run.containsBean("importResourceTest")));
    }
}

The result is as follows:

Whether ImportResourceTest is added to the IOC container: true

Summary

In this article, there are five ways to add bean components to springboot:

  1. The @Configuration annotation can add configuration classes to IOC in the form of beans
  2. Adding @Bean annotations requires ordinary objects, and @Bean can only be used on @Configuration methods
  3. The following common annotations for @Controller, @Service, @Component, @Repository, etc.
  4. The @Import annotation can quickly add some objects to the container
  5. @ImportResource can add beans configured in xml to the container