cd ..

Why Lifecycle Annotations Like @PostConstruct Don't Work in CDI Producers (and How to Fix It)

Contexts and Dependency Injection (CDI) is a powerful Java framework that can occasionally leave even seasoned developers scratching their heads. Recently, I found myself down in one of those rabbit holes—spending far too many hours debugging why lifecycle annotations like @Observes, @PostConstruct, and @PreDestroy have no effect when using @Producer methods. I debugged, searched the web and, because I’m a hopeless optimist, discussed with a LLM[1] for longer than I’d like to admit. But then I found an explanation on Stack Overflow, which led me on the right track:

[…] However, in case of producer method you have full control, how the object is created, so you can call any needed method by yourself. Take notice, that in producer methods object is usually created using new, so annotated fields are not initialized. […] Gas, Stack Overflow, Oct 17, 2014

So, if you—not the container—create the bean, you are responsible for calling the lifecycle methods? That sounds reasonable.

Let’s check the CDI spec[2]:

[…] The application may call bean constructors directly. However, if the application directly instantiates the bean, no parameters are passed to the constructor by the container; the returned object is not bound to any context; no dependencies are injected by the container; and the lifecycle of the new instance is not managed by the container. Ch. 3.5 Bean Constructors, CDI 3.0 Spec

I’m reading this as: “If you call new on a bean class yourself, the container won’t manage the lifecycle of the instance you created”. In hindsight, this seems painfully obvious.

The Problematic Code

// this logs +1 foo as expected, because it's called by the container
@Singleton class Foo {
  @PostConstruct void onCreate() {
    System.out.println("+1 foo");
  }
}
// this does not, since we're responsible for the lifecycle
class Foo {
  @PostConstruct void onCreate() {
    System.out.println("+1 foo");
  }
}
@Dependent class FooProducer {
  @Produces @Singleton void newFoo() {
    return new Foo();
  }
}

A Simple Fix

class Foo {...}
@Dependent class FooProducer {
  @Produces @Singleton void newFoo() {
    var foo = new Foo();
    foo.onCreate();
    return foo;
  }
}

For completeness’s sake: @PreDestroy would be handled with a disposer method:

class Foo {
  // [...]
  @PreDestroy void onDispose() {
    System.out.println("-1 foo");
  }
}
@Dependent class FooProducer {
  // [...]
  @Disposes void disposeFoo(Foo foo) {
    foo.onDispose();
  }
}

So today I learned something very… basic? Still, it was pretty hard to find an explanation online—which is why I wrote this post!



  1. https://chatgpt.com/share/6747b5e4-4fc8-800a-bbbb-c77eacf78469

  2. If you haven’t at least glanced over it, I really recommend it. I find myself referring to it often.