Spring Boot Cheatsheet
Dependency Injection
Use this Spring Boot reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Core Stereotypes
| Annotation | Layer | Notes |
|---|---|---|
@Component | Generic | Base stereotype; scanned by @ComponentScan |
@Service | Business logic | Semantic alias for @Component |
@Repository | Data access | Enables PersistenceExceptionTranslation |
@Controller / @RestController | Web layer | Registers with DispatcherServlet |
@Configuration | Config class | Declares @Bean methods; full CGLIB proxy |
@Bean | Method in @Configuration | Registers return value as a bean |
Injection Styles
Constructor Injection (preferred)
@Service public class OrderService { private final OrderRepository orderRepo; private final PaymentService paymentService; // Spring calls this automatically when there is exactly one constructor public OrderService(OrderRepository orderRepo, PaymentService paymentService) { this.orderRepo = orderRepo; this.paymentService = paymentService; } }
With Lombok (eliminates boilerplate):
@Service @RequiredArgsConstructor // generates constructor for all final fields public class OrderService { private final OrderRepository orderRepo; private final PaymentService paymentService; }
Setter Injection (optional dependencies)
@Service public class ReportService { private NotificationService notificationService; @Autowired(required = false) public void setNotificationService(NotificationService svc) { this.notificationService = svc; } }
Field Injection (avoid in production code)
@Service public class LegacyService { @Autowired // hard to test; hides dependencies private UserRepository userRepository; }
Constructor injection is the only style that guarantees the object is fully initialised and makes dependencies explicit for unit tests.
@Bean Declaration
@Configuration public class AppConfig { @Bean // name defaults to method name public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(12); } @Bean("customRestTemplate") // explicit name public RestTemplate restTemplate(RestTemplateBuilder builder) { return builder .setConnectTimeout(Duration.ofSeconds(5)) .setReadTimeout(Duration.ofSeconds(10)) .build(); } @Bean @ConditionalOnMissingBean // only if no other bean of this type exists public Clock systemClock() { return Clock.systemUTC(); } }
Resolving Multiple Implementations
public interface NotificationSender { void send(String message); } @Service("emailSender") public class EmailSender implements NotificationSender { ... } @Service("smsSender") public class SmsSender implements NotificationSender { ... }
// Inject by qualifier @Service @RequiredArgsConstructor public class AlertService { @Qualifier("emailSender") private final NotificationSender sender; } // Or inject all @Service public class BroadcastService { private final List<NotificationSender> senders; // all implementations public BroadcastService(List<NotificationSender> senders) { this.senders = senders; } } // Or inject as Map<name, bean> public BroadcastService(Map<String, NotificationSender> senders) { ... }
@Primary
@Service @Primary // used when no @Qualifier is specified public class EmailSender implements NotificationSender { ... }
Bean Scopes
| Scope | Annotation | Lifetime |
|---|---|---|
| Singleton | (default) | One instance per ApplicationContext |
| Prototype | @Scope("prototype") | New instance per injection point |
| Request | @RequestScope | One per HTTP request (web only) |
| Session | @SessionScope | One per HTTP session (web only) |
| Application | @ApplicationScope | One per ServletContext |
@Component @RequestScope public class RequestContext { private String traceId = UUID.randomUUID().toString(); }
Injecting a prototype-scoped bean into a singleton: use
ObjectProvider<T>or@Lookupto get a fresh instance each time.
@Service public class TaskService { private final ObjectProvider<WorkerTask> taskProvider; public TaskService(ObjectProvider<WorkerTask> taskProvider) { this.taskProvider = taskProvider; } public void run() { WorkerTask task = taskProvider.getObject(); // new prototype each call task.execute(); } }
Conditional Beans
| Annotation | Condition |
|---|---|
@ConditionalOnProperty("feature.x.enabled") | Property is true |
@ConditionalOnMissingBean(DataSource.class) | No bean of that type |
@ConditionalOnBean(RedisConnectionFactory.class) | Bean exists |
@ConditionalOnClass(name="com.example.Foo") | Class on classpath |
@ConditionalOnWebApplication | Running as a web app |
@Profile("dev") | Active Spring profile matches |
@Bean @ConditionalOnProperty(name = "storage.type", havingValue = "s3") public StorageService s3StorageService(S3Client s3) { return new S3StorageService(s3); } @Bean @ConditionalOnProperty(name = "storage.type", havingValue = "local", matchIfMissing = true) public StorageService localStorageService() { return new LocalStorageService(); }
Lifecycle Callbacks
@Component public class CacheWarmer { @PostConstruct // runs after injection, before the bean is used public void warmUp() { cacheService.loadAll(); } @PreDestroy // runs before the context closes public void flush() { cacheService.flush(); } }
Or via @Bean:
@Bean(initMethod = "init", destroyMethod = "cleanup") public DataSource dataSource() { ... }
ApplicationContext Access (avoid when possible)
@Component public class SpringContextUtil implements ApplicationContextAware { private static ApplicationContext context; @Override public void setApplicationContext(ApplicationContext ctx) { context = ctx; } public static <T> T getBean(Class<T> type) { return context.getBean(type); } }
Lazy Initialization
@Bean @Lazy // instantiated on first use, not at startup public ExpensiveService expensiveService() { ... }
# Make all beans lazy globally (useful for faster startup in dev) spring.main.lazy-initialization=true
Common Pitfalls
BeanCurrentlyInCreationException— circular dependency. Fix with constructor injection refactoring; last resort@Lazyon one constructor parameter.NoUniqueBeanDefinitionException— two beans of the same type; use@Qualifieror@Primary.@Configurationvs@Componentfor@Beanmethods —@Configurationuses CGLIB so that@Beanmethod calls between beans are intercepted and return the same singleton;@Componentdoes not, so inter-@Beancalls create new instances.@Transactionalon private methods — Spring's proxy cannot intercept private methods; transaction is silently skipped.@PostConstructnot called — bean must be managed by Spring (annotated and scanned); manuallynew-ing a class skips all Spring lifecycle.