Skip to main content

Spring Boot Common Tasks

Source: Spring Boot in Practice — Chapter 2 by Somnath Musib (Manning). Concepts rewritten for this course; companion samples: spring-boot-in-practice/repo.

Spring Boot hides a lot of wiring so you can ship features faster. This lesson covers the day-to-day tasks you will keep repeating: externalize config, bind typed properties, run startup hooks, tune logging, and validate input with Bean Validation.

Pattern-check challenges

Spring Boot needs a full app context, so these editors do not compile or run Java. On Check, the site only verifies patterns in your code (sourceChecks). Annotated snippets above each exercise show the usual shape — inline comments explain what each part does; you still type it into the editor yourself. Paste is disabled (keyboard, right-click, drag-drop) so you practice writing the code.

What this chapter covers​

  • Managing configuration across environments
  • Typed custom config with @ConfigurationProperties
  • Startup work via CommandLineRunner
  • Default Logback logging and switching to Log4j2
  • Bean Validation (built-in and custom constraints)

1. Managing configurations​

Real apps usually have several environments (dev, test, staging, prod). The code stays mostly the same; config changes: datasources, security, feature flags, ports.

Spring Boot lets you externalize config without editing source for every environment:

ApproachTypical use
SpringApplication.setDefaultPropertiesOne-shot defaults before bootstrap
@PropertySourceExtra .properties files on a @Configuration class
application.properties / .ymlMain config data files
OS environment variablesSecrets and host-specific values
Command-line argumentsHighest-precedence overrides at launch

1.1 SpringApplication defaults​

SpringApplication.setDefaultProperties(...) accepts a Properties or Map. Useful for early bootstrap knobs that must exist before normal config loading.

Example: optional config imports. With spring.config.import=classpath:extra.properties, a missing file normally fails startup. Setting spring.config.on-not-found=ignore lets bootstrap continue.

Properties properties = new Properties();
properties.setProperty("spring.config.on-not-found", "ignore");
SpringApplication application = new SpringApplication(DemoApplication.class);
application.setDefaultProperties(properties);
application.run(args);
properties.setProperty("spring.config.on-not-found", "ignore"); // continue if an imported config file is missing
application.setDefaultProperties(properties); // apply defaults before normal config loading
application.run(args);

1.2 @PropertySource​

On a @Configuration class, @PropertySource("classpath:dbConfig.properties") loads a classic properties file into the Environment. Inject Environment (or use @Value) to read keys.

@Configuration
@PropertySource("classpath:dbConfig.properties")
public class DbConfiguration {
@Autowired
private Environment env;

@Override
public String toString() {
return "Username: " + env.getProperty("user")
+ ", Password: " + env.getProperty("password");
}
}

Notes:

  • @PropertySource does not load YAML out of the box; stick to .properties or add extra support.
  • On Java 8+, repeat @PropertySource for multiple files (or use @PropertySources).
import org.springframework.context.annotation.PropertySource;

@PropertySource("classpath:dbConfig.properties") // load extra keys into Environment
@Configuration
public class DbConfiguration { ... }

1.3 Config data files​

Most teams use application.properties or application.yml under src/main/resources. Values land in Environment and work with @Value.

Properties style:

server.port=8081
spring.datasource.username=sa
spring.datasource.password=password

YAML style (mind indentation):

server:
port: 8081
spring:
datasource:
username: sa
password: password

Rename the file with spring.config.name (for example sbip loads sbip.yml). Point at a path with spring.config.location. Both are early-binding: set them via CLI, env, or setDefaultProperties — not inside application.properties itself.

Default search order includes classpath root, classpath /config, the process working directory, and /config under that directory. From Boot 2.4+, a missing location fails unless you mark it optional:....

Profiles: application-dev.properties / application-test.yml activate with spring.profiles.active=dev (etc.). Later sources override earlier ones; outside-the-jar profile files beat packaged ones.

1.4 Environment variables and CLI args​

Placeholders pull OS env into config files:

spring.datasource.username=${DB_USERNAME}
spring.datasource.password=${DB_PASSWORD}

Command-line flags win when you need a one-off override:

java -jar app.jar --server.port=9090 --spring.config.name=sbip

1.5 Precedence (low → high)​

If the same key appears in several places, higher wins:

  1. SpringApplication defaults
  2. @PropertySource
  3. Config data files
  4. OS environment variables
  5. Command-line arguments

So --server.port=9090 beats server.port in application.properties.

Quiz

Configuration quiz

1 / 3

Which source wins if server.port is set in application.properties and also passed as --server.port=9090?

Pick an answer to see if you are right.


2. Custom properties with @ConfigurationProperties​

Built-in keys like server.port are documented by Spring Boot. App-specific keys (app.sbip.ct.name, nested security flags, role lists) are better as a typed bean than a pile of @Value strings.

Problems with raw Environment / @Value:

  • Weak typing (URLs, ports, lists) and late failures
  • Scattered individual lookups instead of one cohesive object

Technique: bind a type-safe properties class​

  1. Add optional spring-boot-configuration-processor for IDE metadata / autocomplete.
  2. Declare keys in application.properties (or YAML).
  3. Map them with @ConfigurationProperties("app.sbip.ct") on a POJO (nested static class for security.*).
  4. Prefer constructor binding for immutability (Boot 3 binds a single constructor automatically; older samples used @ConstructorBinding).
  5. Register with @EnableConfigurationProperties(AppProperties.class) or @ConfigurationPropertiesScan.
  6. Inject AppProperties into services.
app.sbip.ct.name=CourseTracker
app.sbip.ct.ip=127.0.0.1
app.sbip.ct.port=9090
app.sbip.ct.security.enabled=true
app.sbip.ct.security.token=...
app.sbip.ct.security.roles=USER,ADMIN
@ConfigurationProperties("app.sbip.ct")
public class AppProperties {
private final String name;
private final String ip;
private final int port;
private final Security security;
// constructor + getters ...
}

Optional @DefaultValue on constructor parameters supplies fallbacks when a key is missing.

@ConfigurationProperties("app.sbip.ct") // maps app.sbip.ct.* properties onto this type
public class AppProperties { ... }

@EnableConfigurationProperties(AppProperties.class) // register the properties bean in the context
@SpringBootApplication
public class DemoApplication { ... }
Quiz

@ConfigurationProperties quiz

1 / 2

Main benefit of @ConfigurationProperties over many @Value fields?

Pick an answer to see if you are right.


3. Startup code with CommandLineRunner​

Need to seed data, warm a cache, or call a remote API once the context is almost ready? Implement CommandLineRunner (or ApplicationRunner). Spring invokes run(...) once, just before SpringApplication.run returns.

Common shapes:

  1. Main class implements CommandLineRunner
  2. @Bean returning a CommandLineRunner lambda
  3. Separate @Component implementing the interface

@Bean / @Component scale better: multiple runners, @Order for sequence, and less clutter in main. Inside run, you can inject any ready bean (for example a Spring Data repository).

@Bean
public CommandLineRunner commandLineRunner() {
return args -> {
logger.info("started with " + args.length + " args");
};
}
@Order(1)
@Component
public class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) {
// init work
}
}
@Bean
public CommandLineRunner commandLineRunner() { // invoked once near end of startup
return args -> logger.info("ready"); // lambda implements CommandLineRunner.run
}
Quiz

CommandLineRunner quiz

1 / 2

When does CommandLineRunner.run execute?

Pick an answer to see if you are right.


4. Logging​

4.1 Default Logback console logging​

Any spring-boot-starter-* pulls in spring-boot-starter-logging → Logback. Console lines typically include timestamp, level, PID, thread, logger, and message.

Useful properties:

PropertyRole
logging.pattern.consoleCustom console pattern
debug=true / trace=trueRaise verbosity
logging.file.name or logging.file.pathFile logging (pick one style)
logging.logback.rollingpolicy.max-file-sizeSize-based rollover (default ~10MB)
logging.logback.rollingpolicy.max-historyHow many archived files to keep

Default levels include INFO / WARN / ERROR; enable DEBUG/TRACE when diagnosing.

4.2 Switch to Log4j2​

  1. Clear logging.* experiments from application.properties if you are moving config into Log4j2 XML.
  2. Exclude spring-boot-starter-logging from spring-boot-starter-web (and other starters that bring it in).
  3. Add spring-boot-starter-log4j2.
  4. Add src/main/resources/log4j2-spring.xml (preferred over log4j2.xml so Boot can control initialization).
  5. Log via SLF4J (LoggerFactory.getLogger) so the facade still works.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

A typical XML file defines a LOG_PATTERN, Console + RollingFile appenders (size/time policies), package loggers, and a Root logger.

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId> <!-- remove default Logback -->
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId> <!-- use Log4j2 on the classpath -->
</dependency>
Quiz

Logging quiz

1 / 2

Why does a typical Boot web app use Logback by default?

Pick an answer to see if you are right.


5. Bean Validation​

Bean Validation (Jakarta Validation; Hibernate Validator is the usual implementation) expresses constraints as annotations. Add spring-boot-starter-validation.

5.1 Built-in annotations​

Common field constraints:

AnnotationChecks
@NotNull / @NotEmpty / @NotBlankNull / empty rules
@Min / @MaxNumeric bounds
@Size(min, max)Length / collection size
@PatternRegex
@EmailEmail shape

Example: course rating must be 1–5.

public class Course {
private long id;
private String name;

@Min(value = 1, message = "A course should have a minimum of 1 rating")
@Max(value = 5, message = "A course should have a maximum of 5 rating")
private int rating;
}

You can validate programmatically:

Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<Course>> violations = validator.validate(course);

Later chapters wire the same annotations into MVC / REST with @Valid.

@Min(value = 1, message = "…") // rating must be at least 1
@Max(value = 5, message = "…") // rating must be at most 5
private int rating;

5.2 Custom constraints​

For domain rules (password policy, IP format), implement:

  1. ConstraintValidator<YourAnnotation, ValueType> with isValid(...)
  2. An annotation marked @Constraint(validatedBy = ...) with message, groups, payload
  3. Apply the annotation on fields

Libraries like Passay help encode password rules (length, character classes, max repeats) inside isValid.

@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PasswordRuleValidator.class)
public @interface Password {
String message() default "Password do not adhere to the specified rule";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class User {
private String userName;
@Password
private String password;
}
@Target({ElementType.METHOD, ElementType.FIELD}) // may annotate fields or methods
@Retention(RetentionPolicy.RUNTIME) // visible to the validator at runtime
@Constraint(validatedBy = PasswordRuleValidator.class) // logic lives in the validator class
public @interface Password {
String message() default "...";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Quiz

Bean Validation quiz

1 / 2

Which starter pulls in Hibernate Validator for Boot?

Pick an answer to see if you are right.


Chapter checklist​

TopicTakeaway
External configProperties/YAML, env, CLI, @PropertySource, early defaults
PrecedenceCLI generally beats env, which beats files, which beat @PropertySource / app defaults
@ConfigurationPropertiesTyped, nested, documentable app settings
CommandLineRunnerOne-shot startup hooks; prefer beans + @Order
LoggingLogback by default; exclude logging starter to use Log4j2
Bean ValidationBuilt-in annotations + custom @Constraint validators

Next stop in the book journey: accessing databases from Spring Boot (repositories, schema init, and related patterns).