Recarregando arquivos de propriedades no Spring
1. Visão geral
Neste tutorial, vamos mostrar como recarregarproperties in a Spring application.
2. Propriedades de leitura na primavera
Temos diferentes opções para acessar propriedades no Spring:
-
Environment - podemos injetarEnvironmente usarEnvironment#getProperty para ler uma determinada propriedade. Environment contém diferentes fontes de propriedade, como propriedades do sistema, parâmetros-D eapplication.properties (.yml). Além disso, fontes de propriedades extras podem ser adicionadas aEnvironment usando@PropertySource.
-
Properties - podemos carregar arquivos de propriedades em uma instânciaProperties e, em seguida, usá-los em um bean chamandoproperties.get(“property”).
-
@Value - podemos injetar uma propriedade específica em um bean com a anotação@Value($\{‘property'}).
-
@ConfigurationProperties - podemos usar@ConfigurationProperties para carregar propriedades hierárquicas em um bean.
3. Recarregando propriedades de arquivo externo
Para alterar as propriedades em um arquivo durante o tempo de execução, devemos colocá-lo em algum lugar fora do jar. Então, diremos ao Spring onde ele está com o parâmetro parameter–spring.config.location=file://\{path to file} da linha de comando. Ou podemos colocá-lo emapplication.properties.
Nas propriedades baseadas em arquivo, teremos que escolher uma forma de recarregar o arquivo. Por exemplo, podemos desenvolver um terminal ou planejador para ler o arquivo e atualizar as propriedades.
Uma biblioteca útil para recarregar o arquivo é ocommons-configuration do Apache. Podemos usarPropertiesConfiguration comReloadingStrategy diferentes.
Vamos adicionarcommons-configuration ao nossopom.xml:
commons-configuration
commons-configuration
1.10
Em seguida, adicionamos um método para criar um beanPropertiesConfiguration, que usaremos mais tarde:
@Bean
@ConditionalOnProperty(name = "spring.config.location", matchIfMissing = false)
public PropertiesConfiguration propertiesConfiguration(
@Value("${spring.config.location}") String path) throws Exception {
String filePath = new File(path.substring("file:".length())).getCanonicalPath();
PropertiesConfiguration configuration = new PropertiesConfiguration(
new File(filePath));
configuration.setReloadingStrategy(new FileChangedReloadingStrategy());
return configuration;
}
No código acima, definimosFileChangedReloadingStrategy como a estratégia de recarregamento com um atraso de atualização padrão. Isso significa quePropertiesConfiguration verifica a data de modificação do arquivoif its last check was before 5000ms ago.
Podemos personalizar o atraso usandoFileChangedReloadingStrategy#setRefreshDelay.
3.1. RecarregandoEnvironment Propriedades
Se quisermos recarregar as propriedades carregadas por meio de uma instânciaEnvironment, temos queextend the PropertySource and then use PropertiesConfiguration to return new values from the external property file.
Vamos começar estendendoPropertySource:
public class ReloadablePropertySource extends PropertySource {
PropertiesConfiguration propertiesConfiguration;
public ReloadablePropertySource(String name, PropertiesConfiguration propertiesConfiguration) {
super(name);
this.propertiesConfiguration = propertiesConfiguration;
}
public ReloadablePropertySource(String name, String path) {
super(StringUtils.isEmpty(name) ? path : name);
try {
this.propertiesConfiguration = new PropertiesConfiguration(path);
this.propertiesConfiguration.setReloadingStrategy(new FileChangedReloadingStrategy());
} catch (Exception e) {
throw new PropertiesException(e);
}
}
@Override
public Object getProperty(String s) {
return propertiesConfiguration.getProperty(s);
}
}
Substituímos o métodogetProperty para delegá-lo aPropertiesConfiguration#getProperty. Portanto, ele verificará os valores atualizados em intervalos de acordo com nosso atraso de atualização.
Agora, vamos adicionar nossasReloadablePropertySource às fontes de propriedade deEnvironment:
@Configuration
public class ReloadablePropertySourceConfig {
private ConfigurableEnvironment env;
public ReloadablePropertySourceConfig(@Autowired ConfigurableEnvironment env) {
this.env = env;
}
@Bean
@ConditionalOnProperty(name = "spring.config.location", matchIfMissing = false)
public ReloadablePropertySource reloadablePropertySource(PropertiesConfiguration properties) {
ReloadablePropertySource ret = new ReloadablePropertySource("dynamic", properties);
MutablePropertySources sources = env.getPropertySources();
sources.addFirst(ret);
return ret;
}
}
We’ve added the new property source as the first item porque queremos que ele substitua qualquer propriedade existente com a mesma chave.
Vamos criar um bean para ler uma propriedade deEnvironment:
@Component
public class EnvironmentConfigBean {
private Environment environment;
public EnvironmentConfigBean(@Autowired Environment environment) {
this.environment = environment;
}
public String getColor() {
return environment.getProperty("application.theme.color");
}
}
Se precisarmos adicionar outras fontes de propriedades externas recarregáveis, primeiro temos que implementar nossoPropertySourceFactory personalizado:
public class ReloadablePropertySourceFactory extends DefaultPropertySourceFactory {
@Override
public PropertySource> createPropertySource(String s, EncodedResource encodedResource)
throws IOException {
Resource internal = encodedResource.getResource();
if (internal instanceof FileSystemResource)
return new ReloadablePropertySource(s, ((FileSystemResource) internal)
.getPath());
if (internal instanceof FileUrlResource)
return new ReloadablePropertySource(s, ((FileUrlResource) internal)
.getURL()
.getPath());
return super.createPropertySource(s, encodedResource);
}
}
Então podemos anotar a classe de um componente com@PropertySource:
@PropertySource(value = "file:path-to-config", factory = ReloadablePropertySourceFactory.class)
3.2. Recarregando Instância de Propriedades
Environment é uma escolha melhor do queProperties, especialmente quando precisamos recarregar as propriedades de um arquivo. No entanto, se precisarmos, podemos estender ojava.util.Properties:
public class ReloadableProperties extends Properties {
private PropertiesConfiguration propertiesConfiguration;
public ReloadableProperties(PropertiesConfiguration propertiesConfiguration) throws IOException {
super.load(new FileReader(propertiesConfiguration.getFile()));
this.propertiesConfiguration = propertiesConfiguration;
}
@Override
public String getProperty(String key) {
String val = propertiesConfiguration.getString(key);
super.setProperty(key, val);
return val;
}
// other overrides
}
SubstituímosgetProperty e suas sobrecargas e, em seguida, delegamos a uma instânciaPropertiesConfiguration. Agora, podemos criar um bean dessa classe e injetá-lo em nossos componentes.
3.3. Recarregando Bean com@ConfigurationProperties
Para obter o mesmo efeito com@ConfigurationProperties, precisaríamos reconstruir a instância.
Mas, o Spring só criará uma nova instância de componentes com escopoprototype ourequest.
Portanto, nossa técnica para recarregar o ambiente também funcionará para eles, mas para singletons, não temos escolha, exceto a implementação de um terminal para destruir e recriar o bean, ou para manipular a propriedade recarregar dentro do próprio bean.
3.4. Recarregando Bean com@Value
A anotação@Value apresenta as mesmas limitações que@ConfigurationProperties.
4. Recarregando propriedades por atuador e nuvem
Spring Actuator fornece diferentes pontos de extremidade para integridade, métricas e configurações, mas nada para atualizar os beans. Portanto, precisamos que o Spring Cloud adicione um endpoint/refresh a ele. Este endpoint recarrega todas as fontes de propriedade deEnvironmente, em seguida, publica umhttps://static.javadoc.io/org.springframework.cloud/spring-cloud-commons-parent/1.1.9.RELEASE/org/springframework/cloud/context/environment/EnvironmentChangeEvent.html.
Spring Cloud também introduziu@RefreshScope, e podemos usá-lo para classes de configuração ou beans. Como resultado, o escopo padrão serárefresh em vez desingleton.
Usando o escoporefresh, o Spring limpará seu cache interno desses componentes em umEnvironmentChangeEvent. Então, no próximo acesso ao bean, uma nova instância é criada. __
Vamos começar adicionandospring-boot-starter-actuator ao nossopom.xml:
org.springframework.boot
spring-boot-starter-actuator
Então, vamos importar tambémspring-cloud-dependencies:
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
Greenwich.SR1
E então adicionamosspring-cloud-starter:
org.springframework.cloud
spring-cloud-starter
Por fim, vamos ativar o ponto de extremidade de atualização:
management.endpoints.web.exposure.include=refresh
Quando usamos Spring Cloud, podemos configurar umConfig Server para gerenciar as propriedades, mas também podemos continuar com nossos arquivos externos. Agora, podemos lidar com dois outros métodos de leitura de propriedades:@Valuee@ConfigurationProperties.
4.1. Atualize o feijão com@ConfigurationProperties
Vamos mostrar como usar@ConfigurationProperties com@RefreshScope:
@Component
@ConfigurationProperties(prefix = "application.theme")
@RefreshScope
public class ConfigurationPropertiesRefreshConfigBean {
private String color;
public void setColor(String color) {
this.color = color;
}
//getter and other stuffs
}
Nosso bean está lendo “color” propriedade da raiz“application.theme” propriedade. Note that we do need the setter method, per Spring’s documentation.
Depois de alterar o valor de “application.theme.color” em nosso arquivo de configuração externo, podemos chamar/refresh, então, podemos obter o novo valor do bean no próximo acesso.
4.2. Atualize o feijão com@Value
Vamos criar nosso componente de amostra:
@Component
@RefreshScope
public class ValueRefreshConfigBean {
private String color;
public ValueRefreshConfigBean(@Value("${application.theme.color}") String color) {
this.color = color;
}
//put getter here
}
O processo de atualização é o mesmo que acima.
No entanto, é necessário observar que/refresh não funcionará para beans com um escoposingleton explícito.
5. Conclusão
Neste tutorial, demonstramos como recarregar propriedades com ou sem recursos do Spring Cloud. Além disso, mostramos as armadilhas e exceções de cada uma das técnicas.
O código completo está disponívelin our GitHub project.