As the old saying goes, “Teach a man to fish, and dependency injection will keep fresh fish coming,”1 dependency injection is the lifeblood of any serious enterprise Java application.
Contexts and Dependency Injection
Spring Framework (obviously)
- Google Guice
Quarkus provides support for the first two DI frameworks. Supersonic Subatomic code that runs like greased lightning doesn’t fully implement all available DI functionality. Full-fledged dependency injection requires a lot of guesswork and usage of the JDK’s Reflection API. All of that gets expensive with a full-fledged DI implementation, so the Quarkus team made smart, intuitive decisions about what to support and what not to support. But, fear not: where Quarkus closes a window, it opens a door. Any functionality that’s had to be jettisoned, more than likely has a suitable replacement elsewhere in the Quarkus extensions ecosystem.
Contexts and Dependency Injection
Contexts and Dependency Injection (CDI) is the standard DI framework for the Java ecosystem, derived largely from Red Hat’s Seam framework. Note that I said framework. CDI is a JavaEE specification, officially called JSR 365. What this means is that Java platform vendors like Red Hat, Google, Spring, and others are free to implement their own interpretation of CDI. This is what the Quarkus team at Red Hat has done in developing ArC, the DI runtime built specifically for use in Quarkus.
Getting Started with CDI
- 1.
A scope: “Application” scope here
- 2.
A name: “deliciousVanilla”
- 1.
I’m using a different scope here, a narrower one – the request scope.
- 2.
Then I inject my VanillaBean into this class, using the bean’s name as a qualifier.
- 3.
I can also just inject the same bean, without specifying a name or any kind of qualifier. Because VanillaBean is annotated with @ApplicationScoped, there’s only ever going to be one instance of it in the entire context. So, aFlavor == vanillaBean will be true.
There’s also the @Context annotation . Use this annotation to obtain instances of certain specialized components from the JavaEE world. There’s a little bit of overlap between @Context and @Inject. There’s currently talk about deprecating @Context in favor of @Inject. For what it’s worth, @Context supplies REST components that @Inject doesn’t currently supply,3 so it still has its uses. If you have bean classes packaged in a JAR, add a file named beans.xml (more on this shortly) to the META-INF directory of the package and CDI will find and supply the beans present in that JAR.
Bean Scopes
- 1.
@Dependent: Beans with this annotation don’t have a lifespan of their own. They last as long as their injection target. For example, inject a @Dependent bean into an @ApplicationScoped bean, then the @Dependent bean lives as long as the @ApplicationScoped bean.
- 2.
@RequestScoped: Beans with this annotation are created anew for every request to your web service. This means that two injections of the same @RequestScoped bean will get two different instances of that same bean.
- 3.
@ApplicationScoped: A bean with this scope will be maintained for the entire uptime of the application. There can only be one.
- 4.
@Singleton: This technically isn’t a scope – it’s a stereotype. In addition to limiting a bean to a single instance within the application, this annotation can get special treatment regarding transactions and locking, for example. For Quarkus’ purposes though, it’s functionally identical to @ApplicationScoped.
There are a couple of other scopes that are recommended in the CDI specification. They’re not here in the hall of fame, because they have no relevance in a non-web application framework like Quarkus. So, suck it, scopes not invited to the party!
Producer and Disposer Methods
Producer methods are particularly useful when you want to supply non-managed beans, using CDI and managed bean components. Say you need to provide a third-party class with some preconfigured variables, you use a producer method.
This method will be called by the CDI runtime whenever instances of VanillaBean need to be destroyed. I get to do my cleanup here before the bean goes kaput.
A disposer method must be matched by a corresponding producer method returning the same class. Without a matching producer method, you will get a deployment exception. A producer method on the other hand doesn’t require a disposer method.
With these and all the other metadata available, you can build conditional logic around what your producer method delivers for injection. A producer method is also a fine option to inject lists of beans into your application. Go nuts!
Quarkus doesn’t require @Produces on producer methods; I just prefer it for completeness and conformity with the CDI specification – and also because I like to type words.
Qualifiers
A qualifier is just any custom annotation that itself is annotated with @Qualifier . With this annotation, I can designate one of my FlavorTown bean classes as “rare”:
This injection will sail right through! The CDI engine will know the specific implementation of FlavorTown I’m interested in injecting. For what it’s worth, CDI supplies a number of implicit qualifiers out of the box, as does Quarkus. For example, @Named is a fine qualifier for many use cases.
Bean Configuration File
Since JavaEE 7 however, this file is no longer necessary for the runtime bootstrap. You now need it only for advanced configuration of managed beans.
Aspect-Oriented Programming
Interceptor binding
Interceptor
- 1.
@Inherited makes it so that this annotation can be inherited by child classes.
- 2.
@InterceptorBinding is what really makes this an interceptor binding. Interceptor bindings can declare interceptor bindings as well. That way, you can combine more cross-cutting concerns in fewer annotations.
- 1.
I apply my previously created @Audited interceptor binding. This is how this annotation will become effective as an AOP annotation.
- 2.
@Interceptor marks this class as an interceptor.
- 3.
@Priority gives this interceptor a numerical rank, relative to other interceptors. The Interceptor.Priority enum contains some presets, but any integer will do.
- 4.
The @AroundInvoke annotation will cause this method to be invoked whenever a method with @Audited is invoked. An interceptor class can have any number of @AroundInvoke methods ; they will all be executed in order of appearance in the class.
- 5.
I get the javax.interceptor.InvocationContext which gives me information about the intercepted method.
- 6.
I must call proceed on the InvocationContext , to ensure that the intercepted method gets invoked.
ArC CDI Engine
In older versions of Quarkus, you would have had to add ArC support to your Quarkus microservice, using the quarkus-arc extension. Now, it’s bundled with the core of the framework.
Getting Started with ArC
This is the ArC way of gaining access to the CDI runtime. It gives you access to the BeanManager, among other core pieces of the CDI runtime. In the CDI world, the BeanManager is the gateway to all the beans and scopes available in the context. You can use it to create, search, and destroy all available beans within the context.
@Inject
@Context
Producer methods and fields
Note how I’m using @Inject to deliver the ServletContext object here; that’s a personal preference that I recommend for you too, in preparation for a probable retirement of the @Context annotation in the future.
Quarkus and its native image generation performs better CDI work without private members. Dependency injection requires a lot of Reflection API usage, which is very expensive. In the interest of generating better-performing native images, don’t use the private modifier for fields. Stick with package-level access for class-level variables.
Quarkus-Only Enhancements to CDI
Quarkus enriches its implementation of CDI in ArC, by adding the following features, among others.
Lazy Bean Initialization
the heavy lifting I’m doing in the @PostConstruct method will not kick in until the first time the bean is injected anywhere in the application. In standard CDI behavior where all the beans are instantiated at startup, your application could pay an unnecessary startup time cost due to long-running initialization operations.
You can still force eager initialization with the io.quarkus.runtime.Startup annotation applied to the bean class. Be sure to not confuse this one for the javax.ejb.Startup annotation that does the same thing, but only for Enterprise JavaBeans (EJB).
Custom Lifecycle Events
The @Observes annotation will be used to deliver these event objects where necessary. Beans that contain StartupEvent and ShutdownEvent listeners will eagerly be instantiated. They trigger this behavior regardless of the scope of the parent CDI bean.
Default Beans
This producer method guarantees that there will always be an instance of BatchProcessWorker available for injection. When there isn’t any available source of BatchProcessWorker anywhere else in the Quarkus code, io.quarkus.arc.DefaultBean will step in and supply the default. If another implementation exists, that implementation takes precedence.
Conditional Bean Supply
- 1.
This producer method will deliver a EastOnlyBatchProcessWorker only if the Quarkus application is deployed with the “test-east” profile active. Conversely, Quarkus provides the @UnlessBuildProfile annotation to exclude a specific profile. With @UnlessBuildProfile("exclude-me"), the annotated bean or producer method will be activated unless the profile named “exclude-me” is active.
- 2.
Without the expected profile being active, the customComponentProvider producer method kicks in and supplies a SimpleBatchProcessWorker as the default bean.
Lean Bean Cleaning Machine
Quarkus removes unused beans from the DI runtime during startup. This leaves the runtime lightweight without any unnecessary beans to track.
Add quarkus.log.category."io.quarkus.arc.processor".level=DEBUG to the application.properties file. This enables debug-level logging specifically for ArC. With this, you can see all the optimizations ArC is doing during startup. A bonus is that you can see all the unused CDI-injectable beans that ArC removes from the runtime. Even more bonus: It shows you what you can inject in the ArC world.
beans.xml
beans.xml is not required to activate dependency injection in Quarkus, contrary to the CDI specification. The only scenario where this file will become necessary is when you package bean classes in an archive like a JAR, and you need those beans available in your Quarkus app. Packaging a beans.xml file in such an archive will expose those beans to ArC and make them injectable.
Limitations of ArC
No @ConversationScoped beans, or “passivation-capable” beans:6 This should mean no beans that implement Serializable or beans that will require serialization to a file. But Quarkus supports the CDI @SessionScoped, a passivation-capable bean. Curious.
- As the contents of beans.xml are ignored,
No CDI portable extensions: The Quarkus extension framework is all the extension framework you’ll need. Trust me, vanilla CDI extensions are overweight and suboptimal for the tier of performance Quarkus aims for.
No beans.xml-based ordering of @Alternative beans; and you don’t need it: Quarkus provides io.quarkus.arc.AlternativePriority, an annotation that designates a bean as an alternative, as well as assigns a numerical priority.
No CDI’s Specialization with @Specializes: But @Specializes is just an alternative that allows inheritance of CDI qualifiers. For a replacement, consider programmatically inheriting the available qualifiers. An alternative approach is to dynamically modify the qualifiers at the injection point, guaranteeing that the desired implementation is injected. This can be achieved with the Quarkus extension framework.
No CDI decorators: You won’t be able to delegate functionality to custom beans using @Decorator and @Delegate.
Spring Framework
Ah yes, the beloved Spring Framework. It’s a behemoth platform that does a lot for the developer and the enterprise; Quarkus on its own is not a replacement for Spring Boot.Yet. That feature parity comes with Apache Camel. If you aren’t familiar with the Apache Camel Framework, I can’t recommend it enough. So much so that, I’ve had colleagues that call me “Mr. Camel”; even going as far as saying I share similarities with them! How cool is that?. Apache Camel opens up a world of options for integrating with many, many systems. With that in your stack, Quarkus could easily rival Spring. The Apache Camel team has even forked off their project into a branch focused on porting Camel extensions for Quarkus support.
Oh. Well that’s just cr-
Quarkus Spring Annotation Support
Spring Core
Spring Web
Spring Security
Spring Data JPA
Spring Boot Properties
Remember, the list-extensions Maven goal will show you all the available extensions in the Quarkus ecosystem; they’re always adding more. add-extension will install the latest version too.
Mixing Beans
That, however, is the extent of the Spring Framework support as at the time of this writing. In Quarkus, there is no Spring ApplicationContext to access, nor are there Spring context-based services available.
Yeah, that’s right. Spring’s @Qualifier is equivalent to CDI’s @Qualifiers, including @Named. What else would you need to bring Spring beans into the CDI world?
Substitute ApplicationContextAware and BeanFactory
- 1.
I obtain an injection of the BeanManager object . There’s only one, just like the ApplicationContext in the Spring Framework.
- 2.
Before I can get a hold of a named bean, I start with the generic Bean interface.
- 3.
I then use the BeanManager to search for and retrieve the CDI bean by name.
- 4.
If the search is successful, I simply pick up the first and only item in the set.
- 5.
Each CDI bean is associated with a context, but here’s where things differ from Spring. In the CDI world, beans and the context are somewhat disconnected. In Spring, the context is tightly coupled with the beans. Way more flexibility in CDI is what I’m saying. For that reason, I need to manufacture a CreationalContext object, to obtain a contextually valid reference to the bean I’m interested in.
- 6.
Finally, I use the BeanManager and the CreationalContext to retrieve the bean from the generic Bean container object.
I’ll admit, it’s more typing than one should want to do, but come on: you really should only have to do this occasionally. @Inject wherever you need your beans. The BeanManager sort of combines the features of Spring’s ApplicationContext and the org.springframework.beans.factory.BeanFactory interface. With the BeanManager however, you’ve got a nice, healthy bowl of mixed Spring and CDI beans. Yum!
Substitute Spring Application Events
Yes, CDIs got you covered here too. There are fancier, more high-performing ways to handle inter-bean messaging in Quarkus, but here’s how you do it with raw CDI.
As with any event-driven design, there are two parts to the operation: firing the event and observing the event. Have a seat; I’ll start with firing the event.
Fire CDI Events
The async event firing gives you a standard JDK CompletionStage that I could optionally use to manage the threading or handle exceptions asynchronously. You’ve probably also noticed the inconsistency in the interface – if there’s a fire method on BeanManager, there ought to be a fireAsync method also. Looking at you, JSR-365 expert group.8
Subscribe to CDI Events
- 1.
Here I use @javax.enterprise.event.Observes to listen for events of the InterestingEvent type.
- 2.
I’m interested in InterestingEvents only if they were fired with the @Gossip qualifier.
- 3.
I can also listen asynchronously for @Gossip InterestingEvents.
So, bring your Spring beans in; let’s give them the Supersonic Subatomic treatment!