In reading Spring Boot source code, see the Spring Boot ImportBeanDefinitionRegistrar to the extensive use of dynamic inject Bean. It is Spring in a strong expansion interface. This article to say something about it related use.
Use Spring Boot in
Related to automatically configure the built-in container in a ServletWebServerFactoryAutoConfiguration Spring Boot class. Such part of the code 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;
// 实现BeanFactoryAware的方法,设置BeanFactory
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof ConfigurableListableBeanFactory) {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
}
// 注册一个WebServerFactoryCustomizerBeanPostProcessor
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
if (this.beanFactory == null) {
return;
}
registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
WebServerFactoryCustomizerBeanPostProcessor.class);
registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
ErrorPageRegistrarBeanPostProcessor.class);
}
// 检查并注册Bean
private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name, Class<?> beanClass) {
// 检查指定类型的Bean name数组是否存在,如果不存在则创建Bean并注入到容器中
if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
beanDefinition.setSynthetic(true);
registry.registerBeanDefinition(name, beanDefinition);
}
}
}
}复制代码
In this automatic configuration class, basically showing the core usage ImportBeanDefinitionRegistrar. Here the interface is mainly used to register BeanDefinition.
BeanPostProcessorsRegistrar realized ImportBeanDefinitionRegistrar interfaces and BeanFactoryAware interface. Wherein the interface is implemented BeanFactoryAware for exposing the ConfigurableListableBeanFactory Spring object.
RegisterBeanDefinitions method is implemented for implantation dynamic Bean, where injected WebServerFactoryCustomizerBeanPostProcessor and ErrorPageRegistrarBeanPostProcessor.
Use a simple example to understand the Spring Boot the following, we summarize the usage and achieve a similar functionality themselves.
ImportBeanDefinitionRegistrar使用
Spring officially by ImportBeanDefinitionRegistrar injection mechanism to achieve a dynamic annotation @ Component, @ Service and so on.
Many constituents integrated with the Spring framework, through the interface will achieve the specified class scanning, and then registered to the spring container. Mapper interfaces in such Mybatis, springCloud FeignClient the interface are registered custom logic implemented through this interface.
All the classes that implement the interface will be treated ConfigurationClassPostProcessor, ConfigurationClassPostProcessor achieved BeanFactoryPostProcessor interface, dynamically registered ImportBeanDefinitionRegistrar bean bean initialization which depends on the priority thereof, can also be aop, validator processing mechanisms.
The basic steps:
- ImportBeanDefinitionRegistrar implement the interface;
- Implement particular class initializing registerBeanDefinitions;
- @Import introduced using implementation class on the class @Configuration annotation configuration;
A simple example
Here implement a very simple operation, a custom @Mapper annotation (not Mybatis implemented in Mapper), @Component achieve similar functions, add annotations @Mapper class will be loaded automatically into containers spring.
First create @Mapper comment.
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
public @interface Mapper {
}复制代码
UserMapper create classes for use @Mapper note.
@Mapper
public class UserMapper {
}
复制代码
Define the implementation class MapperAutoConfigureRegistrar ImportBeanDefinitionRegistrar. If you need to get some data in Spring, you can achieve some Aware Interface, which enables 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;
}
}复制代码
In the above code, obtained by the method setResourceLoader ResourceLoader ResourceLoaderAware interface to the object.
In registerBeanDefinitions method, with the implementation class ClassPathBeanDefinitionScanner class to get a scan Bean require registration.
MapperBeanDefinitionScanner implementation 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);
}
}复制代码
MapperBeanDefinitionScanner inherit sub ClassPathBeanDefinitionScanner, scanning the @Mapper of annotated classes.
AddIncludeFilter method specified in MapperBeanDefinitionScanner parameters comprising the Mapper AnnotationTypeFilter.
Of course, you can specify the type not loaded by excludeFilters. Both methods provided by their parent ClassPathScanningCandidateComponentProvider.
Completion of the above definition, the last step of the pull-in operation. Creating a class autoconfiguration MapperAutoConfig, and by introducing Registrar custom @Import.
@Configuration
@Import(MapperAutoConfigureRegistrar.class)
public class MapperAutoConfig {
}复制代码
At this point, the function of the entire code is already written, write a unit test below.
@RunWith(SpringRunner.class)
@SpringBootTest
public class MapperAutoConfigureRegistrarTest {
@Autowired
UserMapper userMapper;
@Test
public void contextLoads() {
System.out.println(userMapper.getClass());
}
}复制代码
Execution unit test code will be found below the print log:
class com.secbro2.learn.mapper.UserMapper复制代码
UserMapper has been described instantiated successfully, and to inject them Spring container.
summary
Of course, UserMapper here do not interface, here is also not realized in the form of Mybatis. Just a simple example to demonstrate this feature. Note that the text referred to the example of two implementations, the first is the realization of Spring Boot, the second is our Mapper instance. Show the registration of two different methods of operation, but throughout the process is the same, the reader's attention carefully taste, and more complex functions to expand on this basis.
Original link: " the Spring Bean injected through the Boot dynamic ImportBeanDefinitionRegistrar "