Spring Boot嵌入式Tomcat未在ApplicationListener中加载外部属性文件

时间:2017-08-01 14:53:18

标签: spring tomcat spring-boot

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

@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));

我像这样调用应用程序:

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属性设置为

RUN_ARGS = - spring.config.location = /路径/到/ application-datasource.properties

我也尝试将带有属性文件的目录添加到类路径中。我所做的一切似乎都在起作用,但我确信我只是在做一些愚蠢的事情。请注意,加载文件时我没有收到异常,结果属性只是空的。

2 个答案:

答案 0 :(得分:1)

Spring Boot使得属性文件加载变得非常容易和轻松。在加载属性和弹簧引导时,你不需要费心。 有许多方法可以使用spring boot加载属性文件,其中一些方法有: 1)只需使用application-{Profile}.properties在类路径上提供应用程序属性,并将活动的配置文件作为参数--spring.profiles.active= profileName传递

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

file:./config/

file:./

classpath:/config/

classpath:/

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

file:./custom-config/

classpath:custom-config/

file:./config/

file:./

classpath:/config/

classpath:/

3)您还可以在@PropertySource

上使用@Configuration注释

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

已编辑的答案

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

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

以下是符合您要求的示例:

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)在运行时提供实际的活动配置文件

1)创建目录:

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

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

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

java -DactiveProfile=Dev -jar jarFileName.jar

答案 1 :(得分:0)

你可以从main方法本身尝试这个。

  public static void main(String[] args){
    SpringApplication app = new SpringApplication(Application.class);
    Map<String, Object> properties =  new HashMap<>();
    properties.put("spring.profiles.default", "local");
    app.setDefaultProperties(properties);
    Environment env = app.run(args).getEnvironment();
    env.getProperty("spring.application.name")
    }

您可以为您设置的环境创建相应的文件。