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
你可以从主要方法本身来尝试。
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")
}
并且您可以为您设置的环境创建相应的文件。
嗯,我遇到的问题是,我的应用程序监听器执行任何豆已创建或自动装配完成之前过,所以它尚未加载应用程序属性。因此,我试图从类路径中手动加载属性文件,然后使用属性来初始化数据源并获取我的其余属性,并将它们注入环境中,以便它们在应用程序初始化时可用。 – cloudwalker
好吧,我明白了你的观点,并且我相信所提供的路径存在问题。 –
好吧,我已经明白了你的观点,并且我认为提供的路径存在问题,例如你提供的路径类似'/path/to/application-datasource.properties',但是spring不考虑类路径的子目录'src/main /资源/'默认情况下。我会相应地更新我的答案。 –