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

问题描述:

我有一个运行嵌入式Tomcat的SpringBoot应用程序。此侦听器负责从MySQL数据库加载应用程序属性并将其插入到环境中。它看起来像这样:Spring Boot嵌入式Tomcat无法在ApplicationListener中加载外部属性文件

@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

我也尝试添加目录与属性文件的类路径。我所做的没有任何事情似乎有效,但我确信我只是在这里做一些愚蠢的事情。请注意,在加载文件时,我没有收到异常,所产生的属性只是空的。

Spring Boot使属性文件加载非常容易,无忧无虑。加载属性和弹簧启动时不需要费心。 有许多方法来加载属性与弹簧引导文件的一些有: 1)简单地提供对类路径应用属性与application-{Profile}.properties和只传递有效简作为参数--spring.profiles.active= profileName

2)配置文件与spring.config.location其中装载属性被定义为使用的环境属性。 (我们可以从类路径或外部文件路径 加载它作为每春天启动的官方文档,默认情况下,配置的位置是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注解类

请参考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 
+0

嗯,我遇到的问题是,我的应用程序监听器执行任何豆已创建或自动装配完成之前过,所以它尚未加载应用程序属性。因此,我试图从类路径中手动加载属性文件,然后使用属性来初始化数据源并获取我的其余属性,并将它们注入环境中,以便它们在应用程序初始化时可用。 – cloudwalker

+0

好吧,我明白了你的观点,并且我相信所提供的路径存在问题。 –

+0

好吧,我已经明白了你的观点,并且我认为提供的路径存在问题,例如你提供的路径类似'/path/to/application-datasource.properties',但是spring不考虑类路径的子目录'src/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") 
    } 

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