当前位置: 首页 > 知识库问答 >
问题:

使用应用程序属性动态创建bean

邢鸿博
2023-03-14

在我的默认SpringBoot应用程序中,它具有spring-boot-starter-执行器依赖项,调用/执行器/健康(带有show-细节:true)返回以下信息

{
  "status": "UP",
  "components": { ... },
    "diskSpace": {
      "status": "UP",
      "details": {
        "total": 2000396742656,
        "free": 581706977280,
        "threshold": 10485760,
        "exists": true
      }
    },
    "ping": { "status": "UP" }
  }
}

但是,我的机器上有两个驱动器,所以我想查看它们的磁盘空间详细信息。所以我添加了一个配置文件:

@Configuration
public class DiskSpaceConfig {
  @Bean
  public AbstractHealthIndicator driveC() {
    return new DiskSpaceHealthIndicator(new File("C:/"), DataSize.ofMegabytes(100));
  }

  @Bean
  public AbstractHealthIndicator driveD() {
    return new DiskSpaceHealthIndicator(new File("D:/"), DataSize.ofMegabytes(100));
  }
}

现在,我的健康endpoint返回以下数据

{
  "status": "UP",
  "components": { ... },
    "diskSpace": { "status": "UP", "details": { ... } },
    "driveC": { "status": "UP", "details": { ... } },
    "driveD": { "status": "UP", "details": { ... } },
    "ping": { "status": "UP" }
  }
}

(稍后我将考虑如何抑制默认的“diskSpace”条目)

然而,由于我可能想在不同的机器上运行我的应用程序,我想配置磁盘驱动器,以便在我的应用程序中进行动态检查。yml文件。

media:
  health:
    drives:
      - name: driveC
        path: "C:/"
      - name: driveD
        path: "D:/"

还加上

@Slf4j
@Component
@ConfigurationProperties(prefix = "media.health")
@Data
public class MediaHealthConfigLoader {
  private List<DriveConfig> drives;

  @PostConstruct
  public void postProcessBeanFactory() {
    log.info("Found {} configured drives", drives.size());
  }
}

@Data
public class DriveConfig {
  private String name;
  private String path;
}

@Configuration
@EnableConfigurationProperties
public class DiskSpaceConfig {
}

配置在启动期间加载并记录到控制台。

然而,我的问题是,如何动态创建这些bean并将它们添加到bean上下文中?我想我需要去豆工厂。我试过以下方法:

@Slf4j
@Configuration
@EnableConfigurationProperties
@RequiredArgsConstructor
public class DiskSpaceConfig {
  private final MediaHealthConfigLoader mediaHealthConfigLoader;
  private final BeanFactory beanFactory;

  @PostConstruct
  public void createDiskSpaceHealthIndicators() {
    ArrayList<AbstractHealthIndicator> beans = new ArrayList<>();
    if (mediaHealthConfigLoader.getDrives() != null) {
      log.info("Creating {} dynamic DiskSpaceHealthIndicators", mediaHealthConfigLoader.getDrives().size());
      mediaHealthConfigLoader.getDrives().forEach(drive -> {
        DiskSpaceHealthIndicator indicator = new DiskSpaceHealthIndicator(new File(drive.getPath()), DataSize.ofMegabytes(100));
        log.info("Created indicator with name={} for path={}: bean={}", drive.getName(), drive.getPath(), indicator);
        
        // -> I would like to do something like: beanFactory.addBean(drive.getName(), indicator)
      });
    }
  }
}

然而,我的beanFactory只有getter,因此无法添加新创建的bean。

我还尝试使用BeanFactoryPostProcessor连接到bean创建中,但是,在执行回调时,配置属性(或加载它们的类)似乎还没有准备好。

@Slf4j
@Component
@RequiredArgsConstructor
public class DynamicHealthIndicatorFactory implements BeanFactoryPostProcessor {
  private final MediaHealthConfigLoader mediaHealthConfigLoader;

  @Override
  public void postProcessBeanFactory(final ConfigurableListableBeanFactory beanFactory) throws BeansException {
    // mediaHealthConfigLoader is null here :-(

    mediaHealthConfigLoader.getDrives().forEach(drive -> {
        DiskSpaceHealthIndicator newBean = new DiskSpaceHealthIndicator(new File(drive.getPath()), DataSize.ofMegabytes(100));
        beanFactory.initializeBean(newBean, drive.getName());
        beanFactory.registerSingleton(drive.getName(), newBean);
    });
  }
}

或者

@Slf4j
@Component
@ConfigurationProperties(prefix = "media.health") // commenting out the same annotation on MediaHealthConfigLoader
public class DynamicHealthIndicatorFactory implements BeanFactoryPostProcessor {
  private List<DriveConfig> drives;

  @Override
  public void postProcessBeanFactory(final ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
    // drives is null here :-(
    drives.forEach(drive -> {
        DiskSpaceHealthIndicator newBean = new DiskSpaceHealthIndicator(new File(drive.getPath()), DataSize.ofMegabytes(100));
        beanFactory.initializeBean(newBean, drive.getName());
        beanFactory.registerSingleton(drive.getName(), newBean);
    });
  }
}

但是,这两种方法都会导致NullPointerExceptions,因为在生命周期的这个点上,属性和自动构建的bean都准备好了。

我肯定我不是第一个尝试这样做的人,但我似乎没有找到正确的搜索词来找到解决方案。

共有1个答案

岑俊明
2023-03-14

这应该适用于ref:gist应用程序

@Configuration
@EnableConfigurationProperties
@AllArgsConstructor
class DiskSpaceConfig {
    private final MediaHealthConfigLoader mediaHealthConfigLoader;
    private final ConfigurableListableBeanFactory beanFactory;

    @PostConstruct
    public void registerBeans(){
        mediaHealthConfigLoader.getDrives().forEach(drive -> {
            DiskSpaceHealthIndicator newBean = new DiskSpaceHealthIndicator(new File(drive.getPath()), DataSize.ofMegabytes(100));
            beanFactory.initializeBean(newBean, drive.getName());
            beanFactory.registerSingleton(drive.getName(), newBean);
        });
    }
}

如果需要,可以禁用默认磁盘空间

management:
  health:
    diskspace:
      enabled: false
 类似资料:
  • 我想覆盖应用程序中定义的属性。属性通过cmd与应用程序中现有的其他属性。属性,但仅允许提供预定义值。 我需要的是通过应用程序中已经存在的命令行传递调度的参数。属性,它将更改在调度中定义的默认值。 我的问题是,当我从cmd执行jar文件时,它正在获取构建时存在于中的值。它没有覆盖现有值值。我想根据用户需要覆盖值。 Spring代码 应用属性 在cmd中

  • 我在src/main/resources下创建了2个文件: 应用程序。属性 第一个具有从env变量中获取值的属性,而后者具有固定值。 根据这里的具体情况,我以这样的方式推出了Spring靴: 然而,不会产生任何影响,并且应用程序是局部的。属性似乎被忽略。 有什么提示吗?

  • 我正在使用springfox swagger 2.8.0。我想根据我的需求定制我的Swagger文档和API版本路径。能够使用应用程序修改版本路径。属性,并且它正在正常运行: 斯普林福克斯。文档大摇大摆路径=/v2/可用性服务 http://localhost:8080/context-path/v2/availability-service 但是想要我的留档URL如下,如何使用applicati

  • Ember CLI,Ember的命令行界面提供了一个标准的项目结构,一组开发工具和一个插件系统。这允许Ember开发人员专注于构建应用程序,而不是构建使它们运行的​​支持结构。可通过ember --help显示Ember CLI提供的命令,或通过ember help <command-name>查看特定命令的信息。 创建应用程序 $ ember new super-rentals 上述ember

  • 我有一个移动的ios应用程序,它可以动态生成元素,我很难点击一些复选框,因为它们没有像web应用程序中常见的复选框那样有id或值,请看下面的图片。(很抱歉我不能展示所有图像,但app正在开发中) Appium inspector给了我xpath,但我不喜欢它,因为它会根据屏幕上看到的元素而改变,因为我必须向下滚动页面,这取决于设备,有时显示一些复选框有时不显示,因此例如[4]元素会改变。 我想知道

  • 我将使用ADFS作为身份提供者(IDP)实现对java应用程序的单点登录。通过OneLogin找到了此解决方案SSO,并使用了其示例应用程序。除此之外,还有另一个解决方案Shibboleth。 我想知道与我的上下文匹配的最佳解决方案是什么。之间,这不是Spring应用程序。 谢谢