Understanding @PostConstruct in Spring: Specs, Lifecycle, and Alternatives

This article explains the JSR‑250 @PostConstruct annotation, its specifications, how Spring implements its lifecycle handling, execution timing, practical code examples, the deprecation in Java 9+, and alternative approaches such as InitializingBean and DisposableBean.

Senior Brother's Insights
Senior Brother's Insights
Senior Brother's Insights
Understanding @PostConstruct in Spring: Specs, Lifecycle, and Alternatives

Spring’s @PostConstruct and @PreDestroy annotations let developers run custom logic during bean initialization and destruction, but they are defined by the JSR‑250 specification, not by Spring itself.

JSR‑250 Specification

JSR‑250 defines a set of annotations in javax.annotation (e.g., @Resource, @PostConstruct, @PreDestroy) that describe how resources should be managed. The spec provides only the contract; concrete frameworks like Spring implement the behavior.

@PostConstruct Convention

According to JSR‑250, a method annotated with @PostConstruct must:

Have no parameters (unless used as an EJB interceptor).

Return void.

Not throw checked exceptions.

Be any visibility (public, protected, package‑private, or private).

Not be static (except for client applications).

May be final.

If it throws an unchecked exception, the bean must not be placed in service unless the exception can be handled and the bean can recover.

Execution Timing in Spring

When Spring creates a bean, it invokes methods annotated with @PostConstruct after the constructor finishes and before any init() method. The method runs once per bean instance during container startup.

PostConstruct execution flowchart
PostConstruct execution flowchart

Example Demonstration

In a Spring Boot project (Java 8), the following service shows the lifecycle callbacks:

@Service
public class OrderService {

    public OrderService(){
        System.out.println("OrderService constructor executed...");
    }

    @PostConstruct
    private void init() {
        System.out.println("PostConstruct method called");
    }

    @PreDestroy
    private void shutdown() {
        System.out.println("PreDestroy method called");
    }
}

Running the application prints:

OrderService constructor executed...
PostConstruct method called

When the application stops, the @PreDestroy method logs:

PreDestroy method called

Removal in Java 9+ and Workarounds

Starting with Java 9, @PostConstruct and @PreDestroy are deprecated and slated for removal in Java 11. Two common solutions are:

Add the legacy dependency manually:

<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.2</version>
</dependency>

Replace the annotations with Spring’s own lifecycle interfaces InitializingBean and DisposableBean:

@Service
public class PaymentService implements InitializingBean, DisposableBean {

    public PaymentService(){
        System.out.println("PaymentService constructor executed...");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet method called");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("destroy method called");
    }
}

Running this bean logs the constructor, then afterPropertiesSet, and on shutdown logs destroy.

Spring Implementation Details

Spring processes @PostConstruct via the BeanPostProcessor extension point. The concrete class CommonAnnotationBeanPostProcessor (subclass of InitDestroyAnnotationBeanPostProcessor) scans beans for the annotations and invokes the corresponding methods via reflection.

public interface BeanPostProcessor {
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

The processing flow is roughly:

--> Spring IOC container instantiates bean
--> Calls BeanPostProcessor.postProcessBeforeInitialization()
--> Invokes bean's @PostConstruct method(s)
--> Calls bean's init methods (e.g., afterPropertiesSet)
--> Calls BeanPostProcessor.postProcessAfterInitialization()

Inside InitDestroyAnnotationBeanPostProcessor, the method postProcessBeforeInitialization() builds lifecycle metadata, finds @PostConstruct methods, and invokes them.

InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization()
    -> findLifecycleMetadata()
        -> buildLifecycleMetadata()
            -> locate @PostConstruct methods
            -> locate @PreDestroy methods
    -> metadata.invokeInitMethods(bean, beanName);

Conclusion

Key takeaways: Spring merely implements the JSR‑250 contract for @PostConstruct; the annotation is deprecated from Java 9 onward, so adding the legacy dependency or switching to InitializingBean / DisposableBean is recommended for future‑proof code.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaspringLifecycleDependencyInjectionPostConstruct
Senior Brother's Insights
Written by

Senior Brother's Insights

A public account focused on workplace, career growth, team management, and self-improvement. The author is the writer of books including 'SpringBoot Technology Insider' and 'Drools 8 Rule Engine: Core Technology and Practice'.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.