在閱讀Spring Boot源碼時,看到Spring Boot中大量使用ImportBeanDefinitionRegistrar來實現Bean的動態注入。它是Spring中一個強大的擴展接口。本篇文章來說講它相關使用。web
在Spring Boot 內置容器的相關自動配置中有一個ServletWebServerFactoryAutoConfiguration類。該類的部分代碼以下:spring
@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); } } } }
在這個自動配置類中,基本上展現了ImportBeanDefinitionRegistrar最核心的用法。這裏該接口主要用來註冊BeanDefinition。數組
BeanPostProcessorsRegistrar實現了ImportBeanDefinitionRegistrar接口和BeanFactoryAware接口。其中BeanFactoryAware接口的實現是用來暴露Spring的ConfigurableListableBeanFactory對象。微信
而實現registerBeanDefinitions方法則是用來對Bean的動態注入,這裏注入了WebServerFactoryCustomizerBeanPostProcessor和ErrorPageRegistrarBeanPostProcessor。app
簡單瞭解了Spring Boot中的一個使用實例,下面咱們總結一下使用方法,並本身實現一個相似的功能。框架
Spring官方經過ImportBeanDefinitionRegistrar實現了@Component、@Service等註解的動態注入機制。ide
不少三方框架集成Spring的時候,都會經過該接口,實現掃描指定的類,而後註冊到spring容器中。 好比Mybatis中的Mapper接口,springCloud中的FeignClient接口,都是經過該接口實現的自定義註冊邏輯。spring-boot
全部實現了該接口的類的都會被ConfigurationClassPostProcessor處理,ConfigurationClassPostProcessor實現了BeanFactoryPostProcessor接口,因此ImportBeanDefinitionRegistrar中動態註冊的bean是優先於依賴其的bean初始化,也能被aop、validator等機制處理。單元測試
基本步驟:測試
這裏實現一個很是簡單的操做,自定義一個@Mapper註解(並不是Mybatis中的Mapper實現),實現相似@Component的功能,添加了@Mapper註解的類會被自動加載到spring容器中。
首先建立@Mapper註解。
@Documented @Inherited @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) public @interface Mapper { }
建立UserMapper類,用於使用@Mapper注。
@Mapper public class UserMapper { }
定義ImportBeanDefinitionRegistrar的實現類MapperAutoConfigureRegistrar。若是須要獲取Spring中的一些數據,可實現一些Aware接口,這實現了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; } }
在上面代碼中,經過ResourceLoaderAware接口的setResourceLoader方法得到到了ResourceLoader對象。
在registerBeanDefinitions方法中,藉助ClassPathBeanDefinitionScanner類的實現類來掃描獲取須要註冊的Bean。
MapperBeanDefinitionScanner的實現以下:
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繼承子ClassPathBeanDefinitionScanner,掃描被@Mapper的註解的類。
在MapperBeanDefinitionScanner中指定了addIncludeFilter方法的參數爲包含Mapper的AnnotationTypeFilter。
固然也能夠經過excludeFilters指定不加載的類型。這兩個方法由它們的父類ClassPathScanningCandidateComponentProvider提供的。
完成了上面的定義,則進行最後一步引入操做了。建立一個自動配置類MapperAutoConfig,並經過@Import引入自定義的Registrar。
@Configuration @Import(MapperAutoConfigureRegistrar.class) public class MapperAutoConfig { }
至此,整個代碼的功能已經編寫完成,下面寫一個單元測試。
@RunWith(SpringRunner.class) @SpringBootTest public class MapperAutoConfigureRegistrarTest { @Autowired UserMapper userMapper; @Test public void contextLoads() { System.out.println(userMapper.getClass()); } }
執行單元測試代碼,會發現打印以下日誌:
class com.secbro2.learn.mapper.UserMapper
說明UserMapper已經被實例化成功,並注入Spring容器當中。
固然,這裏的UserMapper並不接口,這裏的實現也並非Mybatis中的實現形式。只是爲了演示該功能的簡單示例。須要注意的是文中提到了兩種實現的實例,第一種是Spring Boot中的實現,第二種是咱們的Mapper實例。展示了兩種不一樣方法的註冊的操做,但整個使用流程是一致的,讀者注意仔細品味,並在此基礎上進行拓展更復雜的功能。
原文連接:《Spring Boot經過ImportBeanDefinitionRegistrar動態注入Bean》