© Tayo Koleoso 2020
T. KoleosoBeginning Quarkus Frameworkhttps://doi.org/10.1007/978-1-4842-6032-6_2

2. Dependency Injection

Tayo Koleoso1 
(1)
Silver Spring, MD, USA
 

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.

Dependency injection (DI) is the mechanism by which java objects are made available at specific points in your code by the runtime. It’s an implementation of the inversion of control (IoC) design pattern that makes it so that application code can be loosely coupled. So instead of non-DI code:
MyDependency myDependency = new MyDependency();
//some business logic
myDependency.cleanUp();
myDependency = null;
you can have DI code:
@Inject
MyDependency myDependency;
The @Inject annotation indicates to the runtime that an instance of MyDependency needs to be available, stat! You don’t need to worry about how the object is instantiated, destroyed, or anything in between. There’s quite a bit more to the topic and the mechanism, but that’s out of the scope of this book. Suffice it to say that behind the scenes, a DI runtime provides a service to you: providing you objects wherever you need them. There are a few DI runtimes, and these are the top three in the Java ecosystem:
  • Contexts and Dependency Injection

  • Spring Framework (obviously)

  • Google Guice
    ../images/493703_1_En_2_Chapter/493703_1_En_2_Fig1_HTML.jpg
    Figure 2-1

    Pictured: Dependency Injection

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

Before we get into the meat of things with Quarkus, you should get a fundamental understanding of the DI framework on which everything is built. This isn’t going to be a deep dive into CDI. I’m going to show you only just enough CDI for you to have a good time building Quarkus applications. Hope you’ve brought an appetite, because we’re about to get into some beans!
@ApplicationScoped (1)
@Named("deliciousVanilla") (2)
public class VanillaBean implements FlavorTown{
    long flavorStrength;
    String countryOfOrigin;
    //other "stuff"
}
This is a “vanilla” CDI bean with
  1. 1.

    A scope: “Application” scope here

     
  2. 2.

    A name: “deliciousVanilla”

     
And that’s really all it takes for a class to be known to CDI as a bean, in Quarkus.2 Give it a scope or a name – with the @Named annotation . You can then use this bean in another bean like so:
@RequestScoped (1)
public class VanillaCake{
    @Inject (2)
    @Named("deliciousVanilla")
    VanillaBean vanillaBean;
    @Inject
    FlavorTown aFlavor; (3)
}
Here’s what’s going on here:
  1. 1.

    I’m using a different scope here, a narrower one – the request scope.

     
  2. 2.

    Then I inject my VanillaBean into this class, using the bean’s name as a qualifier.

     
  3. 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.

     
Note that @Named is optional – it’s just a qualifier you can do without in many situations. Also notice how I don’t have any getters or setters in there, against the JavaBean convention? That’s because it’s not necessary for injection in CDI. @Inject can also supply instances to methods:
@Inject
public void gimmeSomeFlavor(FlavorTown flavor){
    ...
}

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

All you need to know about bean scopes is that they determine how long your bean will live. They also determine how many instances of your bean will be available at any given time. For the purposes of “Quarkusing,” here are the scopes that matter in increasing order of lifespan:
  1. 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. 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. 3.

    @ApplicationScoped: A bean with this scope will be maintained for the entire uptime of the application. There can only be one.

     
  4. 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

A CDI producer method lets you take control of the process of instantiating a CDI bean. Check this out:
@Produces
@Named("boldVanilla")
@RequestScoped
public VanillaBean vanillaBean(){
    VanillaBean vanillaBean = new VanillaBean();
    vanillaBean.setFlavorStrength(30);
    vanillaBean.setCountryOfOrigin("Madagascar");
return vanillaBean;
}
With that method defined in any class, managed bean or not, the following injection will obtain its bean instance from my producer method. The optional @Named qualifier and @RequestScoped I’ve supplied there will also kick in at the injection site. With those two annotations, I’ve defined the default name and scope of the VanillaBean. I can override the name the bean at the point of injection, as well as set my desired scope before injection:
@Inject
@Named("boldVanilla") @ApplicationScoped
VanillaBean vanillaBean;

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.

The “destructive” counterpart to a producer method is the disposer method :
public void compostVanillaBean(@Disposes VanillaBean vanillaBean){
    Composter composter = new Composter();
    composter.compost(vanillaBean);
}

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.

Caution

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.

You can also get information about the destination where the produced bean is to be injected, with javax.enterprise.inject.spi.InjectionPoint:
@Produces
@Named("selectiveVanilla")
@RequestScoped
public VanillaBean vanillaBean(InjectionPoint injectionPoint){
    ...
    injectionPoint.getType().getTypeName(); //the name of the injection class
    injectionPoint.getMembers() //other fields in the injection class
    injectionPoint.getQualifiers()//qualifier annotations on the injection site
...
}

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

CDI provides qualifiers: custom annotations you can apply to your managed beans that further help to distinguish them from other beans. So, given these two beans
@ApplicationScoped
public class VanillaBean implements FlavorTown{
  ...
}
@ApplicationScoped
public class BeaverVanilla implements FlavorTown{
  ...
}
the following injection will fail:
@Inject
FlavorTown beaverVanilla;
You will get an error message like
Ambiguous dependencies for type [FlavorTown] with qualifiers [@Default] at injection point
This happens because the ArC CDI engine wouldn’t know which implementation of FlavorTown it needs to deliver. I need to create a qualifier that further distinguishes the class that I’m interested in injecting:
@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface Rare{}

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”:

@ApplicationScoped
@Rare
public class BeaverVanilla implements FlavorTown{
  ...
}
I’ve now designated BeaverVanilla4 as a special implementation, by applying my @Rare qualifier:
@Inject
@Rare
FlavorTown beaverVanilla;

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

The CDI spec mandates a file named “beans.xml” as the configuration file for establishing CDI functionality in a Java application. Making this file available within a project, even without configuration, is what kick-starts the CDI runtime. It’s this file that will cause the classpath to be scanned and all the managed bean annotations picked up. Here’s what it looks:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd" bean-discovery-mode="all" version="2.0">
</beans>

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

CDI provides some aspect-oriented programming to support implementing cross-cutting concerns. Thankfully, CDI dispenses with the fancy AOP lingo, and it’s all boiled down to two components:
  • Interceptor binding

  • Interceptor

Now stay with me: I’ll start with the interceptor binding. The interceptor binding is the annotation I’ll use to mark methods that you want to apply cross-cutting concerns to:
@Inherited (1)
@InterceptorBinding (2)
@Retention(RUNTIME)
@Target({METHOD, TYPE})
public @interface Audited{
}
  1. 1.

    @Inherited makes it so that this annotation can be inherited by child classes.

     
  2. 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.

     
I have the annotation, now for the class that will execute the cross-cutting concern – the Interceptor [lightning and thunder sound effects]:
@Audited (1)
@Interceptor (2)
@Priority(Interceptor.Priority.APPLICATION)(3)
public class AuditingInterceptor implements Serializable {
    ...
    @AroundInvoke (4)
    public Object logExecutions(InvocationContext invocationContext) throws Exception {
        logger.info("Method: "+ invocationContext.getMethod().getName()); (5)
        logger.info("Arguments: "+invocationContext.getParameters());
        logger.info("Executing the called method");
        Object possibleReturn = invocationContext.proceed(); (6)
        logger.info("The object the method was invoked on: "+invocationContext.getTarget());
        return possibleReturn;
    }
}
Alrighty then, let’s dig in:
  1. 1.

    I apply my previously created @Audited interceptor binding. This is how this annotation will become effective as an AOP annotation.

     
  2. 2.

    @Interceptor marks this class as an interceptor.

     
  3. 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. 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. 5.

    I get the javax.interceptor.InvocationContext which gives me information about the intercepted method.

     
  6. 6.

    I must call proceed on the InvocationContext , to ensure that the intercepted method gets invoked.

     
All I need to do now is apply my annotation wherever I want the auditing:
@ApplicationScoped
public class Generator{
        ....
       @Audited
       public String generateAnagram(String source){
           logger.info("Generatin'");
           ...
       }
}

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

Now, I’m showing this only for completeness of information. I’m not a big fan of tight coupling with frameworks – JavaEE standards exist for a reason,5 and we live in a society, dang it! You can directly access the ArC (and by extension, CDI) context from a Quarkus extension or your managed bean using io.quarkus.arc.ArcContainer  – from within a managed bean
ArcContainer arcContainer = Arc.container();

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.

As I’ve talked about, there are three main avenues to inject or obtain resources:
  • @Inject

  • @Context

  • Producer methods and fields

Generally, you’ll use @Inject to supply objects of your own custom classes. You need the @Context annotation to deliver some specialized, container-managed objects that you’re not in control of. Go check out the latest version of the CDI specification to see a complete list of specialized objects that are available to you, courtesy of the CDI runtime. To be safe, I recommend adding the quarkus-undertow extension to your microservice:
mvn quarkus:add-extension -Dextension=quarkus-undertow
This increases the variety of container-managed components you can inject; Undertow brings with it several servlet-related components like org.jboss.resteasy.spi.HttpRequest and javax.servlet.ServletContext, among others:
@Inject
ServletContext servletContext;
@Inject
HttpRequest httpRequest;

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.

Heads Up

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
All the bean initializations are lazy by default. So, given an @ApplicationScoped bean like this
@ApplicationScoped
public class StartupConfigCheck {
    Logger logger = Logger.getLogger(StartupConfigCheck.class.getName());
    @PostConstruct
    public void startupOperations(){
        //heavy lifting startup business logic
    }
    @PreDestroy
    public void shutdownOperations(){
        //here goes your shutdown business logic
    }
}

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.

Tip

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
You get the io.quarkus.runtime.StartupEvent and io.quarkus.runtime.ShutdownEvent classes to hook into the ArC runtime’s startup and shutdown steps, respectively. Here’s what that looks like:
@ApplicationScoped
public class StartupConfigCheck {
    ...
    public void startupOperations(@Observes StartupEvent startup){
        //here goes your startup business logic
    }
    public void shutdownOperations(@Observes ShutdownEvent startup){
        //here goes your shutdown business logic
    }
}

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
What’s a @DefaultBean? It’s an annotation that lets you set a bean to be a default option, in case of injections. It’s a little weird to explain, easier to show. Here’s a producer method:
    @Produces
    @RequestScoped
    @Named("simpleBatchProcessWorker")
    @DefaultBean
    public BatchProcessWorker customComponentProvider(){
        return new SimpleBatchProcessWorker();
    }

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
Use io.quarkus.arc.profile.IfBuildProfile and io.quarkus.arc.DefaultBean to programmatically control when a bean is loaded. Check this out:
public class BeanSupplier{
    @Produces
    @IfBuildProfile("test-east")(1)
    public BatchProcessWorker eastRegionCustomComponentProvider(){
        return EastOnlyBatchProcessWorker();
    }
    @Produces
    @UnlesBuildProfile("prod")
    public BatchProcessWorker testingOnlyBean(){
        ...
    }
    @Produces
    @DefaultBean (2)
    public BatchProcessWorker customComponentProvider(){
        return new SimpleBatchProcessWorker();
    }
}
Here’s the voodoo going on:
  1. 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. 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.

Tip

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

If you’re new to CDI, just skip this bit. You don’t know what you’re missing; and you’ll find that you don’t need those things anyway. If you’re already a CDI veteran, know ye this: ArC can do most things as stipulated by JSR-365, but there are some things that it doesn’t do, mostly by design. Remember, Quarkus is in the business of speed and predictability; some CDI features just don’t jive with that:
  • 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.

Don’t get me wrong, the Spring Framework is an awesome project; but, if you want to try Quarkus out and still want to leverage the investments you’ve made in Spring, this section is for you. This section is about showing where Quarkus integrates elements of the Spring Framework. There are features that are already easily reproducible with ArC. This bit is so that you can drop in your existing Spring-based beans and configuration into the Quarkus world. But really, check out this gorgeous fella:
../images/493703_1_En_2_Chapter/493703_1_En_2_Fig2_HTML.jpg
Figure 2-2

“Meow” – This handsome camel,7 probably

Wait, what? What do you mean that’s not a camel?
../images/493703_1_En_2_Chapter/493703_1_En_2_Fig3_HTML.jpg
Figure 2-3

Camel

Oh. Well that’s just cr-

Quarkus Spring Annotation Support

Quarkus provides support for annotations from the following Spring projects. What this means is that you can bring in most of the annotations from these projects and more, and they’ll behave the way you expect them to. It’s important to understand that under the hood, it’s Quarkus that’s providing the services of these annotations, not the Spring framework. These are some of the Spring modules supported. Visit the Quarkus website for an up to date list of supported modules - they’re always adding more:
  • Spring Core

  • Spring Web

  • Spring Security

  • Spring Data JPA

  • Spring Boot Properties

I’ll add the core Spring Framework support extensions to my Quarkus app:
mvn quarkus:add-extension -Dextension=spring-di
Pro Tip

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

“Quarkus provides support for annotations” is an intentional statement: Quarkus will recognize the documented Spring annotations and interfaces. Then using ArC, it can provide the required services and functionality. What this means is that you can bring the following bean into a Quarkus project:
@Controller
@RestController
@RequestMapping
public class SpringController{
    @Autowired
    private AnotherSpringBean linkedBean;
    @Autowired
    ValueObjectRepository<ValueObject, Long> springJpa;
}
and Quarkus + ArC will provide the required REST endpoint, Spring Boot JPA repository and injections, like nothing’s changed. Your Spring configuration bean can also just be transplanted:
@Configuration
public class AppConfiguration {
    @Bean(name = "anotherSpringBean")
    public AnotherSpringBean anotherSpringBean() {
        return new AnotherSpringBean();
    }
}

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.

As you’ll see later chapters in this book, Quarkus has its own Jakarta EE and MicroProfile-based support for web services, security, data access, configuration, and so much more. The support for these annotations in Quarkus is partly an avenue to encourage and support transition of existing Spring applications into Quarkus. That bears repeating: Quarkus is cool with your precious Spring Framework annotations, but it doesn’t need them. So, you won’t be able to use org.springframework.context.ApplicationContextAware to key into the Spring context; nor will you be able to process Spring context events. But you get to combine two very powerful sets of annotations! Think about it: Spring and CDI beans combined and seen as one! Check it out, it’s awesome:
@Named("aCdiBean")
@ApplicationScoped
public class CdiDao implements AnagramDao{
    @Autowired
    AnotherSpringBean springBean;
     ...
}
Given a Spring-annotated AnotherSpringBean, I can inject it into a CDI-annotated CdiDao . I can also put a CDI-annotated bean into the Spring-annotated one:
@Controller
@RestController
@RequestMapping
public class SpringController{
    @Autowired
    @Qualifier("aCdiBean")
    AnagramDao aCdiBean;
    ...
}

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

And again, not a fan of too close a coupling to framework code, but there are CDI-based approaches to Spring functionality. The most common direct developer use of org.springframework.context.ApplicationContextAware is to access the Spring ApplicationContext. Many ApplicationContext features are reproducible in CDI. The fundamental use of the ApplicationContext is to directly access Spring beans. Here’s how you get a hold of a specific bean in CDI:
@Inject
BeanManager beanManager; (1)
...
public void getBean(){
    Bean namedCdiBean = null; (2)
    HashSet<Bean<?>> beans = (HashSet<Bean<?>>) beanManager.getBeans("aSpringOrCdiBean"); (3)
    if(!beans.isEmpty()){
       namedCdiBean = beans.iterator().next(); (4)
          CreationalContext creationalContext = beanManager.createCreationalContext(namedCdiBean); (5)
          ASpringOrCdiBean bean= (ASpringOrCdiBean) beanManager.getReference(namedCdiBean,namedCdiBean.getBeanClass(),creationalContext); (6)
          }
    }
}
It’s a fair bit more typing than what you’re probably used to, if you’re coming from Spring. If you’re new to this, stay with me and let’s walk through the steps:
  1. 1.

    I obtain an injection of the BeanManager object . There’s only one, just like the ApplicationContext in the Spring Framework.

     
  2. 2.

    Before I can get a hold of a named bean, I start with the generic Bean interface.

     
  3. 3.

    I then use the BeanManager to search for and retrieve the CDI bean by name.

     
  4. 4.

    If the search is successful, I simply pick up the first and only item in the set.

     
  5. 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. 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

Well, I need the event data to send first, don’t I?
public class InterestingEvent{
    String eventMessage;
    ...
}
And now, to fire the event. The simplest way to fire an event is with the trusty ol’ BeanManager:
@Inject
BeanManager beanManager;
...
public void sendInterestingEvent(String message){
    InterestingEvent interestingEvent = new InterestingEvent();
    beanManager.fireEvent(interestingEvent,null);
}
What I’ve done here is shoot off an InterestingEvent into the CDI runtime. The event will be received by observers that have registered interest in InterestingEvents. What’s that null bit? You noticed! I can optionally supply a list of CDI qualifiers (remember those?), to further narrow the list of candidate observers. How about an asynchronous event firing?
public void sendInterestingEvent(String message){
    ...
    CompletionStage completionStage = beanManager.getEvent().fireAsync(interestingEvent);
}

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

With the event firing taken care of, now I just need to be paying attention with a CDI Observer:
public void listenForNews(@Observes InterestingEvent event (1)){
       logger.info("Got some interesting news: "+event.getEventMessage();
}
public void listenForGossip(@Observes @Gossip InterestingEvent event (2)){
       logger.info("Got some interesting gossip: "+event.getEventMessage();
}
public void listenForGossipAsync(@ObservesAsync @Gossip InterestingEvent event (3)){
       logger.info("Got some interesting gossip: "+event.getEventMessage();
}
  1. 1.

    Here I use @javax.enterprise.event.Observes to listen for events of the InterestingEvent type.

     
  2. 2.

    I’m interested in InterestingEvents only if they were fired with the @Gossip qualifier.

     
  3. 3.

    I can also listen asynchronously for @Gossip InterestingEvents.

     

So, bring your Spring beans in; let’s give them the Supersonic Subatomic treatment!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.21.246.218