Beans and injection
CDI (Contexts and Dependency Injection) is the specification that creates application objects and wires them together. An object managed by CDI is called a bean. Instead of new PostStore(), a class simply declares that it needs a store, and the CDI container creates it and injects it into a field. This way every part of the application gets the same object, and the code does not depend on how that object is built.
Which class becomes a bean
In Jakarta EE a class becomes a bean when it has a scope annotation: @ApplicationScoped, @RequestScoped, @SessionScoped, @Dependent. The store from the previous chapter is already a bean:
// src/main/java/com/example/blog/PostStore.java (fragment)
@ApplicationScoped
public class PostStore {
// ...
}You do not need a beans.xml file for this: the server finds classes with these annotations by itself. Jakarta REST resources, servlets and filters are also managed by the container, so injection works in them.
Field injection
The resource gets the store with the @Inject annotation:
// src/main/java/com/example/blog/PostResource.java (fragment)
@Inject
PostStore store;The same works in a servlet. Create src/main/java/com/example/blog/StatsServlet.java:
// src/main/java/com/example/blog/StatsServlet.java
package com.example.blog;
import jakarta.inject.Inject;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/stats")
public class StatsServlet extends HttpServlet {
@Inject
PostStore store;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
response.setContentType("text/plain; charset=UTF-8");
response.getWriter().println("Posts in the blog: " + store.findAll().size());
}
}The servlet and the REST resource got the same store object. That is easy to check: add a post through the API and look at the counter.
curl http://localhost:8080/blog/stats
curl -H "Content-Type: application/json" -d '{"title":"CDI","body":"Beans and injection."}' http://localhost:8080/blog/api/posts
curl http://localhost:8080/blog/statsPosts in the blog: 2
{"body":"Beans and injection.","createdAt":"2026-10-04","id":3,"title":"CDI"}
Posts in the blog: 3
Constructor injection
Beans can depend on each other. The publishing service gets the store through its constructor:
// src/main/java/com/example/blog/PostService.java
package com.example.blog;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
@ApplicationScoped
public class PostService {
private PostStore store;
protected PostService() {
// needed by the container to create a proxy
}
@Inject
public PostService(PostStore store) {
this.store = store;
}
public Post publish(NewPost data) {
return store.add(data);
}
}For @ApplicationScoped beans the container creates a proxy — an intermediary object that forwards calls to the real bean. The proxy needs a no-argument constructor, so next to the @Inject constructor you need protected PostService(). Without it the application fails to start with WELD-001410: The injection point has non-proxyable dependencies. In the resource, replace store.add(data) with a service call:
// src/main/java/com/example/blog/PostResource.java (fragment)
@Inject
PostService service;
// in the create method:
Post post = service.publish(data);Ways to inject
| Way | Example | When it is handy |
|---|---|---|
| Field | @Inject PostStore store; | Resources, servlets, short code |
| Constructor | @Inject PostService(PostStore store) | Dependencies are visible at once, easy to test |
| Method | @Inject void setStore(PostStore store) | Rarely |
Common mistake
Forgetting the scope annotation. If you remove @ApplicationScoped from PostStore, the class is no longer a bean, and the application does not start. The server log shows:
WELD-001408: Unsatisfied dependencies for type PostStore with qualifiers @Default
at injection point [BackedAnnotatedField] @Inject com.example.blog.PostResource.store
"Unsatisfied dependencies" means the container found no matching bean for a field with @Inject.