在阅读Spring Boot源码时,看到Spring Boot中大量利用ImportBeanDefinitionRegistrar来实现Bean的动态注入。它是Spring中一个强盛的扩展接口。本篇文章来讲讲它相干利用。
Spring Boot中的利用
在Spring Boot 内置容器的相干自动设置中有一个ServletWebServerFactoryAutoConfiguration类。该类的部分代码如下:
@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。
简单了解了Spring Boot中的一个利用实例,下面我们总结一下利用方法,并本身实现一个类似的功能。
ImportBeanDefinitionRegistrar利用
Spring官方通过ImportBeanDefinitionRegistrar实现了@Component、@Service等注解的动态注入机制。
很多三方框架集成Spring的时候,都会通过该接口,实现扫描指定的类,然后注册到spring容器中。 比如Mybatis中的Mapper接口,springCloud中的FeignClient接口,都是通过该接口实现的自界说注册逻辑。
全部实现了该接口的类的都会被ConfigurationClassPostProcessor处理,ConfigurationClassPostProcessor实现了BeanFactoryPostProcessor接口,以是ImportBeanDefinitionRegistrar中动态注册的bean是优先于依赖其的bean初始化,也能被aop、validator等机制处理。
根本步调:
- 实现ImportBeanDefinitionRegistrar接口;
- 通过registerBeanDefinitions实现具体的类初始化;
- 在@Configuration注解的设置类上利用@Import导入实现类;
简单示例
这里实现一个非常简单的利用,自界说一个@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注。
@Mapperpublic 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)@SpringBootTestpublic class MapperAutoConfigureRegistrarTest { @Autowired UserMapper userMapper; @Test public void contextLoads() { System.out.println(userMapper.getClass()); }}实行单元测试代码,会发现打印如下日志:
class com.edu.mapper.UserMapper固然,这里的UserMapper并不是接口,这里的实现也并不是Mybatis中的实现情势。只是为了演示该功能的简单示例。必要留意的是文中提到了两种实现的实例,第一种是Spring Boot中的实现,第二种是我们的Mapper实例。展现了两种差异方法的注册的利用,但整个利用流程是划一的,读者留意细致咀嚼,并在此根本上举行拓展更复杂的功能。 |