How many ways are there to hand over beans to Spring container management?

Several ways to hand over beans to Spring container management

Spring core

SpringThe core is IOC and AOP .

The so-called IoC, for springthe framework, is responsible springfor controlling the life cycle of objects and the relationship between objects.

As for a more detailed explanation, or to deeply understand Springthese two cores, it is not the purpose of this article, so I will not go into detail yet.

In our Springproject, we need to hand Beanit over to Springthe container, that is, IOC management, so that you can use annotations for dependency injection.

Package scanning + component annotation

For the case where the class is written by ourselves

This method is most commonly used in our daily development to add classes with , , , annotations springunder the scan path to the container.@Component@Controller@Service@Repositoryspring IOC

If you have used MybatisPlus, this is the same as its package scanning injection.

Then our ComponentScanannotation has three configurations.

Configuration item one

basePackagesPath used to define scanned packages.

@ComponentScan(basePackages = "com.timemail.bootmp")

For example, this is to scan com.timemail.bootmpthe entire package for classes with the above specified annotations and put them in IOC.

I found a complete example in another article:

@Component 
public  class  Person {
     private String name;
 
    public String getName () {
 
        return name;
    }
    public  void  setName (String name) {
         this .name = name;
    }
    @Override 
    public String toString () {
         return  "Person{" +
                 "name='" + name + '\'' +
                 '}' ;
    }
}
 
@ComponentScan(basePackages = "com.springboot.initbean.*") 
public  class  Demo1 {
     public  static  void  main (String[] args) {
         AnnotationConfigApplicationContext  applicationContext  =  new  AnnotationConfigApplicationContext (Demo1.class);
         Person  bean  = applicationContext.getBean(Person. class);
        System.out.println(bean);
    }
}
//Result 
Person{name= 'null' }

This shows that Personthe class in the above code has been managed by the IOC container.

Configuration item two

includeFiltersinclude rules

Filter annotation uses FilterType.CUSTOM to customize scanning rules. You need to implement the TypeFilter interface to implement the match method. The parameter MetadataReader is the current class information (annotations, class path, class source information…) MetadataReaderFactory MetadataReader factory class.

Configuration item three

excludeFiltersRemove rule

Same inclusion rules.

I haven’t used these last two configuration items much, and I’m not very familiar with them, so please check the relevant information for detailed use.

@Configuration+@Bean

@Configuration@Beanis also a commonly used way for us to put it into containers.

@ConfigurationUsed to declare configuration classes

@Beanused to declare aBean

@Configuration 
public  class  Demo {
     @Bean 
    public Person person () {
         Person  person  =  new  Person ();
        person.setAge( 10 );
         return person;
    }
}

like this.

Then we know that SSMinside, usually we will configure it in xml bean.

@Configuration 
public  class  ConfigBean {

}

Then our @Configurationannotation is equivalent to a Beanconfiguration xml.

<?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-4.3.xsd" >

</beans>​​

Properties in Bean annotations

Our @Beanannotations also have many properties that can be configured.

We can view its source code:

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})  //@1 
@Retention(RetentionPolicy.RUNTIME) 
@Documented 
public  @interface Bean {

    @AliasFor("name") 
    String[] value() default {};

    @AliasFor("value") 
    String[] name() default {};

    @Deprecated 
    Autowire autowire ()  default Autowire.NO;

    boolean  autowireCandidate ()  default  true ;

    String initMethod ()  default  "" ;

    String destroyMethod ()  default AbstractBeanDefinition.INFER_METHOD;
}
  1. value and name are the same. When setting, you can only choose one of these two parameters. The reason is caused by @AliasFor
  2. value: string array, the first value is used as the name of the bean, and other values ​​are used as aliases of the bean
  3. autowire: This parameter is marked @Deprecated, which means it has expired and is not recommended for use.
  4. autowireCandidate: Whether to serve as a candidate bean when injecting other objects.
  5. initMethod: bean initialization method. This is related to the life cycle and will be explained in detail later.
  6. destroyMethod: The method of bean destruction, which is also related to the life cycle and will be explained in detail later.

Expand

For @Configurationa modified class, springthe container will cglibcreate a proxy for this class. The proxy will intercept all modified @Beanmethods beanand ensure that these methods are only called once by default (single case) to ensure that these beanare the same bean, that is, singleton Example.

@Import annotation import

Let’s look at the source code first:

@Target(ElementType.TYPE) 
@Retention(RetentionPolicy.RUNTIME) 
@Documented 
public  @interface Import {
 
    /**
   * Used to import a class file
     * { @link Configuration @Configuration }, { @link ImportSelector},
     * { @link ImportBeanDefinitionRegistrar}, or regular component classes to import.
     */
    Class<?>[] value();
 
}

@ImportCan only be used for class annotations.

Here I will directly transfer the content of the public account: Xiaoha learns Java.

His explanation is very detailed.

The first two methods may be used more often and must be known in daily development. The @Import annotation may not be used particularly much, but it is also very important. It is often used when extending Spring. It is often used. Use it with custom annotations, and then import a configuration file into the container.

Regarding the @Import annotation, I will introduce a little more. It has four ways to use it. This is the source code of the @Import annotation, which means it can only be placed on a class.

@Import directly imports classes

public  class  Person {
     private String name;
 
    public String getName () {
 
        return name;
    }
 
    public  void  setName (String name) {
         this .name = name;
    }
 
    @Override 
    public String toString () {
         return  "Person{" +
                 "name='" + name + '\'' +
                 '}' ;
    }
}
/**
* Directly use @Import to import the person class, then try to get it from the applicationContext, and get it successfully
**/ 
@Import(Person.class) 
public  class  Demo1 {
 
    public  static  void  main (String[] args) {
         AnnotationConfigApplicationContext  applicationContext  =  new  AnnotationConfigApplicationContext (Demo1.class);
         Person  bean  = applicationContext.getBean(Person.class);
        System.out.println(bean);
    }
}

The above code directly @Importimports a class, and then it is automatically placed in IOCthe container.

Note: Our 

Personclass does not need any annotations, just import it directly.

@Import + ImportSelector

In fact @Import, it is very clear in the source code of the annotation. If you are interested, you can take a look. We implement an ImportSelectorinterface, then implement the methods in it and import it.

@Import(MyImportSelector.class) 
public  class  Demo1 {
 
    public  static  void  main (String[] args) {
         AnnotationConfigApplicationContext  applicationContext  =  new  AnnotationConfigApplicationContext (Demo1.class);
         Person  bean  = applicationContext.getBean(Person.class);
        System.out.println(bean);
    }
}
 
class  MyImportSelector  implements  ImportSelector {
     @Override 
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
         return  new  String []{ "com.springboot.pojo.Person" };
    }
}

I customized a MyImportSelectorcustom ImportSelectorinterface, overridden selectImportsthe method, and then wrote the fully qualified name of the class we want to import in it. It is also very simple to implement.

@Import + ImportBeanDefinitionRegistrar

This method also requires us to implement ImportBeanDefinitionRegistrarthe methods in the interface. The specific code is as follows:

@Import(MyImportBeanDefinitionRegistrar.class) 
public  class  Demo1 {
 
    public  static  void  main (String[] args) {
         AnnotationConfigApplicationContext  applicationContext  =  new  AnnotationConfigApplicationContext (Demo1.class);
         Person  bean  = applicationContext.getBean(Person.class);
        System.out.println(bean);
    }
}
 
class  MyImportBeanDefinitionRegistrar  implements  ImportBeanDefinitionRegistrar {
 
    @Override 
    public  void  registerBeanDefinitions (AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
         // Build a beanDefinition. I will introduce beanDefinition later, which can be simply understood as the definition of bean. 
        AbstractBeanDefinition  beanDefinition  = BeanDefinitionBuilder.rootBeanDefinition(Person.class).getBeanDefinition();
         // Register the beanDefinition into the Ioc container. 
        registry.registerBeanDefinition( "person" , beanDefinition);
    }
}

The above implementation is actually Importsimilar to the second method. Both need to implement the interface and then import it. I came into contact with a new concept, BeanDefinitionwhich can be simply understood as beanthe definition ( beanmetadata) of , which also needs to be managed in an IOC container. The metadata beanis created , applicationContextand then beanthe metadata is created Bean.

@Import + DeferredImportSelector

This method also requires us to implement the interface. In fact , it is @Importsimilar to the second method. DeferredImportSelectorIt is ImportSelectora sub-interface of , so the implementation method is the same as the second method. It’s just Springthat the processing method is different, it Spring Bootis related to the delayed import of the automatic import configuration file, which is very important. How to use it:

@Import(MyDeferredImportSelector.class) 
public  class  Demo1 {
     public  static  void  main (String[] args) {
         AnnotationConfigApplicationContext  applicationContext  =  new  AnnotationConfigApplicationContext (Demo1.class);
         Person  bean  = applicationContext.getBean(Person.class);
        System.out.println(bean);
    }
}
class  MyDeferredImportSelector  implements  DeferredImportSelector {
     @Override 
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
         // Also put Person's fully qualified name directly 
        return  new  String []{Person.class.getName()};
    }
}

Regarding @Importthe use of annotations, there are probably the above three methods. Of course, it can also be used with @Configurationannotations to import a configuration class.

FactoryBean interface

Speaking of which FactoryBean, many of our entry-level developers can easily confuse him with BeanFactory.

BeanFactoryIt is the container root interface of all Spring Beans. It defines a set of specifications for Spring containers and provides a complete set of specifications for IOC containers, such as the commonly used getBean method.

That is what we often call Beana factory.

And ours FactoryBean, it’s actually one BeanFactoryit’s his name, as the name suggests.

@Configuration 
public  class  Demo1 {
     @Bean 
    public PersonFactoryBean personFactoryBean () {
         return  new  PersonFactoryBean ();
    }
 
    public  static  void  main (String[] args) {
         AnnotationConfigApplicationContext  applicationContext  =  new  AnnotationConfigApplicationContext (Demo1.class);
         Person  bean  = applicationContext.getBean(Person.class);
        System.out.println(bean);
    }
}
 
class  PersonFactoryBean  implements  FactoryBean <Person> {
 

    @Override 
    public Person getObject ()  throws Exception {
         return  new  Person ();
    }

    @Override 
    public Class<?> getObjectType() {
         return Person.class;
    }
}

Here, we can first look at FactoryBeanthe method in:

It is an interface class.

Then we need to have a class to inherit this interface and override the method.

BeanHere, we put the classes that need to be registered FactoryBeaninto the generics.

getObjectMethod is used to directly return the created object.

getObjectTypeReturn the class directly class.

Then in fact, you still need to use @Beanannotations to return the class object that inherits the interface.

Then Configurationannotate and change this class springbootto the configuration class, which is equivalent to the file springmvcin xml.

We can use AnnotationConfigApplicationContextthe getBeanmethod to see if it is IOCmanaged.

After running, you can see that the object address is output.

It means success.

Using BeanDefinitionRegistryPostProcessor

When writing this article, I also checked the information of many big names on the Internet.

There is a method that I’m not familiar with.

then……


Start copying the original text….

In fact, this method is also used . It is a method that 

BeanDefinitionRegistrywill be executed when the Spring container is started . It probably means that after the beanDefinition is loaded, the beanDefinition will be post-processed. You can adjust the beanDefinition in the IOC container here to interfere with the later. Initialize the bean.

BeanDefinitionRegistryPostProcessor

postProcessBeanDefinitionRegistry ()

public  class  Demo1 {
     public  static  void  main (String[] args) {
         AnnotationConfigApplicationContext  applicationContext  =  new  AnnotationConfigApplicationContext ();
         MyBeanDefinitionRegistryPostProcessor  beanDefinitionRegistryPostProcessor  =  new  MyBeanDefinitionRegistryPostProcessor ();
        applicationContext.addBeanFactoryPostProcessor(beanDefinitionRegistryPostProcessor);
        applicationContext.refresh();
        Person  bean  = applicationContext.getBean(Person.class);
        System.out.println(bean);
    }
}
 
class  MyBeanDefinitionRegistryPostProcessor  implements  BeanDefinitionRegistryPostProcessor {
 
    @Override 
    public  void  postProcessBeanDefinitionRegistry (BeanDefinitionRegistry registry)  throws BeansException {
         AbstractBeanDefinition  beanDefinition  = BeanDefinitionBuilder.rootBeanDefinition(Person.class).getBeanDefinition();
        registry.registerBeanDefinition( "person" , beanDefinition);
    }
    @Override 
    public  void  postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory)  throws BeansException {
 
    }
}

In the above code, we manually registered person in beanDefinitionRegistry BeanDefinition, and finally successfully added person to applicationContext.