What Annotations do:
Annotations are metadata markers that provide information about the code but don't directly affect the code's operation. They are processed at compile-time or runtime by frameworks to generate code, inject dependencies, or modify behavior.
How they work:
Compile-time processing (e.g., Lombok's @Getter): The annotation processor generates additional code before compilation
Runtime processing (e.g., Spring's @Inject): Frameworks scan annotations at startup and use reflection to inject beans, configure behavior, etc.
Why use annotations:
Reduce boilerplate: @Getter generates getters automatically instead of writing them manually
Configuration as code: @ConfigProperty binds config values without manual parsing
Dependency injection: @Inject eliminates manual object creation and wiring
Lifecycle management: @Startup runs code at specific application stages
Framework integration: Spring, Quarkus, etc. rely on annotations to manage beans and routes
Annotations:
`@ApplicationScoped` and `@Singleton`
In Quarkus (and CDI in general), both @ApplicationScoped and @Singleton give you “one instance for the whole application”, but they differ in how CDI manages that instance and in some practical behaviors.
What @ApplicationScoped does
- It is a normal CDI scope (from
jakarta.enterprise.context.ApplicationScoped). - CDI injects a proxy instead of the real instance. The real bean is created lazily, when you first call a method on that proxy.
- There is one logical instance per application, shared across all injection points.
- Because of the proxy, the container can:
- Destroy/recreate the bean at runtime while keeping existing injection points valid.
- Allow things like mocking/replacing the bean in Quarkus tests.
Typical use: shared services, clients, and stateful components where you want CDI’s full lifecycle/proxy behavior.
What @Singleton does
- It is a pseudo-scope from
jakarta.inject.Singleton, not a normal CDI scope. - CDI injects the real instance directly (no proxy).
- The bean is created eagerly when it is first injected (at injection time, not at first method call).
- There is still one instance per application, but:
- You cannot easily destroy/recreate it under the hood, because everyone holds the real instance.
- In Quarkus, you generally cannot mock/override such beans with the usual test facilities.
Typical use: you really want a single instance with no proxy indirection, or the class cannot be proxied (e.g. final classes, some special cases).
Practical differences summarized
- Proxy vs real instance
@ApplicationScoped: injects a proxy that delegates to the real instance.@Singleton: injects the real instance, no proxy.
- Creation time
@ApplicationScoped: lazy (first method call).@Singleton: eager (when injected).
- Testing & replacement
@ApplicationScoped: can be mocked/replaced in Quarkus tests.@Singleton: much harder to mock/replace.
- Recommendation
- Default to
@ApplicationScopedfor application-wide beans unless you have a strong reason to avoid proxies or need@Singletonsemantics.
- Default to
package com.seek.service;
import com.seek.components.job.service.JobRecommendationsService;
import jakarta.inject.Inject;
public class Test {
@Inject
JobRecommendationsService job;
public Test() {
}
}
/// quarkus
class QuarkustJobRecommendationsService {
JobRecommendationsService job;
public QuarkustJobRecommendationsService() {
}
public getJobRecommendations() {
get().getJobRecommendations();
}
private JobRecommendationsService get() {
if (job is null )
job = new JobRecommendationsService();
return job
}
}
./// DI Contaniner snippet code
Test test = new Test();
test.job = DIContainer.get(JobRecommendationsService);