Question 9 · Section 5

What do @PostConstruct and @PreDestroy Methods Do?

4. Prototype beans - @PreDestroy is not called -> manually or DestructionCallback

Language versions: English Russian Ukrainian

Junior Level

@PostConstruct - executed once after bean creation. @PreDestroy - executed once before bean removal.

Spring 6 / Boot 3: package changed from javax.annotation to jakarta.annotation.

@Service
public class MyService {

    @PostConstruct
    public void init() {
        // Called after creation and dependency injection
        System.out.println("Bean created!");
    }

    @PreDestroy
    public void cleanup() {
        // Called before bean removal
        System.out.println("Bean is being removed!");
    }
}

When to use:

  • @PostConstruct -> initialization, validation
  • @PreDestroy -> closing connections, cleanup

Middle Level

Execution Order

1. Constructor
2. @Autowired (injection)
3. @PostConstruct  <- Dependencies already exist!
4. Bean is ready

On removal:
1. @PreDestroy
2. Bean removed

Why not Constructor?

// In constructor, Field/Setter dependencies are still NULL!
public MyService() {
    repo.loadData();  // NPE! repo not yet injected
}

// @PostConstruct - dependencies already exist
@PostConstruct
public void init() {
    repo.loadData();  // OK!
}

Singleton vs Prototype

// Singleton:
// -> @PostConstruct called once
// -> @PreDestroy called on shutdown

// Prototype:
// -> @PostConstruct called each time on creation
// -> @PreDestroy is NOT called automatically for Prototype beans. Spring does not track their destruction.

Initialization Order

1. @PostConstruct
2. InitializingBean.afterPropertiesSet()
3. @Bean(initMethod = "init")

-> Use only @PostConstruct (standard)

Senior Level

When NOT to Use @PostConstruct / @PreDestroy

  1. Heavy initialization - blocks context startup -> SmartLifecycle
  2. Calling @Transactional methods - proxy does not exist yet -> ContextRefreshedEvent
  3. Operations with retry - @PostConstruct does not retry -> SmartLifecycle
  4. Prototype beans - @PreDestroy is not called -> manually or DestructionCallback

Proxy Trap

@PostConstruct is called BEFORE proxy creation!

@Service
public class Service {
    @PostConstruct
    public void init() {
        loadData();  // @Transactional -> does NOT work!
    }

    @Transactional
    public void loadData() { }
}

-> Call goes to the original object, not the proxy
-> Transaction is NOT created

Solution:
  @EventListener(ContextRefreshedEvent.class)
  public void init() {
      loadData();  // Proxy already exists -> transaction works
      // ContextRefreshedEvent is published AFTER all beans are created
      // And all proxies too. So calling @Transactional methods from this
      // handler works correctly.
  }

Inheritance

class Parent {
    @PostConstruct
    public void parentInit() { }
}

class Child extends Parent {
    // If not overridden -> parentInit() is called
    // If overridden -> childInit() is called
}

CommonAnnotationBeanPostProcessor

Responsible for processing @PostConstruct and @PreDestroy
-> Registered automatically
-> Part of the standard BPP set

Production Experience

Real scenario: @PreDestroy was not called

// Prototype bean with @PreDestroy
@Scope("prototype")
public class Connection {
    @PreDestroy
    public void close() { }  // Will never be called!
}

// Connection leak!
// Solution: call close() manually

Best Practices

  1. @PostConstruct -> validation, light initialization
  2. @PreDestroy -> resource cleanup
  3. Do NOT call @Transactional methods from @PostConstruct
  4. Prototype -> @PreDestroy does not work
  5. Exception in @PostConstruct -> context will not start.

This is useful - application fails fast with incorrect configuration. But: if initialization can fail expectedly (no DB connection), use SmartLifecycle with retries instead of @PostConstruct.

  1. ContextRefreshedEvent -> when proxy is needed

Summary for Senior

  • @PostConstruct -> after DI, BEFORE proxy
  • @PreDestroy -> for Singleton only
  • Proxy Trap -> @Transactional does not work in @PostConstruct
  • ContextRefreshedEvent -> when proxy is ready
  • Prototype -> @PreDestroy is never called
  • Fail-fast -> exception in @PostConstruct = crash on startup

Interview Cheat Sheet

Must know:

  • @PostConstruct - executed once AFTER bean creation and dependency injection, BEFORE proxy creation
  • @PreDestroy - executed once BEFORE bean removal, for Singleton only
  • In constructor, Field/Setter dependencies are still NULL, in @PostConstruct - already injected
  • @Transactional does NOT work in @PostConstruct - proxy does not exist yet, call goes to original object
  • Prototype: @PostConstruct called each time on creation, @PreDestroy is NOT called automatically
  • Exception in @PostConstruct = application will not start (fail-fast - useful for incorrect configuration)
  • Spring 6 / Boot 3: jakarta.annotation.PostConstruct instead of javax.annotation
  • Order: @PostConstruct -> InitializingBean.afterPropertiesSet() -> init-method (use only @PostConstruct)

Common follow-up questions:

  • Why not use constructor for initialization? In constructor, Field/Setter dependencies are still NULL. For Constructor Injection - dependencies exist, but proxy does not.
  • How to call @Transactional method during initialization? @EventListener(ContextRefreshedEvent.class) - all proxies already created.
  • What if heavy initialization in @PostConstruct? Blocks context startup - use SmartLifecycle with retry.
  • What happens to @PreDestroy in Prototype? Never called - resource leak, clean manually.

Red flags (DO NOT say):

  • “In constructor, all dependencies already exist” (for Field/Setter injection - NULL)
  • @PreDestroy works for all scopes” (does NOT work for Prototype)
  • “You can call @Transactional methods from @PostConstruct” (proxy does not exist yet, transaction is NOT created)
  • “Exception in @PostConstruct is bad” (actually fail-fast is useful, but for expected errors use SmartLifecycle)

Related topics:

  • [[06. What is Bean Lifecycle]]
  • [[07. What are the Bean Lifecycle stages]]
  • [[08. What is BeanPostProcessor]]
  • [[04. What is a Bean in Spring]]
  • [[10. What is Bean scope]]