Implementando o Ambiente no Spring Boot Customizado

O módulo de ambiante é essencial para lidar com configurações variáveis entre diferentes ambientes como desenvolvimento, teste e produção. Vamos explorar uma implementação funcional deste conceito.

Fontes de Configuração

Diferentes origens de propriedades podem ser abstraídas através do seguinte padrão:


public abstract class OrigemConfiguracao<T> {
    protected final String identificador;
    protected final T origem;
    
    public abstract Object obterPropriedade(String chave);
    
    public static class OrigemReservada extends OrigemConfiguracao<Object> {
        @Override
        public Object obterPropriedade(String chave) {
            return null;
        }
    }
}

Propriedades Estruturadas

Para fontes que permitem iteração:


public abstract class OrigemEnumeravel<T> 
    extends OrigemConfiguracao<T> {
    
    public abstract String[] obterChaves();
}

Implementação baseada em mapa:


public class OrigemMapaConfig 
    extends OrigemEnumeravel<Map<String, Object>> {

    @Override
    public Object obterPropriedade(String chave) {
        return this.origem.get(chave);
    }

    @Override
    public String[] obterChaves() {
        return this.origem.keySet().toArray(new String[0]);
    }
}

Gerenciamento de Múltiplas Fontes


public interface FontesConfiguraveis 
    extends Iterable<OrigemConfiguracao<?>> {
    
    boolean contem(String nome);
    OrigemConfiguracao<?> obter(String nome);
}

public class FontesConfigMutaveis implements FontesConfiguraveis {
    private final List<OrigemConfiguracao<?>> fontes = 
        new CopyOnWriteArrayList<>();
    
    @Override
    public boolean contem(String nome) {
        return fontes.stream()
            .anyMatch(fonte -> fonte.identificador.equals(nome));
    }
}

Resolução Dinâmica de Propriedades


public interface ResolvedorPropriedades {
    String resolverMarcadores(String texto);
}

public interface ResolvedorConfiguravel extends ResolvedorPropriedades {
    void definirPrefixoMarcador(String prefixo);
    void definirSufixoMarcador(String sufixo);
}

public class ResolvedorFontesPropriedades implements ResolvedorConfiguravel {
    private final FontesConfiguraveis fontes;
    
    @Override
    public String resolverMarcadores(String texto) {
        // Implementação de resolução de ${...}
    }
}

Gerenciamento de Perfis


public interface PerfilAmbiente {
    boolean corresponde(Predicate<String> perfilAtivo);
}

public class AnalisadorPerfis {
    public static PerfilAmbiente criar(String... expressoes) {
        // Lógica de análise de expressões (AND/OR/NOT)
    }
}

Implementação Central do Ambiente


public interface Ambiente extends ResolvedorPropriedades {
    String[] obterPerfisAtivos();
}

public interface AmbienteConfiguravel extends Ambiente, ResolvedorConfiguravel {
    void definirPerfisAtivos(String... perfis);
    FontesConfigMutaveis obterFontesConfig();
}

public abstract class AmbienteAbstrato implements AmbienteConfiguravel {
    protected final Set<String> perfisAtivos = new LinkedHashSet<>();
    protected final FontesConfigMutaveis fontes;

    protected Set<String> carregarPerfisAtivos() {
        String perfis = obterPropriedade("ambiente.perfis.ativos");
        if (perfis != null) {
            return new HashSet<>(Arrays.asList(perfis.split(",")));
        }
        return Collections.emptySet();
    }
}

public class AmbientePadrao extends AmbienteAbstrato {
    @Override
    protected void inicializarFontes() {
        fontes.adicionar(new OrigemMapaConfig(
            "propriedadesSistema", System.getProperties()));
        fontes.adicionar(new OrigemMapaConfig(
            "variaveisAmbiente", System.getenv()));
    }
}

Testando a Implementação


@Test
public void testarAmbiente() {
    AmbientePadrao ambiente = new AmbientePadrao();
    Map<String, Object> propsTeste = Map.of(
        "app.nome", "TesteAmbiente",
        "app.versao", "1.5"
    );
    ambiente.obterFontesConfig().adicionar(
        new OrigemMapaConfig("configApp", propsTeste));
    
    assertEquals("TesteAmbiente", 
        ambiente.obterPropriedade("app.nome"));
}

Tags: Spring Framework Ambiente de Configuração Injeção de Dependência Gerenciamento de Perfis Desenvolvimento de Frameworks

Publicado em 9-19 17:31