Annotations | Quarkus bits

February 18, 2026

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 @ApplicationScoped for application-wide beans unless you have a strong reason to avoid proxies or need @Singleton semantics.

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);