Spring Boot: 如何动态设置Spring属性

8

在Spring Boot应用程序的application.properties文件中,可以定义许多属性。

但是我想从代码内部传递属性来配置SSL到Spring。

server.ssl.enabled=true
# The format used for the keystore 
server.ssl.key-store-type=PKCS12
# The path to the keystore containing the certificate
server.ssl.key-store=keys/keystore.jks
# The password used to generate the certificate
server.ssl.key-store-password=changeit
# The alias mapped to the certificate
server.ssl.key-alias=tomcat

这些属性将作为Spring定义的属性在application.properties中使用。我只想根据某些逻辑从代码中设置它们。

对于非Spring应用程序,我仍然有一些想法,它们可以作为应用程序、会话或上下文属性传递,但我不知道在Spring中如何实现。

任何帮助都将不胜感激。

4个回答

12

既然您知道如何在Spring Boot应用程序中启用SSL的属性,那么您可以像这样以编程方式传递这些属性到您的Spring Boot应用程序中:

@SpringBootApplication
public class SpringBootTestApplication {
    public static void main(String[] args) {

//      SpringApplication.run(SpringBootTestApplication.class, args);

        Properties props = new Properties();
        props.put("server.ssl.key-store", "/home/ajit-soman/Desktop/test/keystore.p12");
        props.put("server.ssl.key-store-password", "123456");
        props.put("server.ssl.key-store-type", "PKCS12");
        props.put("server.ssl.key-alias", "tomcat");

        new SpringApplicationBuilder(SpringBootTestApplication.class)
            .properties(props).run(args);
    }
}

正如您所看到的,我已经将以下内容注释掉了: SpringApplication.run(SpringBootTestApplication.class, args); 并使用SpringApplicationBuilder类添加了一些属性到应用程序中。

现在,这些属性在程序中被定义,您可以根据自己的需求应用条件并更改属性值。


2
这个会考虑我在application.properties中定义的其他属性吗? - Onki
1
是的。它将考虑您在属性文件中添加的其他属性。 - Ajit Soman
Properties类来自哪里? - Ben Creasy
1
@BenCreasy java.util 有一个 Properties 类。 - Ajit Soman
这种方法的缺点是属性被设置为默认值。这意味着它们具有最低的优先级,任何以其他方式设置的相同属性,例如通过配置文件,都会覆盖默认值。我认为,与安全相关的事项不应该依赖于这一点,但这只是个人观点。 - undefined

10

在Spring Boot中,您可以使用System.getProperty(String key)System.setProperty(String key, String value)来读取和编辑属性。

public static void main(String[] args) {

    // set properties
    System.setProperty("server.ssl.enabled", "true");
    System.setProperty("server.ssl.key-store-type", "PKCS12");
    System.setProperty("server.ssl.key-store", "keys/keystore.jks");
    System.setProperty("server.ssl.key-store-password", "changeit");
    System.setProperty("server.ssl.key-alias", "tomcat");

    // run
    SpringApplication.run(DemoApplication.class, args);
}

注意:

这将覆盖静态属性(不是全部,而是被覆盖的那些)。


百分之百正常工作且简单易懂,不需要修改Spring Boot的xxxxxx应用程序运行类。 - diego matos - keke
@TienDoNam,我不清楚你在最后一行的意思:“这将覆盖静态属性(不是全部,只是被覆盖的)”。你是指在Java中设置的属性将覆盖application.properties文件中的属性,还是反过来? - sebasira
2
经过反复试验,我可以保证通过System.setProperty定义的值优先于application.properties文件中的值。 - sebasira

7
您需要定义并注册一个ApplicationListener,例如:
public class DynamicPropertiesListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
  public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
    ConfigurableEnvironment environment = event.getEnvironment();
    // modify the properties here, see the ConfigurableEnvironment javadoc
  }
}

现在注册监听器。如果您使用main方法运行应用程序:
SpringApplication app = new SpringApplication(primarySources);
app.addListeners(new DynamicPropertiesListener());
app.run(args);

或者,更好的方法是创建一个名为 src\main\resources\META-INF\spring.factories 的文件,并将以下内容添加到其中:
org.springframework.context.ApplicationListener=x.y.DynamicPropertiesListener

其中x.y是您监听器的包。


使用这种方法对我来说是最好的解决方案。此外,以这种方式定义的属性优先于`application.properties' 文件中的值,这正是我所需要的。 - sebasira

0

另外一种添加选项的方法是实现BeanPostProcessor类。它提供了两个方法:

postProcessAfterInitializationpostProcessBeforeInitialization

工厂钩子,允许自定义修改新的bean实例 - 例如,检查标记接口或使用代理包装bean。通常,通过标记接口或类似方式填充bean的后处理器将实现postProcessBeforeInitialization(java.lang.Object, java.lang.String),而将bean包装为代理的后处理器通常会实现postProcessAfterInitialization(java.lang.Object, java.lang.String)。

我正在使用Spring Boot与Spring Kafka,并且只想更改本地配置文件。

在我的代码示例中,我使用它来覆盖Kafka位置属性,因为对于SSL,它不会从类路径中读取。

所以这就是代码:

import io.confluent.kafka.schemaregistry.client.SchemaRegistryClientConfig;
import java.io.IOException;
import java.util.Arrays;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.apache.kafka.common.config.SslConfigs;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;

@Configuration
@RequiredArgsConstructor
public class KafkaConfiguration implements BeanPostProcessor {

  @Value("${spring.kafka.ssl.key-store-location:}")
  private Resource keyStoreResource;
  @Value("${spring.kafka.properties.schema.registry.ssl.truststore.location:}")
  private Resource trustStoreResource;
  private final Environment environment;

  @SneakyThrows
  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    if (bean instanceof KafkaProperties) {
      KafkaProperties kafkaProperties = (KafkaProperties) bean;
      if(isLocalProfileActive()) {
        configureStoreLocation(kafkaProperties);
      }
    }
    return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
  }

  private boolean isLocalProfileActive() {
    return Arrays.stream(environment.getActiveProfiles()).anyMatch(profile -> "local".equals(profile));
  }

  private void configureStoreLocation(KafkaProperties kafkaProperties) throws IOException {
    kafkaProperties.getSsl().setKeyStoreLocation(new FileSystemResource(keyStoreResource.getFile().getAbsolutePath()));
    kafkaProperties.getProperties().put(SchemaRegistryClientConfig.CLIENT_NAMESPACE + SslConfigs.SSL_KEYSTORE_LOCATION_CONFIG, keyStoreResource.getFile().getAbsolutePath());
    kafkaProperties.getSsl().setTrustStoreLocation(new FileSystemResource(trustStoreResource.getFile().getAbsolutePath()));
    kafkaProperties.getProperties().put(SchemaRegistryClientConfig.CLIENT_NAMESPACE + SslConfigs.SSL_TRUSTSTORE_LOCATION_CONFIG, trustStoreResource.getFile().getAbsolutePath());
  }

}

这样我就可以在我的属性文件中写入:

spring.kafka.ssl.key-store-location=classpath:mykeystore.jks

代码将从中获取绝对路径并进行设置。还可以根据配置文件进行过滤。

需要注意的是,BeanPostProcessor会对每个bean运行,所以请确保过滤您想要的内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接