Reading in the Spring when the Boot source, see the Spring Boot ImportBeanDefinitionRegistrar is used in great quantities to the Bean’s dynamic injection. It is a powerful extension interface in Spring. This article will talk about its use.

Use in Spring Boot

In the Spring the Boot built-in container have a ServletWebServerFactoryAutoConfiguration class related automatic configuration. Part of the code for this class is as follows:

@Configuration(proxyBeanMethods = false) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @ConditionalOnClass(ServletRequest.class) @ConditionalOnWebApplication(type = Type.SERVLET) @EnableConfigurationProperties(ServerProperties.class) @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, ServletWebServerFactoryConfiguration.EmbeddedJetty.class, ServletWebServerFactoryConfiguration.EmbeddedUndertow.class }) public class ServletWebServerFactoryAutoConfiguration { / /... /** * Registers a {@link WebServerFactoryCustomizerBeanPostProcessor}. Registered via * {@link ImportBeanDefinitionRegistrar} for early registration. */ public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware { private ConfigurableListableBeanFactory beanFactory; // Implement BeanFactoryAware, Override public void setBeanFactory(BeanFactory BeanFactory) throws BeansException {if (BeanFactory) instanceof ConfigurableListableBeanFactory) { this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; }} / / registered a WebServerFactoryCustomizerBeanPostProcessor @ Override public void registerBeanDefinitions (AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if (this.beanFactory == null) { return; } registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor", WebServerFactoryCustomizerBeanPostProcessor.class); registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor", ErrorPageRegistrarBeanPostProcessor.class); } / / check and register the Bean private void registerSyntheticBeanIfMissing (BeanDefinitionRegistry registry, String name, Class <? > beanClass) {// Check whether the Bean name array of the specified type exists, If there is no Bean is created and injected into the container if (ObjectUtils. IsEmpty (this) the beanFactory) getBeanNamesForType (beanClass, true, false))) { RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass); beanDefinition.setSynthetic(true); registry.registerBeanDefinition(name, beanDefinition); }}}}Copy the code

In the automatic configuration class, basically shows ImportBeanDefinitionRegistrar at the core of usage. This interface is mainly used to register BeanDefinitions.

BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar interface and BeanFactoryAware interface. Including BeanFactoryAware interface implementation ConfigurableListableBeanFactory object is used to expose the Spring.

The registerBeanDefinitions method is implemented to dynamically inject beans, Injected WebServerFactoryCustomizerBeanPostProcessor and ErrorPageRegistrarBeanPostProcessor here.

Having looked briefly at an example of use in Spring Boot, let’s summarize how to use it and implement a similar feature ourselves.

ImportBeanDefinitionRegistrar use

Spring officially by ImportBeanDefinitionRegistrar @ Component, @ Service annotations such as dynamic injection mechanism.

Many tripartite frameworks integrate with Spring by using this interface to scan specified classes and register them with the Spring container. For example, Mapper interface in Mybatis and FeignClient interface in springCloud are all customized registration logic realized through this interface.

All classes that implements this interface will be ConfigurationClassPostProcessor processing, realized ConfigurationClassPostProcessor spring BeanFactoryPostProcessor interface, So ImportBeanDefinitionRegistrar dynamic registration in the bean is preferred to rely on the bean initialization, can also be aop, such as the validator mechanism to deal with.

Basic steps:

  • Implement ImportBeanDefinitionRegistrar interface;
  • Use registerBeanDefinitions to initialize specific classes.
  • Import the implementation class using @import on the Configuration class of the @Configuration annotation;

A simple example

This is a very simple operation, a custom @mapper annotation (not the Mapper implementation in Mybatis) that does something like @Component. Classes that add the @Mapper annotation are automatically loaded into the Spring container.

Start by creating the @mapper annotation.

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
public @interface Mapper {
}Copy the code

Create a UserMapper class to use @mapper annotations.

@Mapper
public class UserMapper {
}
Copy the code

Define ImportBeanDefinitionRegistrar MapperAutoConfigureRegistrar implementation class. If you need to get some data from Spring, you can implement some Aware interfaces, which implement ResourceLoaderAware.

public class MapperAutoConfigureRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware { private ResourceLoader resourceLoader; @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { MapperBeanDefinitionScanner scanner = new MapperBeanDefinitionScanner(registry, false); scanner.setResourceLoader(resourceLoader); scanner.registerFilters(); scanner.addIncludeFilter(new AnnotationTypeFilter(Mapper.class)); scanner.doScan("com.secbro2.learn.mapper"); } @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; }}Copy the code

In the above code, the ResourceLoader object is obtained through the setResourceLoader method of the ResourceLoaderAware interface.

In registerBeanDefinitions approach, with the aid of ClassPathBeanDefinitionScanner class implementation class to scan need to register for a Bean.

MapperBeanDefinitionScanner implementation is as follows:

public class MapperBeanDefinitionScanner extends ClassPathBeanDefinitionScanner { public MapperBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) { super(registry, useDefaultFilters); } protected void registerFilters() { addIncludeFilter(new AnnotationTypeFilter(Mapper.class)); } @Override protected Set<BeanDefinitionHolder> doScan(String... basePackages) { return super.doScan(basePackages); }}Copy the code

MapperBeanDefinitionScanner inheritance ClassPathBeanDefinitionScanner, scan be @ the annotation of Mapper class.

Specified in the MapperBeanDefinitionScanner addIncludeFilter method of parameter to include AnnotationTypeFilter Mapper.

It is also possible to specify the type not to load through excludeFilters. These two methods provided by their parent class ClassPathScanningCandidateComponentProvider.

Once the above definition is complete, the final import operation is performed. Create an auto-configuration class called MapperAutoConfig and bring in the custom Registrar via @import.

@Configuration
@Import(MapperAutoConfigureRegistrar.class)
public class MapperAutoConfig {
}Copy the code

Now that the functionality of the entire code has been written, let’s write a unit test.

@RunWith(SpringRunner.class) @SpringBootTest public class MapperAutoConfigureRegistrarTest { @Autowired UserMapper userMapper; @Test public void contextLoads() { System.out.println(userMapper.getClass()); }}Copy the code

After executing the unit test code, the following log is printed:

class com.secbro2.learn.mapper.UserMapperCopy the code

The UserMapper is successfully instantiated and injected into the Spring container.

summary

Of course, UserMapper here is not an interface, and the implementation here is not the implementation in Mybatis. This is just a simple example to demonstrate this capability. Note that there are two implementations mentioned, the first in Spring Boot and the second in our Mapper instance. Show two different methods of registration operation, but the entire use process is consistent, readers pay attention to taste carefully, and on this basis to expand more complex functions.

The original link: “Spring Boot by ImportBeanDefinitionRegistrar dynamic injection Bean”


Program new horizon