Core & Setup — Cheat Sheet
Spring Boot · 14 topics. Download the PDF or the Instagram carousel and share it.
Spring Boot Introduction
Spring Boot is an opinionated framework that eliminates boilerplate Spring configuration through auto-configuration, embedded servers, and production-ready defaults.
- ✓@SpringBootApplication = @Configuration + @EnableAutoConfiguration + @ComponentScan.
- ✓Auto-configuration is conditional — it backs off when you define your own beans.
- ✓Embedded Tomcat/Jetty means the app runs as a plain JAR with java -jar.
- ✓application.properties/yml is the central config; profile files override per environment.
- ✓Property override order: command-line args > env vars > profile files > application.properties.
- ✓Use --debug or /actuator/conditions to inspect which auto-configurations are active.
@SpringBootApplication // = @Configuration + @EnableAutoConfiguration + @ComponentScan
public class AiCanCodeApplication {
public static void main(String[] args) {
SpringApplication.run(AiCanCodeApplication.class, args);
}
}
// application.properties — key defaults you often override
server.port=8080
spring.application.name=toolhub
spring.profiles.active=dev
// Fat JAR build + run
// mvn package → target/toolhub-1.0.0.jar
// java -jar target/toolhub-1.0.0.jar --server.port=9090Spring Boot Starters
Starters are curated dependency descriptors (e.g. spring-boot-starter-web) that bundle all required libraries for a feature, removing version-conflict headaches.
- ✓Starters are curated dependency bundles — one starter replaces many individual dependencies.
- ✓spring-boot-dependencies BOM manages all library versions; override via Maven properties.
- ✓Naming convention: spring-boot-starter-{feature} (official) or {name}-spring-boot-starter (third-party).
- ✓Exclude an auto-included transitive dep (e.g. Tomcat) by adding <exclusions> on the starter.
- ✓spring-boot-starter-web = Tomcat; swap to spring-boot-starter-undertow or spring-boot-starter-webflux.
- ✓spring-boot-starter-test brings JUnit 5, Mockito, AssertJ, MockMvc, and Testcontainers.
<!-- pom.xml — common starters -->
<dependencies>
<!-- Web: Spring MVC, Tomcat, Jackson, Hibernate Validator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- JPA: Hibernate, Spring Data JPA, HikariCP -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- Security: Spring Security core + web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Actuator: health, metrics, info endpoints -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Test: JUnit 5, Mockito, AssertJ, MockMvc, Testcontainers -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>Auto-Configuration
Spring Boot scans the classpath and conditionally creates beans on your behalf; understanding @EnableAutoConfiguration and spring.factories unlocks deep customisation.
- ✓@SpringBootApplication includes @EnableAutoConfiguration which triggers the entire auto-configuration loading mechanism.
- ✓Auto-configuration classes are loaded from META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports (Boot 3) or META-INF/spring.factories (Boot 2).
- ✓@ConditionalOnMissingBean is the primary escape hatch — declare your own @Bean of the same type to override any auto-configured bean.
- ✓User-defined beans are always registered before auto-configured ones, so explicit definitions always win.
- ✓Use the --debug flag to print the ConditionEvaluationReport and understand exactly why each auto-config fired or was skipped.
- ✓Write your own Spring Boot starter by creating a @Configuration class with @Conditional annotations and registering it in the AutoConfiguration.imports file.
// Spring Boot 3 imports file:
// META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
// Simplified example of DataSourceAutoConfiguration
@AutoConfiguration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@Conditional(PooledDataSourceCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@Import({ DataSourceConfiguration.Hikari.class })
protected static class PooledDataSourceConfiguration { }
}@SpringBootApplication
A convenience annotation combining @Configuration, @EnableAutoConfiguration, and @ComponentScan that bootstraps the entire application context.
- ✓@SpringBootApplication = @Configuration + @EnableAutoConfiguration + @ComponentScan
- ✓Auto-configuration is conditional — @ConditionalOnMissingBean means your own beans take priority
- ✓Use exclude or spring.autoconfigure.exclude to disable unwanted auto-configs
- ✓scanBasePackages restricts component scanning to a specific root (improves startup)
- ✓Only one @SpringBootApplication is needed per application; typically on the main class
- ✓spring.main.lazy-initialization=true delays bean creation to first use, cutting startup time
@SpringBootApplication(
scanBasePackages = "com.example",
exclude = { DataSourceAutoConfiguration.class }
)
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
// Equivalent explicit form:
// @Configuration
// @EnableAutoConfiguration(exclude = DataSourceAutoConfiguration.class)
// @ComponentScan("com.example")application.properties / YAML
Externalise configuration via key-value properties or structured YAML; Spring Boot resolves values from multiple sources following a strict precedence order.
- ✓Property source precedence (highest first): CLI args → env vars → profile properties → base properties.
- ✓@ConfigurationProperties binds a prefix to a typed bean — prefer over scattered @Value for grouped config.
- ✓@Validated on @ConfigurationProperties enables JSR-303 constraint checking at startup.
- ✓Never commit secrets to application.properties — inject via environment variables or a secrets manager.
- ✓spring.profiles.active selects environment-specific files; profile groups (Spring Boot 2.4+) activate multiple at once.
- ✓Environment variables override properties: SPRING_DATASOURCE_URL overrides spring.datasource.url.
# application.properties (lowest priority in the set)
server.port=8080
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=app
# Overridden by application-prod.properties (active profile)
# application-prod.properties
server.port=80
spring.datasource.url=jdbc:mysql://prod-db:3306/mydb
# Overridden by environment variable (higher priority)
# SPRING_DATASOURCE_URL=jdbc:mysql://rds-host:3306/mydb
# Overridden by command-line arg (highest priority)
# java -jar app.jar --spring.datasource.url=jdbc:mysql://override:3306/mydb
# Activate profile
spring.profiles.active=prod
# Or: SPRING_PROFILES_ACTIVE=prod (env var)
# Or: java -jar app.jar --spring.profiles.active=prod (CLI)
# YAML equivalent (application.yml)
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: app
profiles:
active: dev@ConfigurationProperties
Binds a whole hierarchy of properties to a strongly-typed POJO, supporting relaxed binding, JSR-303 validation, and IDE auto-completion.
- ✓@ConfigurationProperties binds a whole group of related properties to one POJO — much cleaner than multiple @Value annotations.
- ✓Add @Validated to the properties class and JSR-303 annotations on fields — Spring Boot fails fast on startup if config is invalid.
- ✓Relaxed binding maps kebab-case YAML, camelCase Java, and UPPERCASE_ENV_VARS to the same field automatically.
- ✓Use a Java record (Boot 3+) for immutable, concise configuration properties without boilerplate getters/setters.
- ✓Add spring-boot-configuration-processor (optional, annotationProcessor scope) to unlock IDE auto-completion for your custom properties.
- ✓Nested objects are supported — group sub-settings in inner classes or nested records for clean hierarchies.
// application.yml
payment:
gateway-url: https://api.stripe.com/v1
api-key: sk_live_xxxx
timeout-seconds: 30
retry:
max-attempts: 3
back-off-ms: 500
// 1. Define the POJO
@ConfigurationProperties(prefix = "payment")
@Validated // enables JSR-303 validation on fields
public class PaymentProperties {
@NotBlank
private String gatewayUrl;
@NotBlank
private String apiKey;
@Min(1) @Max(120)
private int timeoutSeconds;
private Retry retry = new Retry(); // nested object
// getters + setters (or use a record in Boot 3.x)
public static class Retry {
private int maxAttempts = 3;
private long backOffMs = 500;
// getters + setters
}
}
// 2. Register (choose one approach)
@SpringBootApplication
@ConfigurationPropertiesScan // scans for all @ConfigurationProperties in package
public class App { }
// 3. Inject anywhere
@Service
@RequiredArgsConstructor
public class PaymentService {
private final PaymentProperties props;
// props.getGatewayUrl(), props.getRetry().getMaxAttempts(), etc.
}Spring Profiles
Profiles separate configuration per environment (dev, test, prod); beans annotated with @Profile or properties suffixed -dev.yml are activated selectively.
- ✓Profile-specific files follow the naming convention `application-{profile}.yml`; they override keys in the base `application.yml`.
- ✓Set `SPRING_PROFILES_ACTIVE` env var for production/K8s deployments — never hardcode the active profile in application.yml for prod.
- ✓Multiple profiles can be active simultaneously: `SPRING_PROFILES_ACTIVE=prod,datadog`.
- ✓@Profile on a @Bean or @Component registers it only when the profile matches; use `!prod` for "active in all non-prod environments".
- ✓@ActiveProfiles("test") in test classes activates a profile just for that test class without changing JVM arguments.
- ✓Profiles do not need to match environment names — you can create profiles for features: `spring.profiles.active=billing-v2`.
# application.yml — shared defaults
spring:
application:
name: order-service
jpa:
show-sql: false
logging:
level:
root: INFO
---
# application-dev.yml — local dev overrides
spring:
datasource:
url: jdbc:h2:mem:devdb
driver-class-name: org.h2.Driver
jpa:
show-sql: true # override: show SQL locally
logging:
level:
com.example: DEBUG # verbose logging in dev
---
# application-prod.yml — production
spring:
datasource:
url: ${DB_URL} # injected from env var / Kubernetes Secret
username: ${DB_USER}
password: ${DB_PASS}Spring Boot DevTools
Provides automatic application restart on classpath changes, live reload for static resources, and relaxed property defaults during development.
- ✓DevTools uses two classloaders: base (libs) + restart (app code) — only restart classloader reloads on change
- ✓Declare as optional/developmentOnly so it never ends up in the production fat-jar
- ✓LiveReload server starts on port 35729 and integrates with the LiveReload browser extension
- ✓Template caches (Thymeleaf, FreeMarker) are disabled automatically — no need to configure
- ✓Trigger-file mode prevents spurious restarts in IDEs that save files aggressively
- ✓Remote DevTools allows restarting a remote app via a secure tunnel — use only in controlled staging environments
<!-- Maven -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
// Gradle
dependencies {
developmentOnly 'org.springframework.boot:spring-boot-devtools'
}Embedded Servers
Spring Boot embeds Tomcat, Jetty, or Undertow directly in the fat JAR, removing the need to deploy WARs to external containers.
- ✓Embedded servers (Tomcat/Jetty/Undertow/Netty) are packaged in the JAR — no WAR deployment needed.
- ✓java -jar app.jar is all that's required to start — simplifies cloud/container deployment.
- ✓server.port, server.ssl.*, and server.tomcat.* cover most tuning needs via properties.
- ✓WebServerFactoryCustomizer<T> allows programmatic server configuration beyond properties.
- ✓server.shutdown=graceful lets in-flight requests complete before shutdown.
- ✓HTTP/2 is supported but requires SSL — enable with server.http2.enabled=true.
# application.properties — embedded server config
# Port and context path
server.port=8080
server.servlet.context-path=/api
# SSL (enable HTTPS)
server.ssl.enabled=true
server.ssl.key-store=classpath:keystore.p12
server.ssl.key-store-type=PKCS12
server.ssl.key-store-password=${SSL_KEYSTORE_PASSWORD}
# Tomcat-specific tuning
server.tomcat.max-threads=200 # max worker threads
server.tomcat.min-spare-threads=20 # min idle threads
server.tomcat.accept-count=100 # queue length when all threads busy
server.tomcat.connection-timeout=20000 # ms before idle connection dropped
# Undertow-specific
server.undertow.threads.io=4 # IO threads (= CPU cores typically)
server.undertow.threads.worker=32 # worker threads
# HTTP/2 support (requires SSL)
server.http2.enabled=true
# Compression
server.compression.enabled=true
server.compression.mime-types=text/html,text/plain,application/json
server.compression.min-response-size=1024Conditional Beans (@ConditionalOn*)
@ConditionalOnClass, @ConditionalOnProperty, and friends let you register beans only when specific conditions on the classpath or environment hold true.
- ✓@ConditionalOnClass guards auto-configuration beans on classpath presence
- ✓@ConditionalOnMissingBean is the override hook — user-defined beans take precedence
- ✓@ConditionalOnProperty enables/disables features via application.properties flags
- ✓@ConditionalOnExpression evaluates SpEL for multi-property conditions
- ✓All @ConditionalOn* annotations are built on @Conditional(SomeCondition.class)
- ✓matchIfMissing=true on @ConditionalOnProperty enables the bean when the property is absent
@Configuration
@ConditionalOnClass(DataSource.class) // only if JDBC is on classpath
public class DataSourceAutoConfig {
@Bean
@ConditionalOnMissingBean(DataSource.class) // default — user can override
public DataSource defaultDataSource(DataSourceProperties props) {
return DataSourceBuilder.create()
.url(props.getUrl())
.username(props.getUsername())
.password(props.getPassword())
.build();
}
}
@Bean
@ConditionalOnProperty(
name = "feature.cache.enabled",
havingValue = "true",
matchIfMissing = false // don't create bean if property absent
)
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("users");
}Spring Framework & IoC
The Spring Framework is built on Inversion of Control (IoC) — you declare what you need, the container creates and wires it. Understanding the IoC container is the foundation of everything else in Spring.
- ✓IoC container creates and wires objects — you declare dependencies, Spring fulfills them.
- ✓ApplicationContext is the full-featured IoC container used in Spring Boot apps.
- ✓Constructor injection is preferred: makes dependencies explicit, enables final fields, fails fast.
- ✓Field injection (@Autowired on fields) hides dependencies and breaks unit testing without Spring.
- ✓Beans are singletons by default — one instance shared across the entire application.
- ✓@PostConstruct and @PreDestroy handle bean lifecycle hooks.
// The container reads @Component, @Service, @Repository, @Controller, @Bean
// and builds a map of name → bean instance
// Conceptually what the container does:
// 1. Scan classpath for annotated classes (@ComponentScan)
// 2. Instantiate each bean (constructor or no-arg + setters)
// 3. Inject dependencies (constructor injection preferred)
// 4. Call @PostConstruct lifecycle methods
// 5. Register the bean, ready to serve
// Accessing the container directly (rarely needed):
@SpringBootApplication
public class App {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(App.class, args);
// Look up a bean by type
UserService userService = ctx.getBean(UserService.class);
// Inspect registered bean names
String[] names = ctx.getBeanDefinitionNames();
}
}Spring Boot Setup
Spring Boot eliminates boilerplate setup through auto-configuration and starter dependencies. A production-ready app can be running in under 5 minutes with the right project structure.
- ✓spring-boot-starter-parent manages all dependency versions — never specify library versions manually.
- ✓Starters bundle related libraries with compatible versions; you pick the feature, Spring picks the versions.
- ✓@SpringBootApplication must be in the root package for @ComponentScan to find all sub-packages.
- ✓Auto-configuration is conditional — it backs off when you declare your own beans.
- ✓spring-boot-maven-plugin produces a fat executable JAR runnable with java -jar.
- ✓Use application-{profile}.yml for environment-specific overrides (dev, prod, test).
<!-- pom.xml — minimal Spring Boot web app -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.0</version>
</parent>
<dependencies>
<!-- Web: Tomcat + Spring MVC + Jackson -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- JPA: Hibernate + Spring Data -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- PostgreSQL driver -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Test: JUnit 5, Mockito, MockMvc -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Builds executable fat JAR with java -jar -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>Dependency Injection
Spring's DI container wires your application together. Understanding @Component stereotypes, @Bean factory methods, @Qualifier disambiguation, and scope is essential for every Spring developer.
- ✓@Service, @Repository, @Controller are specializations of @Component — use them to communicate layer intent.
- ✓@Repository adds persistence exception translation: vendor SQLExceptions → Spring DataAccessException.
- ✓Use @Bean methods in @Configuration classes for third-party objects or beans needing complex setup.
- ✓@Primary sets a default when multiple beans satisfy a type; @Qualifier picks a specific named bean.
- ✓Singleton scope (default) means one shared instance — never store mutable request state in a singleton.
- ✓Prototype scope creates a new instance per injection; request scope creates one per HTTP request.
// @Service — business logic, no extra framework magic
@Service
public class UserService {
private final UserRepository userRepository;
private final EmailService emailService;
public UserService(UserRepository userRepository, EmailService emailService) {
this.userRepository = userRepository;
this.emailService = emailService;
}
public User register(RegisterRequest req) {
if (userRepository.existsByEmail(req.email())) {
throw new EmailAlreadyExistsException(req.email());
}
User user = new User(req.email(), passwordEncoder.encode(req.password()));
User saved = userRepository.save(user);
emailService.sendWelcome(saved); // fire and forget
return saved;
}
}
// @Repository — data access layer
// Also translates SQLExceptions to DataAccessException
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
boolean existsByEmail(String email);
}
// Plain @Component — doesn't fit other stereotypes
@Component
public class SlugGenerator {
public String generate(String title) {
return title.toLowerCase().replaceAll("\s+", "-");
}
}Application Configuration
application.yml is the central config file. @ConfigurationProperties binds whole config sections to typed classes — the recommended approach for anything beyond a single value. @Value injects individual properties.
- ✓@ConfigurationProperties is preferred for multi-property sections — type-safe, validatable, IDE-autocompletable.
- ✓@Value("${prop:default}") injects a single property with an optional fallback default.
- ✓Command-line args > env vars > application-{profile}.yml > application.yml — env vars always win.
- ✓Never hardcode secrets in YAML — use ${ENV_VAR} references and set them in the runtime environment.
- ✓@ConfigurationPropertiesScan on @SpringBootApplication auto-registers all @ConfigurationProperties.
- ✓Use profile-specific files (application-prod.yml) for environment differences, not if/else in code.
// application.yml
app:
jwt:
secret: ${JWT_SECRET} # from env var
expiry-minutes: 60
email:
provider: resend
api-key: ${EMAIL_API_KEY}
from: noreply@aicancode.org
rate-limit:
requests-per-minute: 100
burst-capacity: 200
// --- Java config class (Java record — Spring Boot 3+) ---
@ConfigurationProperties(prefix = "app.jwt")
public record JwtProperties(
@NotBlank String secret,
@Min(5) @Max(1440) int expiryMinutes
) {}
@ConfigurationProperties(prefix = "app.email")
public record EmailProperties(
@NotBlank String provider,
@NotBlank String apiKey,
@Email String from
) {}
// Register in main class
@SpringBootApplication
@ConfigurationPropertiesScan // picks up all @ConfigurationProperties in package
public class App { ... }
// Inject and use
@Service
public class JwtService {
private final JwtProperties jwt;
public JwtService(JwtProperties jwt) { this.jwt = jwt; }
public String generateToken(String userId) {
return Jwts.builder()
.subject(userId)
.expiration(Date.from(Instant.now()
.plusSeconds(jwt.expiryMinutes() * 60L)))
.signWith(Keys.hmacShaKeyFor(jwt.secret().getBytes()))
.compact();
}
}