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

Spring Boot嵌入Tomcat未在ApplicationListener中加载外部属性文件,spring,tomcat,spring-boot,Spring,Tomcat,Spring Boot,我有一个SpringBoot应用程序,它正在运行嵌入式Tomcat。此侦听器负责从MySQL数据库加载应用程序属性,并将它们插入到环境中。看起来是这样的: @Component public class DbMigrationAndPropertyLoaderApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered { private st

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

@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=/path/to/application-datasource.properties


我还尝试将带有属性文件的目录添加到类路径中。我所做的一切似乎都不起作用,但我肯定我只是在做一些愚蠢的事情。请注意,加载文件时没有出现异常,生成的属性只是空的。

您可以从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")
    }
publicstaticvoidmain(字符串[]args){
SpringApplication app=新的SpringApplication(Application.class);
映射属性=新的HashMap();
properties.put(“spring.profiles.default”、“local”);
app.setDefaultProperties(属性);
Environment env=app.run(args.getEnvironment();
env.getProperty(“spring.application.name”)
}

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

Spring Boot使属性文件的加载变得非常简单和轻松。在加载属性和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
注释

有关更多详细信息,请参阅(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\u 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

嗯,我遇到的问题是,我的应用程序侦听器在创建任何bean之前或在自动连接完成之前执行,因此它尚未加载应用程序属性。所以,,我正在尝试从类路径手动加载属性文件,然后使用属性初始化数据源,获取我的其余属性并将它们注入到环境中,以便它们在应用程序初始化时可用。好的,我明白你的意思,我相信提供的路径有问题。好的,我明白你的意思,我相信有问题提供的路径存在问题,例如,您提供的路径类似于“/path/to/application datasource.properties”,但spring默认情况下不考虑类路径“src/main/resources/”的子目录。我将相应地更新我的答案。这就成功了!我稍微修改了你的代码,但那太完美了。非常感谢。在检查相对路径之前,我只是添加了一些东西以将路径作为系统属性加载;if(StringUtils.isEmpty(configPath)){String projectDir=System.getProperty(“user.dir”);int-lastIndex=projectDir.lastIndexOf(File.separator);configPath=projectDir.substring(0,lastIndex)+File.separator+“config”;}