Careeroza
Config Clients and Profiles
Connect a Service
Add spring-cloud-starter-config to order-service and import the server in its own small application.yml:
spring:
application:
name: order-service # decides which files are fetched
profiles:
active: ${SPRING_PROFILES_ACTIVE:dev}
config:
import: "optional:configserver:${CONFIG_SERVER_URL:http://localhost:8888}"
cloud:
config:
fail-fast: false # true in prod: refuse to start without configExample
SPRING_PROFILES_ACTIVE=prod java -jar order-service.jarExample output
INFO o.s.c.c.c.ConfigServerConfigDataLoader : Fetching config from server at : http://localhost:8888 INFO o.s.c.c.c.ConfigServerConfigDataLoader : Located environment: name=order-service, profiles=[prod], label=null, version=4c1e9a7d, state=null INFO c.s.order.OrderServiceApplication : The following 1 profile is active: "prod"
Note: optional: means "start anyway if the server is down" (using local values). Without it the app fails to start. The old bootstrap.yml way is no longer needed since Spring Cloud 2020.
Which Value Wins?
More specific sources override less specific ones. For order-service with profile prod, from weakest to strongest: local application.yml → repo application.yml → order-service.yml → order-service-prod.yml. This plain-Java program merges layers the same way and shows where each final value came from:
Example
import java.util.*;
public class Main {
public static void main(String[] args) {
// weakest first
List<Layer> layers = List.of(
new Layer("local application.yml", Map.of(
"server.port", "8082", "shopease.orders.max-items-per-order", "20")),
new Layer("repo application.yml", Map.of(
"management.endpoints.web.exposure.include", "health,info,refresh")),
new Layer("repo order-service.yml", Map.of(
"shopease.orders.max-items-per-order", "10", "shopease.orders.cod-enabled", "true")),
new Layer("repo order-service-prod.yml", Map.of(
"shopease.orders.max-items-per-order", "5"))
);
Map<String, String> value = new TreeMap<>();
Map<String, String> source = new TreeMap<>();
for (Layer layer : layers) {
layer.props().forEach((k, v) -> {
value.put(k, v); // later layer overrides
source.put(k, layer.name());
});
}
value.forEach((k, v) ->
System.out.printf("%-42s = %-20s <- %s%n", k, v, source.get(k)));
}
}
record Layer(String name, Map<String, String> props) {}Output
management.endpoints.web.exposure.include = health,info,refresh <- repo application.yml server.port = 8082 <- local application.yml shopease.orders.cod-enabled = true <- repo order-service.yml shopease.orders.max-items-per-order = 5 <- repo order-service-prod.yml
Use the Values in Code
Prefer a typed @ConfigurationProperties record over many @Value fields (the Spring Boot material covers this in depth):
@ConfigurationProperties(prefix = "shopease.orders")
public record OrderProperties(int maxItemsPerOrder, boolean codEnabled) {}
@SpringBootApplication
@ConfigurationPropertiesScan
public class OrderServiceApplication { /* main */ }
@Service
@RequiredArgsConstructor
class OrderRules {
private final OrderProperties props;
void check(PlaceOrderRequest req) {
if (req.items().size() > props.maxItemsPerOrder()) {
throw new TooManyItemsException(props.maxItemsPerOrder());
}
if (req.paymentMode() == PaymentMode.COD && !props.codEnabled()) {
throw new CodNotAvailableException();
}
}
}