Lightweight Jakarta CDI bridge for GuicedEE applications.
Maps CDI annotations (@Inject, @Named, @ApplicationScoped, @Qualifier) to their Guice equivalents, provides a BeanManager adapter backed by the Guice injector, and registers itself as the Jakarta CDIProvider — so CDI-annotated code runs seamlessly inside a Guice-managed container.
Built on Google Guice · Jakarta CDI · JPMS module com.guicedee.cdi · Java 25+
<dependency>
<groupId>com.guicedee</groupId>
<artifactId>cdi</artifactId>
</dependency>Gradle (Kotlin DSL)
implementation("com.guicedee:cdi:2.0.0-SNAPSHOT")- CDI → Guice annotation mapping —
jakarta.inject.Inject,jakarta.inject.Named,jakarta.inject.Singleton, andjakarta.inject.Qualifierare mapped to their Guice counterparts automatically - Scope bridging —
@jakarta.inject.Singletonand@jakarta.enterprise.context.ApplicationScopedare bound to Guice'sSINGLETONscope viaBindScopeProvision BeanManageradapter —GuiceCDIBeanManagerAdapterimplementsjakarta.enterprise.inject.spi.BeanManagerand delegates toIGuiceContextfor bean resolutionCDIProviderregistration —JakartaCDIProvideris set as the JakartaCDIprovider on startup, soCDI.current()returns a Guice-backedGuicedCDIinstanceGuiceCDIBeanManager— simplified bean lookup API with support for type,@Namedqualifier, and annotation qualifier- SPI-driven wiring — five Guice SPI providers (
BindScopeProvider,InjectionPointProvider,BindingAnnotationProvider,NamedAnnotationProvider,InjectorAnnotationsProvider) are registered via JPMSprovides - Automatic module loading —
GuiceCDIModuleis discovered viaServiceLoader/ JPMS; no manual installation required - JPMS-first — full
module-info.javawith properexports,requires,provides, andusesdirectives
Step 1 — Add the dependency (see Installation above).
Step 2 — Annotate your classes with standard Jakarta CDI annotations:
@jakarta.enterprise.context.ApplicationScoped
public class GreetingService {
public String greet(String name) {
return "Hello, " + name + "!";
}
}Step 3 — Inject anywhere via @Inject:
public class WelcomeResource {
@jakarta.inject.Inject
private GreetingService greeter;
public String hello(String name) {
return greeter.greet(name);
}
}Step 4 — Declare the dependency in your module-info.java:
module my.app {
requires com.guicedee.cdi;
}Step 5 — Bootstrap GuicedEE:
IGuiceContext.registerModuleForScanning.add("my.app");
IGuiceContext.instance();
WelcomeResource resource = IGuiceContext.get(WelcomeResource.class);
resource.hello("World"); // "Hello, World!"The CDI module itself is loaded automatically — no JPMS provides declaration is needed in your application module.
Startup
IGuiceContext.instance()
└─ IGuiceModule hooks
└─ GuiceCDIModule.configure()
├─ bind(GuiceCDIBeanManager.class) → Singleton
├─ bind(ICDIProvider.class) → GuiceCDIProvider (Singleton)
├─ bind(GuiceCDIBeanManagerAdapterImpl.class) → Singleton
├─ bind(BeanManager.class) → GuiceCDIBeanManagerAdapterImpl
├─ bind(JakartaCDIProvider.class) → Singleton
└─ CDI.setCDIProvider(new JakartaCDIProvider())
└─ Guice SPI providers (auto-registered via JPMS)
├─ BindScopeProvision → @Singleton, @ApplicationScoped → SINGLETON
├─ InjectionPointProvision → @Inject, @Named, @PostConstruct detection
├─ BindingAnnotationsProvision → @Qualifier as binding annotation
├─ NamedAnnotationProvision → jakarta.inject.Named → Guice Names.named()
└─ InjectorAnnotationsProvision → jakarta.inject.Inject recognition
CDI.current()
→ JakartaCDIProvider.getCDI()
→ GuicedCDI (singleton)
→ select(Class<T>)
→ IGuiceContext.get(Class<T>) ← Guice injector lookup
@Inject BeanManager beanManager
→ GuiceCDIBeanManagerAdapterImpl
→ getReference(bean, type, ctx) → IGuiceContext.get(type)
→ getInjectableReference(ip, ctx) → IGuiceContext.get(ip.getType())
→ createCreationalContext(...) → no-op implementation
The CDI module registers five Guice SPI providers that teach the Guice runtime how to handle Jakarta CDI annotations:
| SPI Interface | Implementation | Purpose |
|---|---|---|
BindScopeProvider |
BindScopeProvision |
Binds @jakarta.inject.Singleton and @ApplicationScoped to Guice's SINGLETON scope |
InjectionPointProvider |
InjectionPointProvision |
Detects @Inject, @Named, and @PostConstruct on annotated members |
BindingAnnotationProvider |
BindingAnnotationsProvision |
Registers @Qualifier as a Guice binding annotation marker |
NamedAnnotationProvider |
NamedAnnotationProvision |
Converts jakarta.inject.Named to com.google.inject.name.Names.named() |
InjectorAnnotationsProvider |
InjectorAnnotationsProvision |
Identifies jakarta.inject.Inject as an injector annotation |
Simplified bean lookup API backed by IGuiceContext:
@Inject
private GuiceCDIBeanManager beanManager;
// By type
MyService svc = beanManager.getBean(MyService.class);
// By type + @Named qualifier
MyService svc = beanManager.getBean(MyService.class, "primary");
// By type + annotation qualifier
MyService svc = beanManager.getBean(MyService.class, myQualifier);
// Check existence
boolean exists = beanManager.containsBean(MyService.class);Minimal jakarta.enterprise.inject.spi.CDI<Object> implementation. Returned by CDI.current() after the module sets the provider:
CDI<Object> cdi = CDI.current();
MyService svc = cdi.select(MyService.class).get();The Guice module that wires everything together. Loaded automatically via IGuiceModule SPI — sort order Integer.MAX_VALUE - 200 (loads after most modules).
com.guicedee.cdi
├── com.guicedee.client (SPI contracts — IGuiceModule, IGuiceContext)
├── jakarta.cdi (Jakarta CDI API — BeanManager, CDI, CDIProvider)
└── jakarta.el (Expression Language — required by BeanManager interface)
Issues and pull requests are welcome — please add tests for new bridging behaviour or annotation support.