Series MapLesson 06 / 112
Focus mode active/Press Alt+Shift+R to toggle/Esc to exit
Start HereOrdered learning track

Jakarta Namespace and Annotations

Jakarta Namespace Libraries and Annotations

Membaca namespace jakarta.*, library Jakarta, annotation penting, dan batas antara API standard, implementation, serta runtime enterprise

14 min read2722 words
PrevNext
Lesson 06112 lesson track01–21 Start Here
#jakarta#annotations#jakarta-ee#jax-rs+2 more

Part 006 — Jakarta Namespace, Libraries, and Annotations

Fokus part ini: mampu membaca import, annotation, dan dependency Jakarta dengan benar, lalu membedakan standard API, implementation, container-provided behavior, dan internal framework behavior.

Di codebase enterprise, banyak informasi architecture tersembunyi di import dan dependency. Sebelum memahami JAX-RS secara mendalam, kita perlu bisa membaca tanda-tanda seperti ini:

import jakarta.ws.rs.Path;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.core.Response;
import jakarta.inject.Inject;
import jakarta.annotation.PostConstruct;
import jakarta.validation.Valid;
import jakarta.enterprise.context.ApplicationScoped;
import org.glassfish.jersey.server.ResourceConfig;

Semua terlihat seperti “framework Java backend”, tetapi sebenarnya berasal dari lapisan yang berbeda.

Jika part sebelumnya membahas boundary Java SE, Servlet, Jakarta EE, JAX-RS, dan Jersey, part ini membahas cara membaca namespace dan annotation sebagai evidence praktis saat masuk codebase.


1. Core Mental Model

jakarta.* adalah namespace modern untuk banyak specification Jakarta. Namun namespace jakarta.* tidak otomatis berarti:

  • aplikasi memakai full Jakarta EE server,
  • CDI aktif,
  • semua annotation diproses runtime,
  • implementation tertentu tersedia,
  • behavior sama di semua container.

Mental model yang lebih benar:

jakarta.* imports
  -> indicate a standard API or annotation contract
  -> require an implementation/container/runtime to do something
  -> may be ignored if no processor/runtime integrates it
  -> may behave differently depending on implementation and configuration

Annotation adalah metadata. Metadata hanya berguna jika ada runtime/component yang membaca dan menindaklanjutinya.

Contoh:

@Path("/quotes")
public class QuoteResource { }

@Path tidak “membuka endpoint” sendirian. Endpoint terbuka jika:

  1. class masuk classpath,
  2. resource diregister atau ditemukan via scanning,
  3. JAX-RS implementation berjalan,
  4. servlet/application path benar,
  5. runtime menerima traffic,
  6. security/gateway tidak memblokir.

2. javax.* vs jakarta.*

Secara historis, banyak enterprise Java API memakai namespace javax.*. Setelah transisi ke Jakarta, banyak API berpindah ke jakarta.*.

Contoh lama:

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.inject.Inject;
import javax.validation.Valid;

Contoh modern:

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.inject.Inject;
import jakarta.validation.Valid;

Kenapa ini penting?

Karena mencampur javax.* dan jakarta.* bisa menyebabkan:

  • provider tidak terdeteksi,
  • annotation tidak diproses,
  • dependency conflict,
  • runtime class mismatch,
  • migration bug,
  • test jalan tetapi production gagal,
  • library lama tidak compatible dengan runtime baru.

Contoh bug migration

Resource memakai jakarta.ws.rs.Path, tetapi runtime JAX-RS lama hanya memahami javax.ws.rs.Path.

Akibatnya, class terlihat benar saat compile, tetapi tidak dianggap resource oleh runtime.

Sebaliknya, resource lama memakai javax.ws.rs.Path, sementara runtime modern hanya scanning jakarta.ws.rs.Path.

PR review heuristic

Jangan hanya cek compile. Cek compatibility matrix:

Java version
  -> Jakarta API version
  -> JAX-RS implementation version
  -> Servlet API version
  -> DI implementation version
  -> Validation provider version
  -> JSON provider version
  -> runtime/container version

3. API Jar vs Implementation Jar

Ini konsep paling penting dalam dependency Jakarta.

API jar biasanya berisi:

  • annotation,
  • interface,
  • abstract contract,
  • exception type,
  • constant.

Implementation jar berisi:

  • runtime behavior,
  • scanner,
  • dispatcher,
  • provider implementation,
  • integration logic,
  • lifecycle management.

Contoh dependency API:

<dependency>
  <groupId>jakarta.ws.rs</groupId>
  <artifactId>jakarta.ws.rs-api</artifactId>
</dependency>

Dependency ini membuat code seperti ini bisa compile:

@Path("/quotes")
public class QuoteResource { }

Namun API jar sendiri tidak menjalankan HTTP server dan tidak melakukan routing.

Contoh implementation Jersey:

<dependency>
  <groupId>org.glassfish.jersey.core</groupId>
  <artifactId>jersey-server</artifactId>
</dependency>

Contoh integration Jersey dengan Servlet:

<dependency>
  <groupId>org.glassfish.jersey.containers</groupId>
  <artifactId>jersey-container-servlet</artifactId>
</dependency>

Contoh dependency injection integration Jersey/HK2:

<dependency>
  <groupId>org.glassfish.jersey.inject</groupId>
  <artifactId>jersey-hk2</artifactId>
</dependency>

Senior rule

Jika melihat annotation Jakarta, selalu tanya:

Siapa yang membaca annotation ini di runtime?

Jika tidak bisa menjawab, kita belum memahami behavior-nya.


4. Jakarta REST / JAX-RS Namespace

Namespace utama:

jakarta.ws.rs.*
jakarta.ws.rs.core.*
jakarta.ws.rs.container.*
jakarta.ws.rs.ext.*

4.1 Resource annotation

import jakarta.ws.rs.Path;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.PATCH;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.Produces;

Digunakan untuk mendefinisikan route dan HTTP method.

Contoh:

@Path("/quotes")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class QuoteResource {

    @POST
    public Response createQuote(CreateQuoteRequest request) {
        return Response.status(Response.Status.CREATED).build();
    }
}

Review questions:

  • Apakah method semantics benar?
  • Apakah @Consumes/@Produces eksplisit?
  • Apakah class-level dan method-level annotation saling override secara jelas?
  • Apakah route conflict dengan resource lain?

4.2 Parameter binding annotation

import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.HeaderParam;
import jakarta.ws.rs.CookieParam;
import jakarta.ws.rs.FormParam;
import jakarta.ws.rs.MatrixParam;
import jakarta.ws.rs.DefaultValue;

Contoh:

@GET
@Path("/{quoteId}")
public QuoteResponse getQuote(
    @PathParam("quoteId") String quoteId,
    @QueryParam("includePrices") @DefaultValue("true") boolean includePrices,
    @HeaderParam("X-Correlation-Id") String correlationId
) {
    // ...
}

Review questions:

  • Apakah parameter wajib/opsional eksplisit?
  • Apakah default aman?
  • Apakah header transport langsung bocor ke domain layer?
  • Apakah conversion failure ditangani konsisten?

4.3 Core types

import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.UriInfo;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.SecurityContext;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.StreamingOutput;

Contoh:

@GET
@Path("/{quoteId}")
public Response getQuote(@PathParam("quoteId") String quoteId) {
    QuoteResponse body = service.getQuote(quoteId);
    return Response.ok(body)
        .header("Cache-Control", "no-store")
        .build();
}

Review questions:

  • Apakah Response dipakai karena butuh status/header eksplisit?
  • Apakah entity type masih jelas untuk generated docs?
  • Apakah SecurityContext hanya dipakai di boundary layer?

4.4 Container filter/interceptor

import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.container.ContainerResponseFilter;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerResponseContext;

Contoh:

@Provider
public class CorrelationIdFilter implements ContainerRequestFilter, ContainerResponseFilter {
    @Override
    public void filter(ContainerRequestContext requestContext) {
        // read or create correlation id
    }

    @Override
    public void filter(ContainerRequestContext requestContext,
                       ContainerResponseContext responseContext) {
        // write correlation id response header
    }
}

Review questions:

  • Apakah filter global terlalu agresif?
  • Apakah filter order jelas?
  • Apakah filter aman terhadap exception?
  • Apakah context dibersihkan setelah request?

4.5 Extension provider

import jakarta.ws.rs.ext.Provider;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.MessageBodyReader;
import jakarta.ws.rs.ext.MessageBodyWriter;

Contoh:

@Provider
public class DomainExceptionMapper implements ExceptionMapper<DomainException> {
    @Override
    public Response toResponse(DomainException exception) {
        return Response.status(Response.Status.CONFLICT)
            .entity(ErrorResponse.from(exception))
            .build();
    }
}

Review questions:

  • Apakah mapper terlalu umum?
  • Apakah mapper menangkap exception yang seharusnya menjadi 500?
  • Apakah error response stable?
  • Apakah provider registered di runtime?

5. Jakarta Annotation Namespace

Namespace:

jakarta.annotation.*

Annotation yang sering muncul:

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.annotation.Priority;

5.1 @PostConstruct

Dipakai untuk logic setelah dependency injection selesai.

public class CatalogClient {
    private final Config config;

    @Inject
    public CatalogClient(Config config) {
        this.config = config;
    }

    @PostConstruct
    void initialize() {
        // validate config, initialize expensive resource
    }
}

Risiko:

  • tidak dipanggil jika object dibuat manual dengan new,
  • startup lambat jika melakukan I/O berat,
  • exception bisa membuat application gagal start,
  • behavior tergantung container/integration.

Review questions:

  • Apakah initialization harus fail-fast?
  • Apakah method melakukan network call saat startup?
  • Apakah lifecycle hook benar-benar dipanggil di test dan production?

5.2 @PreDestroy

Dipakai untuk cleanup sebelum bean dihancurkan.

@PreDestroy
void shutdown() {
    executor.shutdown();
}

Risiko:

  • tidak dipanggil jika object tidak dikelola container,
  • shutdown terlalu lambat bisa melewati Kubernetes termination grace period,
  • resource leak jika cleanup tidak terjadi.

Review questions:

  • Siapa owner resource?
  • Apakah cleanup idempotent?
  • Apakah shutdown timeout aman?

5.3 @Priority

Sering dipakai untuk ordering provider/filter/interceptor.

@Provider
@Priority(1000)
public class AuthenticationFilter implements ContainerRequestFilter {
    // ...
}

Review questions:

  • Apakah order security/logging/correlation jelas?
  • Apakah ada magic number tanpa constant?
  • Apakah order berbeda antar implementation?

6. Jakarta Inject Namespace

Namespace:

jakarta.inject.*

Annotation umum:

import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import jakarta.inject.Provider;
import jakarta.inject.Named;
import jakarta.inject.Qualifier;

6.1 @Inject

@Inject adalah annotation standard untuk injection, tetapi tidak menentukan container mana yang melakukan injection.

public class QuoteResource {
    private final QuoteService quoteService;

    @Inject
    public QuoteResource(QuoteService quoteService) {
        this.quoteService = quoteService;
    }
}

Pertanyaan penting:

Injection ini dilakukan oleh HK2, CDI, framework internal, atau test framework?

@Inject sendiri tidak cukup menjawab.

6.2 Constructor injection vs field injection

Lebih mudah direview:

public class QuoteService {
    private final QuoteRepository repository;

    @Inject
    public QuoteService(QuoteRepository repository) {
        this.repository = repository;
    }
}

Lebih sulit dites dan lebih tersembunyi:

public class QuoteService {
    @Inject
    private QuoteRepository repository;
}

Senior preference biasanya constructor injection untuk dependency wajib.

6.3 Provider<T>

Dipakai saat dependency ingin di-resolve lazily atau per request.

@Inject
Provider<RequestContext> requestContextProvider;

Risiko:

  • lifecycle tidak jelas,
  • dependency tersembunyi,
  • request scope bisa diakses di luar request,
  • lazy failure muncul di runtime path tertentu.

6.4 @Singleton

@Singleton menunjukkan satu instance dalam container tertentu.

Risiko besar:

  • mutable state shared antar request,
  • thread-safety wajib,
  • lifecycle panjang,
  • cache internal bisa stale,
  • tenant leakage jika menyimpan context request.

Contoh berbahaya:

@Singleton
public class CurrentTenantHolder {
    private String tenantId;
}

Ini berbahaya karena singleton shared antar request.


7. CDI Namespace

Namespace umum:

jakarta.enterprise.context.*
jakarta.enterprise.inject.*

Contoh:

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.context.RequestScoped;
import jakarta.enterprise.inject.Produces;
import jakarta.enterprise.inject.Alternative;
import jakarta.enterprise.inject.Specializes;

7.1 CDI scope

@ApplicationScoped
public class PricingService {
    // one application-scoped bean
}
@RequestScoped
public class RequestContextHolder {
    // one per request if CDI request context active
}

Review questions:

  • Apakah CDI aktif di runtime?
  • Apakah request context tersedia di JAX-RS thread?
  • Apakah scope tepat untuk mutable state?
  • Apakah bean discovery benar?

7.2 Producer

@Produces
@ApplicationScoped
public ObjectMapper objectMapper() {
    return configuredObjectMapper;
}

Producer berguna, tetapi bisa menciptakan hidden global configuration.

Review questions:

  • Siapa owner object yang diproduksi?
  • Apakah object thread-safe?
  • Apakah ada lebih dari satu producer untuk type yang sama?
  • Apakah qualifier jelas?

7.3 CDI vs HK2

Jika service memakai Jersey, injection bisa memakai HK2. Jika CDI juga aktif, perlu paham integration.

Masalah umum:

JAX-RS resource created by Jersey/HK2
  but dependency registered as CDI bean only

atau sebaliknya:

CDI bean wants Jersey/HK2 service
  but binding only exists in HK2 service locator

Hasilnya bisa berupa:

  • unsatisfied dependency,
  • ambiguous dependency,
  • null injection,
  • duplicate singleton,
  • lifecycle hook tidak dipanggil,
  • different instance in test vs production.

8. Jakarta Validation Namespace

Namespace:

jakarta.validation.*

Annotation umum:

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.Max;

Contoh DTO:

public record CreateQuoteRequest(
    @NotBlank String customerId,
    @NotBlank String catalogVersion,
    @Valid List<CreateQuoteLineRequest> lines
) {}

JAX-RS integration dapat memvalidasi request body jika validation provider dan integration tersedia.

Namun jakarta.validation-api sendiri hanya API. Implementation umum adalah Hibernate Validator.

Review questions

  • Apakah validation otomatis aktif untuk resource method?
  • Apakah @Valid dipakai untuk nested object?
  • Apakah error validation dipetakan ke response contract yang konsisten?
  • Apakah validation groups dipakai?
  • Apakah domain invariant tidak bocor menjadi sekadar DTO annotation?

Common failure

DTO punya annotation, tetapi tidak divalidasi karena:

  • @Valid hilang,
  • provider validation tidak tersedia,
  • integration JAX-RS/validation tidak aktif,
  • parameter tidak dianggap bean validation target,
  • test tidak menjalankan container validation.

9. Jakarta Servlet Namespace

Namespace:

jakarta.servlet.*
jakarta.servlet.http.*

Contoh:

import jakarta.servlet.Filter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

Servlet API biasanya muncul pada:

  • low-level filter,
  • request logging,
  • security integration,
  • session/cookie logic,
  • framework adapter,
  • servlet listener,
  • integration dengan Jersey servlet container.

Boundary rule

Servlet API sebaiknya dikurung di infrastructure/web layer.

Hindari domain/application service yang menerima HttpServletRequest langsung.

Bad:

public void submitQuote(HttpServletRequest request, SubmitQuoteRequest dto) { }

Better:

public void submitQuote(RequestContext context, SubmitQuoteCommand command) { }

Servlet filter vs JAX-RS filter

Servlet filter:

public class RawFilter implements Filter { }

JAX-RS filter:

@Provider
public class ApiFilter implements ContainerRequestFilter { }

Review questions:

  • Filter ini harus jalan sebelum atau sesudah JAX-RS matching?
  • Apakah butuh raw servlet request?
  • Apakah filter order jelas?
  • Apakah filter berjalan untuk static/error/health endpoint juga?

10.1 JSON-B

Namespace:

jakarta.json.bind.*

JSON-B adalah standard binding JSON Jakarta.

Contoh:

import jakarta.json.bind.annotation.JsonbProperty;

Jika service memakai Jackson, annotation JSON-B mungkin tidak berpengaruh kecuali provider JSON-B aktif.

10.2 JSON-P

Namespace:

jakarta.json.*

JSON-P adalah API untuk processing JSON object/stream level.

10.3 JAXB

Namespace:

jakarta.xml.bind.*

Dipakai untuk XML binding.

Contoh:

import jakarta.xml.bind.annotation.XmlRootElement;

Review questions

  • JSON provider yang aktif Jackson atau JSON-B?
  • Annotation serialization mana yang benar-benar dibaca runtime?
  • Apakah ada campuran Jackson annotation dan JSON-B annotation?
  • Apakah XML masih bagian contract?
  • Apakah XML security seperti XXE diproteksi?

11. Common Jakarta Library Map

ConcernStandard API namespaceCommon implementation/runtimeNotes
REST APIjakarta.ws.rs.*Jersey, RESTEasy, CXFAPI jar tidak cukup menjalankan server
Servletjakarta.servlet.*Tomcat, Jetty, GlassFish, UndertowContainer menyediakan runtime
Injectionjakarta.inject.*HK2, CDI container, internal DIAnnotation saja bukan container
CDIjakarta.enterprise.*Weld, OpenWebBeans, app server CDIPerlu CDI integration
Validationjakarta.validation.*Hibernate ValidatorPerlu provider dan integration
JSON-Bjakarta.json.bind.*Yasson atau provider lainBerbeda dari Jackson
XML Bindingjakarta.xml.bind.*JAXB implementationXML support perlu provider
Annotation lifecyclejakarta.annotation.*Container/DI runtime@PostConstruct butuh managed object

Gunakan tabel ini sebagai peta awal, bukan kebenaran final. Codebase internal bisa memiliki wrapper/framework sendiri.


12. Maven Scope and Runtime Meaning

Dependency scope memengaruhi apakah class tersedia saat compile, test, dan runtime.

Contoh:

<dependency>
  <groupId>jakarta.servlet</groupId>
  <artifactId>jakarta.servlet-api</artifactId>
  <scope>provided</scope>
</dependency>

provided berarti dependency dibutuhkan saat compile, tetapi diasumsikan disediakan runtime/container.

Ini masuk akal jika deploy ke Servlet container yang menyediakan Servlet API.

Namun jika membuat executable JAR tanpa container yang menyediakan API tersebut, provided bisa menyebabkan runtime error.

Common dependency questions

  • Apakah dependency API scope-nya provided karena container menyediakan implementation?
  • Apakah dependency implementation scope-nya runtime atau compile?
  • Apakah test classpath berbeda dari production?
  • Apakah Docker image membawa semua dependency yang diperlukan?
  • Apakah ada dependency lama javax.* yang tertarik transitively?

13. Annotation Processing and Runtime Scanning

Tidak semua annotation diproses dengan cara yang sama.

13.1 Compile-time processing

Contoh: MapStruct annotation diproses saat compile untuk generate code.

Jika annotation processor tidak jalan, generated class tidak ada.

13.2 Runtime reflection/scanning

Contoh: JAX-RS implementation scanning class dengan @Path atau provider @Provider.

Jika package scanning salah, annotation ada tetapi tidak ditemukan.

13.3 Container lifecycle processing

Contoh: @PostConstruct, CDI scope, injection.

Jika object tidak dikelola container, annotation tidak diproses.

13.4 Framework-specific processing

Contoh: Jersey ResourceConfig.register(...) atau HK2 binder.

Jika registration tidak dilakukan, annotation standard mungkin tidak cukup.

Practical implication

Annotation bukan magic. Selalu cari processor-nya.

Annotation present
  -> processor/scanner exists?
  -> class is included in scan path?
  -> runtime integration enabled?
  -> dependency implementation present?
  -> lifecycle managed by container?

14. Mixed Annotation Problems

14.1 Jackson vs JSON-B

import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.json.bind.annotation.JsonbProperty;

Jika keduanya muncul, tanyakan:

  • Provider JSON apa yang aktif?
  • Annotation mana yang dibaca?
  • Apakah field name contract konsisten?
  • Apakah generated OpenAPI mengikuti salah satu?

14.2 javax.validation vs jakarta.validation

import javax.validation.Valid;

vs

import jakarta.validation.Valid;

Jika runtime modern Jakarta memakai jakarta.validation, annotation lama bisa tidak diproses.

14.3 CDI scope vs HK2 scope

@ApplicationScoped
public class PricingService { }

Jika CDI tidak aktif, annotation ini mungkin tidak berarti apa-apa untuk HK2 kecuali ada bridge/integration.

14.4 Servlet filter vs JAX-RS provider

Keduanya bisa sama-sama melakukan logging/security, tetapi urutan dan context berbeda.

Masalah umum:

  • correlation ID dibuat dua kali,
  • auth dilakukan dua kali,
  • response header ditimpa,
  • exception mapping tidak konsisten,
  • metrics double-counted.

15. Internal Verification Checklist

Saat membaca codebase internal, gunakan checklist berikut.

15.1 Namespace inventory

  • Apakah mayoritas code memakai jakarta.* atau javax.*?
  • Apakah ada campuran namespace lama/baru?
  • Package mana yang masih javax.*?
  • Apakah dependency transitif membawa API lama?
  • Apakah generated code memakai namespace yang sama?

15.2 JAX-RS evidence

  • Apakah resource memakai jakarta.ws.rs.*?
  • Apakah provider memakai jakarta.ws.rs.ext.Provider?
  • Apakah filter memakai jakarta.ws.rs.container.*?
  • Apakah application bootstrap memakai Application atau Jersey ResourceConfig?
  • Apakah resources diregister scanning atau explicit?

15.3 Implementation evidence

  • Apakah ada dependency org.glassfish.jersey.*?
  • Apakah ada dependency org.jboss.resteasy.*?
  • Apakah ada dependency org.apache.cxf.*?
  • Apakah startup log menyebut implementation tertentu?
  • Apakah ada class implementation-specific di app layer?

15.4 DI evidence

  • Apakah injection memakai jakarta.inject.Inject?
  • Apakah ada jakarta.enterprise.* CDI annotation?
  • Apakah ada HK2 AbstractBinder?
  • Apakah ada framework internal untuk service locator/config?
  • Apakah test membuat object manual sehingga injection berbeda dari production?

15.5 Validation evidence

  • Apakah DTO memakai jakarta.validation.*?
  • Apakah Hibernate Validator dependency tersedia?
  • Apakah validation error punya mapper standard?
  • Apakah nested DTO memakai @Valid?
  • Apakah validation aktif di endpoint test?

15.6 Serialization evidence

  • Apakah JSON memakai Jackson, JSON-B, atau keduanya?
  • Apakah ada custom ObjectMapper?
  • Apakah date/time format distandardkan?
  • Apakah unknown field diterima atau ditolak?
  • Apakah XML/JAXB masih dipakai?

15.7 Lifecycle evidence

  • Apakah @PostConstruct/@PreDestroy dipakai?
  • Apakah object yang memilikinya managed by container?
  • Apakah startup logs menunjukkan initialization?
  • Apakah shutdown hook/probe memberi waktu cleanup?

16. PR Review Checklist

Saat mereview perubahan yang menambah annotation/dependency Jakarta:

  • Apakah annotation berasal dari namespace yang benar?
  • Apakah annotation diproses oleh runtime yang aktif?
  • Apakah dependency API dan implementation tersedia?
  • Apakah scope dependency Maven benar?
  • Apakah tidak mencampur javax.* dan jakarta.* tanpa alasan?
  • Apakah implementation-specific import dikurung di infrastructure layer?
  • Apakah @Inject dependency jelas binding-nya?
  • Apakah scope bean aman untuk thread dan tenant?
  • Apakah @PostConstruct tidak melakukan I/O berat tanpa alasan?
  • Apakah validation annotation benar-benar diuji lewat endpoint/integration test?
  • Apakah serialization annotation sesuai provider aktif?
  • Apakah change membutuhkan update OpenAPI/contract/test?

17. Failure Modes and Debugging

17.1 Annotation ada tapi tidak berefek

Kemungkinan:

  • class tidak di-scan,
  • object dibuat manual,
  • runtime tidak mendukung annotation tersebut,
  • implementation dependency tidak ada,
  • namespace mismatch javax vs jakarta,
  • profile men-disable registration.

Debug:

  1. Cek import annotation.
  2. Cek dependency API/implementation.
  3. Cek registration/scanning.
  4. Cek startup logs.
  5. Cek test yang menjalankan runtime nyata.

17.2 Compile berhasil, runtime gagal

Kemungkinan:

  • API jar ada, implementation tidak ada,
  • dependency provided tidak tersedia di runtime,
  • container version mismatch,
  • classpath conflict,
  • transitive dependency override.

Debug:

  • inspect dependency tree,
  • inspect packaged artifact,
  • inspect Docker image,
  • compare local/test/prod classpath,
  • check startup exception root cause.

17.3 Injection ambiguity

Kemungkinan:

  • dua bean untuk type yang sama,
  • qualifier hilang,
  • HK2 dan CDI sama-sama punya binding,
  • producer duplicate,
  • test binding berbeda.

Debug:

  • cari semua implementation type,
  • cari binder/producer,
  • cek qualifier,
  • cek scope,
  • cek runtime container yang membuat object.

17.4 Serialization annotation ignored

Kemungkinan:

  • memakai Jackson annotation tetapi provider JSON-B aktif,
  • memakai JSON-B annotation tetapi Jackson aktif,
  • custom object mapper override,
  • generated DTO tidak memiliki annotation yang sama.

Debug:

  • cek active provider,
  • cek dependency JSON,
  • cek ObjectMapper/JsonbConfig,
  • buat endpoint serialization test.

18. Senior Engineer Heuristics

  1. Import is evidence, not full truth. Import menunjukkan API yang dipakai, bukan runtime yang aktif.
  2. API jar is not implementation. Compile success tidak menjamin runtime behavior.
  3. Annotation needs a reader. Selalu cari scanner/processor/container yang memproses annotation.
  4. Namespace consistency matters. Campuran javax/jakarta harus dianggap risk sampai terbukti aman.
  5. Scope is architecture. @Singleton, @RequestScoped, @ApplicationScoped mengubah concurrency dan lifecycle risk.
  6. Implementation-specific import is a boundary decision. Jersey/HK2 import boleh, tetapi harus sadar lokasinya.
  7. Test must match runtime. Unit test manual wiring tidak membuktikan injection/scanning/validation runtime.

19. Mini Exercise

Ambil satu resource class di codebase dan jawab:

  1. Annotation JAX-RS apa saja yang dipakai?
  2. Apakah semua import memakai jakarta.* atau ada javax.*?
  3. Resource ditemukan via scanning atau explicit registration?
  4. JSON provider yang aktif apa?
  5. Validation provider yang aktif apa?
  6. Apakah class dibuat oleh HK2, CDI, atau runtime lain?
  7. Apakah constructor injection atau field injection?
  8. Apakah ada lifecycle hook?
  9. Apakah ada implementation-specific annotation/import?
  10. Apakah test membuktikan runtime behavior atau hanya method call biasa?

Jika bisa menjawab ini, kita sudah mulai membaca codebase sebagai runtime system.


20. Summary

Part ini membahas cara membaca namespace dan annotation Jakarta secara benar:

  • jakarta.* menunjukkan standard API/annotation, bukan otomatis full Jakarta EE runtime.
  • javax.* vs jakarta.* adalah boundary penting untuk compatibility.
  • API jar berbeda dari implementation jar.
  • JAX-RS annotation perlu implementation seperti Jersey/RESTEasy/CXF.
  • @Inject perlu DI runtime seperti HK2/CDI/internal framework.
  • @PostConstruct/@PreDestroy hanya berlaku untuk managed object.
  • Validation annotation perlu provider dan integration.
  • Serialization annotation hanya berguna jika provider yang membaca annotation itu aktif.
  • Dependency scope dan runtime packaging menentukan apakah code benar-benar berjalan.

Kemampuan utama setelah part ini: saat membaca import dan dependency, kita bisa membedakan standard contract, implementation detail, runtime responsibility, dan hal yang harus diverifikasi secara internal.

Lesson Recap

You just completed lesson 06 in start here. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.

Continue The Track

Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.