首页 > 其他分享 >springboot之ImportBeanDefinitionRegistrar动态注入

springboot之ImportBeanDefinitionRegistrar动态注入

时间:2024-02-05 12:01:35浏览次数:30  
标签:Mapper springboot ImportBeanDefinitionRegistrar class 接口 动态 public registry

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注。

@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。

标签:Mapper,springboot,ImportBeanDefinitionRegistrar,class,接口,动态,public,registry
From: https://blog.51cto.com/u_13529088/9603611

相关文章

  • 【动态规划】最长公共子串
    目录题目应用1:最长公共子串题目解题思路边界条件状态转移代码实现应用2:Leetcode718.最长重复子数组题目解题思路代码实现解题思路方法一:动态规划初始条件状态转移复杂度方法二:滑动窗口复杂度代码实现题目应用1:最长公共子串题目给定两个字符串text1和text2,返回这两个......
  • SpringBoot- 文件上传
    packagecom.example.demo.controller;importorg.springframework.web.bind.annotation.PostMapping;importorg.springframework.web.bind.annotation.RestController;importorg.springframework.web.multipart.MultipartFile;importjavax.servlet.http.HttpServlet......
  • 安卓动态链接库文件体积优化探索实践
    背景介绍应用安装包的体积影响着用户下载量、安装时长、用户磁盘占用量等多个方面,据GooglePlay统计,应用体积每增加6MB,安装的转化率将下降1%。   安装包的体积受诸多方面影响,针对dex、资源文件、so文件都有不同的优化策略,在此不做一一展开,本文主要记录了在研发时针对动态......
  • SpringBoot中使用Spring自带线程池ThreadPoolTaskExecutor与Java8CompletableFuture实
    场景关于线程池的使用:Java中ExecutorService线程池的使用(Runnable和Callable多线程实现):https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/126242904Java中创建线程的方式以及线程池创建的方式、推荐使用ThreadPoolExecutor以及示例:https://blog.csdn.net/BADAO_......
  • 阿里二面:SpringBoot可以同时处理多少个请求?当场懵了。。。。
    SpringBoot以其简洁高效的开发方式和强大的内嵌容器特性,为开发者提供了构建高性能后端服务的便利。然而,当面临高并发场景时,理解并合理配置SpringBoot应用以达到最佳的并发处理能力至关重要。在SpringBoot中,应用程序对HTTP请求的并发处理主要依赖于内嵌的Servlet容器(如Tomcat)。接......
  • SpringBoot实现统一异常处理
    目录前言实现步骤定义统一响应对象类定义业务异常枚举接口和实现定义业务异常基类定义全局异常处理切面测试和验证总结前言近日心血来潮想做一个开源项目,目标是做一款可以适配多端、功能完备的模板工程,包含后台管理系统和前台系统,开发者基于此项目进行裁剪和扩展来完成自己的功......
  • Drvsetup.dll 是 Windows 操作系统中的一个动态链接库文件,用于设备驱动程序的安装和配
     Drvsetup.dll是Windows操作系统中的一个动态链接库文件,用于设备驱动程序的安装和配置过程中。该文件通常位于C:\Windows\System32文件夹下。Drvsetup.dll主要负责设备驱动程序的安装和配置过程中的一些核心功能,包括驱动程序的复制、注册、配置和卸载等。在设备驱动程序......
  • drvstore.dll 是 Windows 操作系统中的一个动态链接库文件
    drvstore.dll是Windows操作系统中的一个动态链接库文件,用于存储和管理设备驱动程序的信息。它通常位于系统目录(如C:\Windows\System32)下。drvstore.dll的主要作用是维护设备驱动程序的备份和安装信息,以便在需要时能够快速找到并加载正确的驱动程序。当用户连接新设备或更新设......
  • 全网最全:SpringBoot 各种回滚骚操作实战
    事务定义事务,就是一组操作数据库的动作集合。事务是现代数据库理论中的核心概念之一。如果一组处理步骤或者全部发生或者一步也不执行,我们称该组处理步骤为一个事务。当所有的步骤像一个操作一样被完整地执行,我们称该事务被提交。由于其中的一部分或多步执行失败,导致没有步骤被提交......
  • 开发小技巧 - springboot项目启动控制台输出访问地址
    在SpringBoot项目中,有时我们需要记录或输出访问的地址和IP,以便进行调试、监控或日志记录。以下是如何在SpringBoot中实现这一需求的方法: 1、编写获取所有访问地址工具类packagecom.example.utils;importcn.hutool.core.util.StrUtil;importorg.springframework.b......