springboot之ImportBeanDefinitionRegistrar动态注入

源代码 2024-9-28 09:00:34 46 0 来自 中国
在阅读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的动态注入,这里注入了WebServerFactoryCustomizerBeanPostProcessorErrorPageRegistrarBeanPostProcessor
简单了解了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实例。展现了两种差异方法的注册的利用,但整个利用流程是划一的,读者留意细致咀嚼,并在此根本上举行拓展更复杂的功能。
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-11-22 05:12, Processed in 0.179579 second(s), 32 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表