ConfigurationProperties的初始化

ConfigurationProperties的初始化

本文介绍了Spring ConfigurationProperties的初始化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我具有以下来自application.properties的属性的类映射部分:

I have the following class mapping parts of the properties from the application.properties:

@Component
@ConfigurationProperties(prefix = "city")
@Getter
@Setter
public class CityProperties {
    private int populationAmountWorkshop;
    private double productionInefficientFactor;
    private Loaner loaner = new Loaner();
    private Tax tax = new Tax();
    private Guard pikeman = new Guard();
    private Guard bowman = new Guard();
    private Guard crossbowman = new Guard();
    private Guard musketeer = new Guard();

    @Getter
    @Setter
    public static class Loaner {
        private int maxRequest;
        private int maxAgeRequest;
        private int maxNbLoans;
    }

    @Getter
    @Setter
    public static class Tax {
        private double poor;
        private double middle;
        private double rich;
        private int baseHeadTax;
        private int basePropertyTax;
    }

    @Getter
    @Setter
    public static class Guard {
        private int weeklySalary;
    }
}

application.properties的一部分:

#City
# Amount of inhabitants to warrant the city to have one workshop
city.populationAmountWorkshop=2500
# Factor that is applied on the efficient production to get the inefficient production
city.productionInefficientFactor=0.6
# Maximum requests per loaner
city.loaner.maxRequest=6
# Maximum  age of loan request in weeks
city.loaner.maxAgeRequest=4
# Maximum loan offers per loaner
city.loaner.maxNbLoans=3
# Weekly tax value factor for the various population per 100 citizens
city.tax.poor=0
city.tax.middle=0.6
city.tax.rich=2.0
city.tax.baseHeadTax=4
city.tax.basePropertyTax=280
city.pikeman.weeklySalary=3
city.bowman.weeklySalary=3
city.crossbowman.weeklySalary=4
city.musketeer.weeklySalary=6

然后这是测试设置的应用程序:

Then this is the application for the test setup:

@SpringBootApplication
@Import({ServerTestConfiguration.class})
@ActiveProfiles("server")
@EnableConfigurationProperties
@PropertySource(value = {"application.properties", "server.properties", "bean-test.properties"})
public class SavegameTestApplication {
}

这些是ServerTestConfiguration类的注释,所有其他导入的配置与我在生产案例中使用的相同:

These are annotations on the ServerTestConfiguration class all other imported confugrations are the same as I use in the production case as well:

@Configuration
@EnableAutoConfiguration
@Import(value = {ClientServerInterfaceServerConfiguration.class, ServerConfiguration.class, ImageConfiguration.class})
public class ServerTestConfiguration {
  ...
}

最后是我的测试类的构造函数,用于初始化Spring-Boot应用程序:

And finally the constructor of my test class that initializes the Spring-Boot application:

public CityWallSerializationTest() {
    SpringApplicationBuilder builder = new SpringApplicationBuilder(SavegameTestApplication.class);
    DependentAnnotationConfigApplicationContext context = (DependentAnnotationConfigApplicationContext)
            builder.contextClass(DependentAnnotationConfigApplicationContext.class).profiles("server").run();
    setContext(context);
    setClientServerEventBus((AsyncEventBus) context.getBean("clientServerEventBus"));
    IConverterProvider converterProvider = context.getBean(IConverterProvider.class);
    BuildProperties buildProperties = context.getBean(BuildProperties.class);
    Archiver archiver = context.getBean(Archiver.class);
    IDatabaseDumpAndRestore databaseService = context.getBean(IDatabaseDumpAndRestore.class);
    TestableLoadAndSaveService loadAndSaveService = new TestableLoadAndSaveService(context, converterProvider,
            buildProperties, archiver, databaseService);
    setLoadAndSaveService(loadAndSaveService);
}

这在我的生产代码中可以正常工作,但是当我想使用Spring Boot应用程序编写一些测试时,值不会初始化.

This works fine in my production code, however when I want to write some tests using a spring boot application the values are not initialized.

在构造函数的末尾打印出CityProperties会得到以下输出:

Printing out the CityProperties at the end of the constructor results in this output:

我想了解Spring如何处理这些ConfigurationProperties带注释类的初始化,可以这么说魔术是如何发生的.我想知道这一点,以便正确调试应用程序以找出错误的地方.

I would like to understand how Spring handles the initialization of these ConfigurationProperties annotated classes, how the magic happens so to speak. I want to know this in order to properly debug the application to figure out where it goes wrong.

生产性代码是JavaFX应用程序,它使整个初始化过程更加复杂:

The productive code is a JavaFX application, that makes the whole initialization a bit more complicated:

@Slf4j
@SpringBootApplication
@Import(StandaloneConfiguration.class)
@PropertySource(value = {"application.properties", "server.properties"})
public class OpenPatricianApplication extends Application implements IOpenPatricianApplicationWindow {

    private StartupService startupService;
    private GamePropertyUtility gamePropertyUtility;

    private int width;
    private int height;
    private boolean fullscreen;
    private Stage primaryStage;

    private final AggregateEventHandler<KeyEvent> keyEventHandlerAggregate;
    private final MouseClickLocationEventHandler mouseClickEventHandler;

    private ApplicationContext context;

    public OpenPatricianApplication() {
        width = MIN_WIDTH;
        height = MIN_HEIGHT;
        this.fullscreen = false;
        keyEventHandlerAggregate = new AggregateEventHandler<>();

        CloseApplicationEventHandler closeEventHandler = new CloseApplicationEventHandler();
        mouseClickEventHandler = new MouseClickLocationEventHandler();
        EventHandler<KeyEvent> fullScreenEventHandler = event -> {
            try {
                if (event.getCode().equals(KeyCode.F) && event.isControlDown()) {
                    updateFullscreenMode();
                }
            } catch (RuntimeException e) {
                log.error("Failed to switch to/from fullscreen mode", e);
            }
        };
        EventHandler<KeyEvent> closeEventWindowKeyHandler = event -> {
            if (event.getCode().equals(KeyCode.ESCAPE)) {
                log.info("Pressed ESC");
                context.getBean(MainGameView.class).closeEventView();
            }
        };
        addKeyEventHandler(closeEventHandler);
        addKeyEventHandler(fullScreenEventHandler);
        addKeyEventHandler(closeEventWindowKeyHandler);
    }

    /**
     * Add a key event handler to the application.
     * @param eventHandler to be added.
     */
    private void addKeyEventHandler(EventHandler<KeyEvent> eventHandler) {
        keyEventHandlerAggregate.addEventHandler(eventHandler);
    }

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void init() {
        SpringApplicationBuilder builder = new SpringApplicationBuilder(OpenPatricianApplication.class);
        context = builder.contextClass(DependentAnnotationConfigApplicationContext.class).profiles("standalone")
                .run(getParameters().getRaw().toArray(new String[0]));
        this.startupService = context.getBean(StartupService.class);
        this.gamePropertyUtility = context.getBean(GamePropertyUtility.class);
        if (startupService.checkVersion()) {
            startupService.logEnvironment();

            CommandLineArguments cmdHelper = new CommandLineArguments();
            Options opts = cmdHelper.createCommandLineOptions();
            CommandLine cmdLine = cmdHelper.parseCommandLine(opts, getParameters().getRaw().toArray(new String[getParameters().getRaw().size()]));
            if (cmdLine.hasOption(CommandLineArguments.HELP_OPTION)){
                cmdHelper.printHelp(opts);
                System.exit(0);
            }
            if (cmdLine.hasOption(CommandLineArguments.VERSION_OPTION)) {
                System.out.println("OpenPatrician version: "+OpenPatricianApplication.class.getPackage().getImplementationVersion());
                System.exit(0);
            }
            cmdHelper.persistAsPropertyFile(cmdLine);
        }
    }

    @Override
    public void start(Stage primaryStage) {
        this.primaryStage = primaryStage;
        this.primaryStage.setMinWidth(MIN_WIDTH);
        this.primaryStage.setMinHeight(MIN_HEIGHT);
        primaryStage.getIcons().add(new Image(getClass().getResourceAsStream("/icons/trade-icon.png")));
        UIFactory uiFactory = context.getBean(UIFactory.class);
        uiFactory.setApplicationWindow(this);
        BaseStartupScene startupS = uiFactory.getStartupScene();
        Scene defaultScene = new Scene(startupS.getRoot(), width, height);
        defaultScene.getStylesheets().add("/styles/font.css");

        this.fullscreen = Boolean.valueOf((String) gamePropertyUtility.getProperties().get("window.fullscreen"));
        startupS.setSceneChangeable(this);
        defaultScene.setOnMousePressed(mouseClickEventHandler);
        defaultScene.setOnKeyPressed(keyEventHandlerAggregate);
        try {
            CheatKeyEventListener cheatListener = context.getBean(CheatKeyEventListener.class);
            if (cheatListener != null) {
                addKeyEventHandler(cheatListener);
            }
        } catch (Exception e) {
            // the cheat listener is no defined for the context.
            e.printStackTrace();
        }

        setCursor(defaultScene);

        primaryStage.setFullScreen(fullscreen);
        primaryStage.setFullScreenExitHint("");
        primaryStage.setTitle("OpenPatrician");
        primaryStage.setScene(defaultScene);
        primaryStage.show();
    }

    private void setCursor(Scene scene) {
        URL url = getClass().getResource("/icons/64/cursor.png");
        try {
            Image img = new Image(url.openStream());
            scene.setCursor(new ImageCursor(img));
        } catch (IOException e) {
            log.warn("Failed to load cursor icon from {}", url);
        }
    }

    /**
     * @see SceneChangeable#changeScene(OpenPatricianScene)
     */
    @Override
    public void changeScene(final OpenPatricianScene scene) {
        primaryStage.getScene().setOnMousePressed(mouseClickEventHandler);
        primaryStage.getScene().setOnKeyPressed(keyEventHandlerAggregate);

        primaryStage.getScene().setRoot(scene.getRoot());
    }
    /**
     * Toggle between full screen and non full screen mode.
     */
    public void updateFullscreenMode() {
        fullscreen = !fullscreen;
        primaryStage.setFullScreen(fullscreen);
    }

    @Override
    public double getSceneWidth() {
        return primaryStage.getScene().getWidth();
    }

    @Override
    public double getSceneHeight() {
        return primaryStage.getScene().getHeight();
    }

    @Override
    public void stop() throws Exception {
        System.out.println("Stopping the UI Application");

        stopUIApplicationContext();
        super.stop();
    }

    /**
     * Closing the application context for the user interface.
     */
    private void stopUIApplicationContext() {
        AsyncEventBus eventBus = (AsyncEventBus) context.getBean("clientServerEventBus");
        eventBus.post(new GameStateChange(EGameStatusChange.SHUTDOWN));
        ((AbstractApplicationContext)context).close();
    }
}

推荐答案

处理ConfigurationProperties绑定的类为 ConfigurationPropertiesBindingPostProcessor .

在这种特殊情况下,事实证明,加载的唯一属性文件是测试项目的类路径中存在的application.properties,而不是实际包含正确键值对的application.properties.

In this particular case it turned out that the only properties file loaded was the application.properties that was present on the class path from the test project instead of the application.properties that actually contains the proper key value pairs.

使用-Dlogging.level.org.springframework=DEBUG运行应用程序,然后在输出中查找以下内容时,可以在启动时看到它:

This can be seen at startup when running the application with -Dlogging.level.org.springframework=DEBUG and then look in the output for:

2019-12-31 10:54:49,884 [main] DEBUG  o.s.b.SpringApplication : Loading source class ch.sahits.game.savegame.SavegameTestApplication
2019-12-31 10:54:49,908 [main] DEBUG  o.s.b.c.c.ConfigFileApplicationListener : Loaded config file 'file:<path>/OpenPatrician/OpenPatricianModel/target/classes/application.properties' (classpath:/application.properties)
2

第二行指定要加载的application.properties的位置.

The second line specifies the location of the application.properties that is loaded.

这篇关于Spring ConfigurationProperties的初始化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-20 21:41