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

AnnotationLayerNotes
@ComponentGenericBase stereotype; scanned by @ComponentScan
@ServiceBusiness logicSemantic alias for @Component
@RepositoryData accessEnables PersistenceExceptionTranslation
@Controller / @RestControllerWeb layerRegisters with DispatcherServlet
@ConfigurationConfig classDeclares @Bean methods; full CGLIB proxy
@BeanMethod in @ConfigurationRegisters 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

ScopeAnnotationLifetime
Singleton(default)One instance per ApplicationContext
Prototype@Scope("prototype")New instance per injection point
Request@RequestScopeOne per HTTP request (web only)
Session@SessionScopeOne per HTTP session (web only)
Application@ApplicationScopeOne 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 @Lookup to 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

AnnotationCondition
@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
@ConditionalOnWebApplicationRunning 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 @Lazy on one constructor parameter.
  • NoUniqueBeanDefinitionException — two beans of the same type; use @Qualifier or @Primary.
  • @Configuration vs @Component for @Bean methods@Configuration uses CGLIB so that @Bean method calls between beans are intercepted and return the same singleton; @Component does not, so inter-@Bean calls create new instances.
  • @Transactional on private methods — Spring's proxy cannot intercept private methods; transaction is silently skipped.
  • @PostConstruct not called — bean must be managed by Spring (annotated and scanned); manually new-ing a class skips all Spring lifecycle.