spring carding (2) registering bean s based on annotations

Posted by Oni on Fri, 10 May 2019 22:58:01 +0200

We know that if you want to put beans in the spring container management, you need to register beans in the spring container first, and beans can be registered by xml or annotations. The configuration based on xml is usually configurated by xml tags such as < bean >, < context: component-scan >, and then the spring container scans xml files for registration; annotation-based registration is mainly through several Annotations defined by spring are also scanned by spring containers and beans are registered in the containers. Annotation-based development of spring has become more and more popular. Annotations are also widely used in spring boot and other spring family frameworks to drive development. Learning how to develop spring annotations is very helpful for understanding and learning spring boot.

This article introduces annotation-based registration bean s:

  • Way 1: Combine @Configuration with @Bean
@Configuration
public class CarConfig{
    
    @Bean
    public Car car() {
        return new Car();
    }
}

Classes identified by the @Configuration annotation automatically acquire the @Component feature, because the annotation itself uses the @Component annotation, which allows you to view the source code definition of @Configuration, and this class acts as a configuration class for spring. When creating a bean of this type, spring scans all the methods of @Bean annotation, executes automatically, and automatically registers the return value. In containers, the default method name is used as the name of the bean. You can also name beans by providing the value value of @Bean or by setting the name attribute of the bean.

  • Mode 2: Automatic registration using @ComponentScan annotation
@ComponentScan("cn.wyn")
@Configuration
public class BookConfig {
    
}

package cn.wyn;
@Component
public class Book {
    
}

Similar to configuring <context: component-scan base-package="> in xml, spring scans all classes labeled with @Component or related annotations (such as @Controller, @Configuration, @Service) using the @ComponentScan annotation, automatically creates instances of all classes labeled with @Component related annotations according to the specified scanning package path, and registers them with sp. The @Bean method is also executed in the ring container if it is @Configuration annotated.

We can also filter scanned classes, such as scanners excluding classes containing @Controller:

@ComponentScan(value = "cn.wyn", 
        excludeFilters = { @ComponentScan.Filter(
        type = FilterType.ANNOTATION,
        classes = {Controller.class})})
@Configuration
public class MyConfig {
}

The above configuration is equivalent to the xml configuration:

<context:component-scan base-package="cn.wyn">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

For example, only classes with @Service annotations are scanned:

@ComponentScan(value = "cn.wyn",
        includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,
        classes = Service.class)}, 
        useDefaultFilters = false)
@Configuration
public class MyConfig {
}

The above configuration is equivalent to the xml configuration:

<context:component-scan base-package="cn.wyn" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>

Configuring include also requires specifying useDefaultFilters to be false, so spring does not automatically register all Component s under the package.

Note: @ComponentScan can be reused on the same class for multiple scans, but this feature requires JDK versions of jdk8 and above. If the JDK version is lower than jdk8, you can use @ComponentScans to achieve multiple scans.

We can also customize the scanning rules by specifying the type value of include or exclude as CUSTOM and the TypeFilter class of processing rules. We need to customize a class that implements the TypeFilter interface and override the match method:

class MyTypeFilte implements TypeFilter {

    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        return false;
    }
}

@Configuration
@ComponentScan(value = "cn.wyn",
        includeFilters = { @ComponentScan.Filter(type = FilterType.CUSTOM,
        classes = MyTypeFilter.class)}, 
        useDefaultFilters = false)
class MyConfig {
}

Description: The metadata Reader, a parameter of the match method, can obtain metadata information of the class being scanned, such as class name, annotation information on the class, etc. If the return value of the match method is true, it will match. When true is returned, if include, it is registered, and if exclude, it is ignored. Return false and vice versa.

  • Mode 3: Import a class into the spring container using the @Import annotation

@Configuration
@Import({Car.class, Book.class})
class MyConfig {
}

By annotating the @Import annotation on the configuration class, you can quickly create an instance of a class and import it into the spring container.

Mode 3 Extension 1: @Import uses ImportSelector to import in batches:

The specific method is to specify the value of @Import as a class that implements the ImportSelector interface, which overrides the selectImports method, and the selectImports method returns a String array containing the fully qualified class name to be imported. ImportSelector does not import ImportSelector implementation classes, but imports only the classes specified in the array returned by the selectImports method.

class MyImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"java.lang.String"};
    }
}

@Configuration
@Import({MyImportSelector.class})
class BeanConfig {

}

Mode 3 Extension 2: @Import uses ImportBean Definition Registrar

Customize an ImportBeanDefinitionRegistrar class, implement the ImportBeanDefinitionRegistrar interface, rewrite the registerBeanDefinitions method, and register bean s through parameter registry, such as:

class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        
        //Create a BeanDefinition object
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Book.class);
        //Register a BeanDefinition named xixi
        registry.registerBeanDefinition("xixi", rootBeanDefinition);
    }
}


@Configuration
@Import({MyImportBeanDefinitionRegistrar.class})
class BeanConfig {

}

ImportBean Definition Registrar is also imported using the @Import annotation. Similarly, only bean s registered in the registerBean Definitions method are registered, and the Import Definition Registrar class is not registered.

  • Way 4: Register bean s using FactoryBean

Implement the FactoryBean interface and implement the following three methods:

Method name Effect
getObject Getting bean s in this way
getObjectType This method obtains Bean's lass object
isSingleton This method specifies whether the scope of a bean is an instance or not.

Example:

class MyFactoryBean implements FactoryBean {


    //Specific methods for obtaining bean s
    @Override
    public Object getObject() throws Exception {
        return new String("Ha ha ha");
    }

    //Get the Class type of the bean
    @Override
    public Class<?> getObjectType() {
        return String.class;
    }

    //Use this method to specify whether bean s are singletons
    @Override
    public boolean isSingleton() {
        return true;
    }
}
@Configuration
class BeanConfig {

    @Bean("beanName")
    public MyFactoryBean createBean() {
        return new MyFactoryBean();
    }
}

spring determines that the return value of the @Bean annotated method is a factory bean, and executes the getObject method of the factory bean to get an instance and register it in the container. If it is a singleton, it registers only once. Instead of registering the implementation class of FactoryBean. If you want to get an instance of the factory bean itself, you can prefix the bean name specified when you get the bean with a "&" prefix, such as context. getBean ("&bean").

Setting the Scope of Beans -- Annotation Configuration

The scope of a bean is:

  • singleton: singleton, there is only one instance bean in the whole application
  • prototype: As opposed to a singleton, each getBean regenerates a Bean.
  • Request: In a web environment, each request creates a bean, and there is only one bean in one request. Different requests have different beans.
  • Session: In the web environment, the same bean is captured in the session lifecycle

By default, a singleton single instance can specify the scope of the Bean in the following way.

@Configuration
class BeanConfig {

    @Bean("book")
    @Scope("prototype")
    public Book createBean() {
        return new Book();
    }
}


Lazy Loading of Beans--Annotation Configuration

By default, all single instance beans are created when the spring container is created, and if created when the bean is first used, we call it lazy loading.

The configuration is simple, just add the @Lazy annotation to the method of creating bean s.

@Configuration
class BeanConfig {

    @Bean("book")
    @Lazy
    public Book createBean() {
        return new Book();
    }
}

Register Bean s on condition

We can choose whether or not to register beans by some criteria, which can be achieved by the @Condition annotation.

@Configuration
class BeanConfig {

    @Bean("book")
    @Conditional(MyCondition.class)
    public Book createBean() {
        return new Book();
    }
}

//Implement the Condition interface and override the matches method to decide whether to register beans based on the Boolean values returned by the method
class MyCondition implements Condition {


    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    
        /**
        * According to the existence of my.env=hello attribute in the environment variable, the creation of the environment variable is determined.
        * You can test it by specifying - Dmy.env=hello as the startup parameter.
        **/
        Environment environment = context.getEnvironment();
        String property = environment.getProperty("my.env");
        if ("hello".equals(property)) {
            return true;
        }
        return false;
    }
}


//test
public class MainTest {

    @Test
    public void test() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
    }
}

//Classes for testing
class Book {
    public Book() {
        System.out.println("init book ...");
    }
}

@ Conditional annotations can be added either to methods or to classes, where all @Bean methods in a class are set uniformly.

Use of Profile

In daily development, we may need to register a set of different beans according to different environments, such as: our production environment, test environment, development environment will use different data sources. With Profile configuration, you can specify that the Bean is registered in the spring container when a Profile is activated, which is the same as the profile in maven.

As follows:

    @Profile("dev")
    public Book book1(){
        return new Book();
    }

    @Profile("test") Book book2() {
        return new Book();
    }

    @Profile("prod") Book book3() {
        return new Book();
    }

Profiles can be activated by specifying environment variables, or jvm startup parameters: - Dspring. profile s. active = dev. Profiles can also be activated in code, such as:

public class MainTest {

    @Test
    public void test() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        
        //Setting Activated profile
        context.getEnvironment().setActiveProfiles("dev");
        context.register(BeanConfig.class);
        context.refresh();
    }
}


@Configuration
class BeanConfig {


    @Bean
    public Book book(){
        return new Book();
    }

    @Bean
    @Profile("dev")
    public Book book1(){
        return new Book();
    }

    @Bean
    @Profile("test") Book book2() {
        return new Book();
    }

    @Bean
    @Profile("prod") Book book3() {
        return new Book();
    }
}

@ Profile annotations can also be written on configuration classes, and all configurations of the entire configuration class are activated under the specified profile.

Reprinted please indicate the source
Author: wyn-lin
Links to the original text: https://www.jianshu.com/p/bdca18850673

Topics: Spring xml Attribute JDK