Spring Boot 嵌入式 Tomcat 未在 ApplicationListener 中加载外部属性文件 [英] Spring Boot embedded Tomcat not loading external properties file in ApplicationListener

查看:37
本文介绍了Spring Boot 嵌入式 Tomcat 未在 ApplicationListener 中加载外部属性文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个与嵌入式 Tomcat 一起运行的 SpringBoot 应用程序.此侦听器负责从 MySQL 数据库加载应用程序属性并将它们插入到环境中.它看起来像这样:

I have a SpringBoot app that is running with embedded Tomcat. This listener is responsible for loading the application properties from a MySQL database and inserting them into the Environment. It looks like this:

@Component
public class DbMigrationAndPropertyLoaderApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {

    private static final Logger LOGGER = LoggerFactory.getLogger(DbMigrationAndPropertyLoaderApplicationListener.class);

    private static final String PROPERTY_SOURCE_NAME = "applicationProperties";

    private final int order = Ordered.HIGHEST_PRECEDENCE + 4;

    private final PropertySourceProcessor propertySourceProcessor = new PropertySourceProcessor();

    @Override
    public int getOrder() {
        return this.order;
    }

    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        Properties databaseProperties;
        try {
            databaseProperties = PropertiesLoaderUtils.loadAllProperties("application-datasource.properties");
        } catch (IOException e) {
            throw new RuntimeException("Unable to load properties from application-datasource.properties. Please ensure that this file is on your classpath", e);
        }
    ConfigurableEnvironment environment = event.getEnvironment();
    Map<String, Object> propertySource = new HashMap<>();
    try {
        DataSource ds = DataSourceBuilder
                .create()
                .username(databaseProperties.getProperty("flyway.user"))
                .password(EncryptionUtil.decrypt(databaseProperties.getProperty("flyway.password")))
                .url(databaseProperties.getProperty("spring.datasource.url"))
                .driverClassName(databaseProperties.getProperty("spring.datasource.driver-class-name"))
                .build();

        LOGGER.debug("Running Flyway Migrations");
        //Run Flyway migrations. If this is the first time, it will create and populate the APPLICATION_PROPERTY table.
        Flyway flyway = new Flyway();
        flyway.setDataSource(ds);
        flyway.migrate();

        LOGGER.debug("Initializing properties from APPLICATION_PROPERTY table");
        //Fetch all properties

        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try {
            connection = ds.getConnection();
            preparedStatement = connection.prepareStatement("SELECT prop_key, prop_value FROM APPLICATION_PROPERTY");

            resultSet = preparedStatement.executeQuery();

            //Populate all properties into the property source
            while (resultSet.next()) {
                String propName = resultSet.getString("prop_key");
                propertySource.put(propName, propertySourceProcessor.decrypt(resultSet.getString("prop_value")));
            }

            //Create a custom property source with the highest precedence and add it to the Environment
            environment.getPropertySources().addFirst(new MapPropertySource(PROPERTY_SOURCE_NAME, propertySource));

我像这样调用应用程序:

I Invoke the application like this:

public static void main(String[] args) {
        ApplicationContext ctx = new SpringApplicationBuilder(PortalApplication.class)
                .listeners(new DbMigrationAndPropertyLoaderApplicationListener())
                .build(args)
                .run();

我想要做的是将 application-datasource.properties 文件外部化,以便它可以驻留在我的各种应用服务器(Dev、QA、Prod 等)上.但是,我无法让侦听器找到此属性文件,我不确定为什么.我尝试将 deployment.conf 文件中的 RUN_ARGS 属性设置为类似

What I'm trying to do is to externalize the application-datasource.properties file so it can reside on my various app servers (Dev, QA, Prod, etc.). However, I am unable to get the listener to find this properties file, and I'm not sure why. I tried setting the RUN_ARGS property in the deployment.conf file to something like

RUN_ARGS=--spring.config.location=/path/to/application-datasource.properties

RUN_ARGS=--spring.config.location=/path/to/application-datasource.properties

我还尝试将带有属性文件的目录添加到类路径中.我正在做的任何事情似乎都没有用,但我确定我只是在这里做一些愚蠢的事情.请注意,加载文件时我没有收到异常,结果属性只是空的.

I've also tried adding the directory with the properties file to the classpath. Nothing I'm doing seems to be working, but I'm sure I'm just doing something stupid here. Note that I don't get an Exception when loading the file, the resulting Properties are simply empty.

推荐答案

Spring Boot 使属性文件加载变得非常简单和轻松.加载属性时您无需费心,Spring Boot 就在那里.有很多方法可以使用 spring boot 加载属性文件,其中一些是:1) 只需使用 application-{Profile}.properties 在类路径上提供应用程序属性,然后将活动配置文件作为参数传递 --spring.profiles.active= profileName

Spring Boot made property file loading very easy and hassle free.You do not need to get bother while loading properties and spring boot is there. There are many ways to load properties file with spring boot some of there are: 1) simply provide application property on classpath with application-{Profile}.properties and just pass active profile as argument --spring.profiles.active= profileName

2) 带有 spring.config.location 的配置文件,其中加载的属性被定义为使用的环境属性.(我们可以从类路径或外部文件路径加载它.根据 spring boot 官方文档,默认情况下,配置的位置是 classpath:/,classpath:/config/,file:./,file:./config/. 结果搜索顺序是:

2) configuration file with spring.config.location in which loaded properties to be defined as an environment property used. (we can load it from classpath or external file path. As per spring boot official docs, By default, the configured locations are classpath:/,classpath:/config/,file:./,file:./config/. The resulting search order is:

file:./config/

file:./

classpath:/config/

classpath:/

配置自定义配置位置时,除了默认位置之外,还会使用它们.在默认位置之前搜索自定义位置.例如,如果配置了自定义位置 classpath:/custom-config/,file:./custom-config/,则搜索顺序变为:

When custom config locations are configured, they are used in addition to the default locations. Custom locations are searched before the default locations. For example, if custom locations classpath:/custom-config/,file:./custom-config/ are configured, the search order becomes:

file:./custom-config/

classpath:custom-config/

file:./config/

file:./

classpath:/config/

classpath:/

3) 你也可以在你的 @Configuration 类上使用 @PropertySource 注释

3) You can also use @PropertySource annotations on your @Configurationclasses

请参考 this 了解更多详情(24.4 和 24.5)

Please refer this for more details (24.4 and 24.5)

根据您的评论,您希望在创建任何 bean 之前加载属性,那么为什么要从类路径中获取它???您可以通过将其保留在相对文件路径上来使其更安全.从相对文件路径读取属性文件有几个好处.

As per your comment you want your properties loaded before any bean created then why you want it from class path??? You can make it more secure by keep it on relative file path. There are several benefits while reading property file from relative file path.

1) 服务器上相对文件路径上的属性文件是安全的,不能直接访问.2) 如果文件被修改,则不需要新补丁,您只需要重新启动该过程并使用更新的属性.3) 总而言之,修改所需的努力更少.

1) Property file on relative file path on server which is secure and no direct access. 2) If file is modified then new patch is not required, You just need to restart the process and updated properties will be utilized. 3) Over all less efforts required in modification.

下面是与您的要求完美匹配的示例:

Now below is the example which is perfect match with your requirements:

private static Properties loadConfigProps() {
        InputStream configStream = null;
        Properties _ConfigProps = null;
        try {
            String prjDir = System.getProperty("user.dir");
            String activeProfile = System.getProperty("activeProfile");
            int lastIndex = prjDir.lastIndexOf(File.separator);

            String configPath = prjDir.substring(0, lastIndex);
            configStream = new FileInputStream(new File(configPath
                    + File.separator + "_configurations" + File.separator + activeProfile + File.separator 
                    + "resources" + File.separator + "myDatabaseProperties.properties"));
            _ConfigProps = new Properties();
            _ConfigProps.load(configStream);

        } catch (Exception ex) {
            ex.printStackTrace();
            System.exit(1);
        } finally {
            if (null != configStream) {
                try {
                    configStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return _ConfigProps;
    }

现在你只需要做两件事,1) 创建目录2) 在运行时提供实际的活动配置文件

Now you just need to do two things, 1) Create Directory 2) Provide actual active profile on run time

1)创建目录:

如果您的工作目录是 ./path/to/applications/active_project 然后在 ./path/to/applications/ 目录中创建新文件夹:

If your working directory is ./path/to/applications/active_project then create new folder in ./path/to/applications/ directory:

./path/to/applications/
             -active_project/
             -_configurations/
                            -QA/myDatabaseProperties.properties
                            -Dev/myDatabaseProperties.properties
                            -Prod/myDatabaseProperties.properties

2) 在运行时提供实际的活动配置文件:

2) Provide actual active profile on run time:

java -DactiveProfile=Dev -jar jarFileName.jar

这篇关于Spring Boot 嵌入式 Tomcat 未在 ApplicationListener 中加载外部属性文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆