All the classes defined in a Spring Boot application are managed beans by default

5.1. Eager Instantiation of Beans

5.1.1. Lazy By Default

By default, CDI beans are created lazily, when needed. What exactly "needed" means depends on the scope of a bean.

  • A normal scoped bean (@ApplicationScoped, @RequestScoped, etc.) is needed when a method is invoked upon an injected instance (contextual reference per the specification).

    In other words, injecting a normal scoped bean will not suffice because a client proxy is injected instead of a contextual instance of the bean.

  • A bean with a pseudo-scope (@Dependent and @Singleton ) is created when injected.

Lazy Instantiation Example

@Singleton // => pseudo-scope class AmazingService { String ping() { return "amazing"; } } @ApplicationScoped // => normal scope class CoolService { String ping() { return "cool"; } } @Path("/ping") public class PingResource { @Inject AmazingService s1; (1) @Inject CoolService s2; (2) @GET public String ping() { return s1.ping() + s2.ping(); (3) } }

1 Injection triggers the instantiation of AmazingService.
2 Injection itself does not result in the instantiation of CoolService. A client proxy is injected.
3 The first invocation upon the injected proxy triggers the instantiation of CoolService.

5.1.2. Startup Event

However, if you really need to instantiate a bean eagerly you can:

  • Declare an observer of the StartupEvent - the scope of the bean does not matter in this case:

    @ApplicationScoped class CoolService { void startup(@Observes StartupEvent event) { (1) } }

    1 A CoolService is created during startup to service the observer method invocation.

  • Use the bean in an observer of the StartupEvent - normal scoped beans must be used as described in Lazy By Default:

    @Dependent class MyBeanStarter { void startup(@Observes StartupEvent event, AmazingService amazing, CoolService cool) { (1) cool.toString(); (2) } }

    1 The AmazingService is created during injection.
    2 The CoolService is a normal scoped bean, so we have to invoke a method upon the injected proxy to force the instantiation.

  • Annotate the bean with @io.quarkus.runtime.Startup as described in Startup annotation:

    @Startup (1) @ApplicationScoped public class EagerAppBean { private final String name; EagerAppBean(NameGenerator generator) { (2) this.name = generator.createName(); } }

    1 For each bean annotated with @Startup a synthetic observer of StartupEvent is generated. The default priority is used.
    2 The bean constructor is called when the application starts and the resulting contextual instance is stored in the application context.

5.2. Request Context Lifecycle

The request context is also active:

  • during notification of a synchronous observer method.

The request context is destroyed:

  • after the observer notification completes for an event, if it was not already active when the notification started.

An event with qualifier @Initialized(RequestScoped.class) is fired when the request context is initialized for an observer notification. Moreover, the events with qualifiers @BeforeDestroyed(RequestScoped.class) and @Destroyed(RequestScoped.class) are fired when the request context is destroyed.

5.3. Qualified Injected Fields

In CDI, if you declare a field injection point you need to use @Inject and optionally a set of qualifiers.

@Inject @ConfigProperty(name = "cool") String coolProperty;

In Quarkus, you can skip the @Inject annotation completely if the injected field declares at least one qualifier.

@ConfigProperty(name = "cool") String coolProperty;

With the notable exception of one special case discussed below, @Inject is still required for constructor and method injection.

5.4. Simplified Constructor Injection

In CDI, a normal scoped bean must always declare a no-args constructor (this constructor is normally generated by the compiler unless you declare any other constructor). However, this requirement complicates constructor injection - you need to provide a dummy no-args constructor to make things work in CDI.

@ApplicationScoped public class MyCoolService { private SimpleProcessor processor; MyCoolService() { // dummy constructor needed } @Inject // constructor injection MyCoolService(SimpleProcessor processor) { this.processor = processor; } }

There is no need to declare dummy constructors for normal scoped bean in Quarkus - they are generated automatically. Also, if there’s only one constructor there is no need for @Inject.

@ApplicationScoped public class MyCoolService { private SimpleProcessor processor; MyCoolService(SimpleProcessor processor) { this.processor = processor; } }

We don’t generate a no-args constructor automatically if a bean class extends a class that does not declare a no-args constructor.

5.5. Removing Unused Beans

The container attempts to remove all unused beans, interceptors and decorators during build by default. This optimization helps to minimize the amount of generated classes, thus conserving memory. However, Quarkus can’t detect the programmatic lookup performed via the CDI.current() static method. Therefore, it is possible that a removal results in a false positive error, i.e. a bean is removed although it’s actually used. In such cases, you’ll notice a big warning in the log. Users and extension authors have several options how to eliminate false positives.

The optimization can be disabled by setting quarkus.arc.remove-unused-beans to none or false. Quarkus also provides a middle ground where application beans are never removed whether or not they are unused, while the optimization proceeds normally for non application classes. To use this mode, set quarkus.arc.remove-unused-beans to fwk or framework.

5.5.1. What’s Removed?

Quarkus first identifies so-called unremovable beans that form the roots in the dependency tree. A good example is a JAX-RS resource class or a bean which declares a @Scheduled method.

An unremovable bean:

  • is excluded from removal by an extension, or

  • has a name designated via @Named, or

  • declares an observer method.

An unused bean:

  • is not unremovable, and

  • is not eligible for injection to any injection point in the dependency tree, and

  • does not declare any producer which is eligible for injection to any injection point in the dependency tree, and

  • is not eligible for injection into any javax.enterprise.inject.Instance or javax.inject.Provider injection point.

Unused interceptors and decorators are not associated with any bean.

When using the dev mode (running ./mvnw clean compile quarkus:dev), you can see more information about which beans are being removed:

  1. In the console - just enable the DEBUG level in your application.properties, i.e. quarkus.log.category."io.quarkus.arc.processor".level=DEBUG

  2. In the relevant Dev UI page

5.5.2. How To Eliminate False Positives

Users can instruct the container to not remove any of their specific beans (even if they satisfy all the rules specified above) by annotating them with @io.quarkus.arc.Unremovable. This annotation can be declared on a class, a producer method or field.

Since this is not always possible, there is an option to achieve the same via application.properties. The quarkus.arc.unremovable-types property accepts a list of string values that are used to match beans based on their name or package.

Table 2. Value Examples

Value

Description

org.acme.Foo

Match the fully qualified name of the bean class

org.acme.*

Match beans where the package of the bean class is org.acme

org.acme.**

Match beans where the package of the bean class starts with org.acme

Bar

Match the simple name of the bean class

Example application.properties

quarkus.arc.unremovable-types=org.acme.Foo,org.acme.*,Bar

Furthermore, extensions can eliminate false positives by producing an UnremovableBeanBuildItem.

5.6. Default Beans

Quarkus adds a capability that CDI currently does not support which is to conditionally declare a bean if no other bean with equal types and qualifiers was declared by any available means (bean class, producer, synthetic bean, …​) This is done using the @io.quarkus.arc.DefaultBean annotation and is best explained with an example.

Say there is a Quarkus extension that among other things declares a few CDI beans like the following code does:

@Dependent public class TracerConfiguration { @Produces public Tracer tracer(Reporter reporter, Configuration configuration) { return new Tracer(reporter, configuration); } @Produces @DefaultBean public Configuration configuration() { // create a Configuration } @Produces @DefaultBean public Reporter reporter(){ // create a Reporter } }

The idea is that the extension autoconfigures things for the user, eliminating a lot of boilerplate - we can just @Inject a Tracer wherever it is needed. Now imagine that in our application we would like to utilize the configured Tracer, but we need to customize it a little, for example by providing a custom Reporter. The only thing that would be needed in our application would be something like the following:

@Dependent public class CustomTracerConfiguration { @Produces public Reporter reporter(){ // create a custom Reporter } }

@DefaultBean allows extensions (or any other code for that matter) to provide defaults while backing off if beans of that type are supplied in any way Quarkus supports.

5.7. Enabling Beans for Quarkus Build Profile

Quarkus adds a capability that CDI currently does not support which is to conditionally enable a bean when a Quarkus build time profile is enabled, via the @io.quarkus.arc.profile.IfBuildProfile and @io.quarkus.arc.profile.UnlessBuildProfile annotations. When used in conjunction with @io.quarkus.arc.DefaultBean, these annotations allow for the creation of different bean configurations for different build profiles.

Imagine for instance that an application contains a bean named Tracer, which needs to do nothing when in tests or in dev mode, but works in its normal capacity for the production artifact. An elegant way to create such beans is the following:

@Dependent public class TracerConfiguration { @Produces @IfBuildProfile("prod") public Tracer realTracer(Reporter reporter, Configuration configuration) { return new RealTracer(reporter, configuration); } @Produces @DefaultBean public Tracer noopTracer() { return new NoopTracer(); } }

If instead, it is required that the Tracer bean also works in dev mode and only default to doing nothing for tests, then @UnlessBuildProfile would be ideal. The code would look like:

@Dependent public class TracerConfiguration { @Produces @UnlessBuildProfile("test") // this will be enabled for both prod and dev build time profiles public Tracer realTracer(Reporter reporter, Configuration configuration) { return new RealTracer(reporter, configuration); } @Produces @DefaultBean public Tracer noopTracer() { return new NoopTracer(); } }

The runtime profile has absolutely no effect on the bean resolution using @IfBuildProfile and @UnlessBuildProfile.

5.8. Enabling Beans for Quarkus Build Properties

Quarkus adds a capability that CDI currently does not support which is to conditionally enable a bean when a Quarkus build time property has/has not a specific value, via the @io.quarkus.arc.properties.IfBuildProperty and @io.quarkus.arc.properties.UnlessBuildProperty annotations. When used in conjunction with @io.quarkus.arc.DefaultBean, this annotation allow for the creation of different bean configurations for different build properties.

The scenario we mentioned above with Tracer could also be implemented in the following way:

@Dependent public class TracerConfiguration { @Produces @IfBuildProperty(name = "some.tracer.enabled", stringValue = "true") public Tracer realTracer(Reporter reporter, Configuration configuration) { return new RealTracer(reporter, configuration); } @Produces @DefaultBean public Tracer noopTracer() { return new NoopTracer(); } }

@IfBuildProperty and @UnlessBuildProperty are repeatable annotations, i.e. a bean will only be enabled if all the conditions defined by these annotations are satisfied.

If instead, it is required that the RealTracer bean is only used if the some.tracer.enabled property is not false, then @UnlessBuildProperty would be ideal. The code would look like:

@Dependent public class TracerConfiguration { @Produces @UnlessBuildProperty(name = "some.tracer.enabled", stringValue = "false") public Tracer realTracer(Reporter reporter, Configuration configuration) { return new RealTracer(reporter, configuration); } @Produces @DefaultBean public Tracer noopTracer() { return new NoopTracer(); } }

Properties set at runtime have absolutely no effect on the bean resolution using @IfBuildProperty.

5.9. Declaring Selected Alternatives

In CDI, an alternative bean may be selected either globally for an application by means of @Priority, or for a bean archive using a beans.xml descriptor. Quarkus has a simplified bean discovery and the content of beans.xml is ignored.

The disadvantage of @javax.annotation.Priority is that it has @Target({ TYPE, PARAMETER }) and so it cannot be used for producer methods and fields. This problem should be fixed in Common Annotations 2.1. Users are encouraged to use @io.quarkus.arc.Priority instead, until Quarkus upgrades to this version of jakarta.annotation-api.

However, it is also possible to select alternatives for an application using the unified configuration. The quarkus.arc.selected-alternatives property accepts a list of string values that are used to match alternative beans. If any value matches then the priority of Integer#MAX_VALUE is used for the relevant bean. The priority declared via @Priority or @AlternativePriority is overridden.

Table 3. Value Examples

Value

Description

org.acme.Foo

Match the fully qualified name of the bean class or the bean class of the bean that declares the producer

org.acme.*

Match beans where the package of the bean class is org.acme

org.acme.**

Match beans where the package of the bean class starts with org.acme

Bar

Match the simple name of the bean class or the bean class of the bean that declares the producer

Example application.properties

quarkus.arc.selected-alternatives=org.acme.Foo,org.acme.*,Bar

5.10. Simplified Producer Method Declaration

In CDI, a producer method must be always annotated with @Produces.

class Producers { @Inject @ConfigProperty(name = "cool") String coolProperty; @Produces @ApplicationScoped MyService produceService() { return new MyService(coolProperty); } }

In Quarkus, you can skip the @Produces annotation completely if the producer method is annotated with a scope annotation, a stereotype or a qualifier.

class Producers { @ConfigProperty(name = "cool") String coolProperty; @ApplicationScoped MyService produceService() { return new MyService(coolProperty); } }

5.11. Interception of Static Methods

The Interceptors specification is clear that around-invoke methods must not be declared static. However, this restriction was driven mostly by technical limitations. And since Quarkus is a build-time oriented stack that allows for additional class transformations, those limitations don’t apply anymore. It’s possible to annotate a non-private static method with an interceptor binding:

class Services { @Logged (1) static BigDecimal computePrice(long amount) { (2) BigDecimal price; // Perform computations... return price; } }

1 Logged is an interceptor binding.
2 Each method invocation is intercepted if there is an interceptor associated with Logged.

5.11.1. Limitations

  • Only method-level bindings are considered for backward compatibility reasons (otherwise static methods of bean classes that declare class-level bindings would be suddenly intercepted)

  • Private static methods are never intercepted

  • InvocationContext#getTarget() returns null for obvious reasons; therefore not all existing interceptors may behave correctly when intercepting static methods

    Interceptors can use InvocationContext.getMethod() to detect static methods and adjust the behavior accordingly.

5.12. Ability to handle 'final' classes and methods

In normal CDI, classes that are marked as final and / or have final methods are not eligible for proxy creation, which in turn means that interceptors and normal scoped beans don’t work properly. This situation is very common when trying to use CDI with alternative JVM languages like Kotlin where classes and methods are final by default.

Quarkus however, can overcome these limitations when quarkus.arc.transform-unproxyable-classes is set to true (which is the default value).

5.13. Container-managed Concurrency

There is no standard concurrency control mechanism for CDI beans. Nevertheless, a bean instance can be shared and accessed concurrently from multiple threads. In that case it should be thread-safe. You can use standard Java constructs (volatile, synchronized, ReadWriteLock, etc.) or let the container control the concurrent access. Quarkus provides @io.quarkus.arc.Lock and a built-in interceptor for this interceptor binding. Each interceptor instance associated with a contextual instance of an intercepted bean holds a separate ReadWriteLock with non-fair ordering policy.

io.quarkus.arc.Lock is a regular interceptor binding and as such can be used for any bean with any scope. However, it is especially useful for "shared" scopes, e.g. @Singleton and @ApplicationScoped.

Container-managed Concurrency Example

import io.quarkus.arc.Lock; @Lock (1) @ApplicationScoped class SharedService { void addAmount(BigDecimal amount) { // ...changes some internal state of the bean } @Lock(value = Lock.Type.READ, time = 1, unit = TimeUnit.SECONDS) (2) (3) BigDecimal getAmount() { // ...it is safe to read the value concurrently } }

1 @Lock (which maps to @Lock(Lock.Type.WRITE)) declared on the class instructs the container to lock the bean instance for any invocation of any business method, i.e. the client has "exclusive access" and no concurrent invocations will be allowed.
2 @Lock(Lock.Type.READ) overrides the value specified at class level. It means that any number of clients can invoke the method concurrently, unless the bean instance is locked by @Lock(Lock.Type.WRITE).
3 You can also specify the "wait time". If it’s not possible to acquire the lock in the given time a LockException is thrown.

5.14. Repeatable interceptor bindings

Quarkus has limited support for @Repeatable interceptor binding annotations.

When binding an interceptor to a component, you can declare multiple @Repeatable annotations on methods. Repeatable interceptor bindings declared on classes and stereotypes are not supported, because there are some open questions around interactions with the Interceptors specification. This might be added in the future.

As an example, suppose we have an interceptor that clears a cache. The corresponding interceptor binding would be called @CacheInvalidateAll and would be declared as @Repeatable. If we wanted to clear two caches at the same time, we would add @CacheInvalidateAll twice:

@ApplicationScoped class CachingService { @CacheInvalidateAll(cacheName = "foo") @CacheInvalidateAll(cacheName = "bar") void heavyComputation() { // ... // some computation that updates a lot of data // and requires 2 caches to be invalidated // ... } }

This is how interceptors are used. What about creating an interceptor?

When declaring interceptor bindings of an interceptor, you can add multiple @Repeatable annotations to the interceptor class as usual. This is useless when the annotation members are @Nonbinding, as would be the case for the @Cached annotation, but is important otherwise.

For example, suppose we have an interceptor that can automatically log method invocations to certain targets. The interceptor binding annotation @Logged would have a member called target, which specifies where to store the log. Our implementation could be restricted to console logging and file logging:

@Interceptor @Logged(target = "console") @Logged(target = "file") class NaiveLoggingInterceptor { // ... }

Other interceptors could be provided to log method invocations to different targets.

5.15. Caching the Result of Programmatic Lookup

In certain situations, it is practical to obtain a bean instance programmatically via an injected javax.enterprise.inject.Instance and Instance.get(). However, according to the specification the get() method must identify the matching bean and obtain a contextual reference. As a consequence, a new instance of a @Dependent bean is returned from each invocation of get(). Moreover, this instance is a dependent object of the injected Instance. This behavior is well-defined, but it may lead to unexpected errors and memory leaks. Therefore, Quarkus comes with the io.quarkus.arc.WithCaching annotation. An injected Instance annotated with this annotation will cache the result of the Instance#get() operation. The result is computed on the first call and the same value is returned for all subsequent calls, even for @Dependent beans.

class Producer { AtomicLong nextLong = new AtomicLong(); AtomicInteger nextInt = new AtomicInteger(); @Dependent @Produces Integer produceInt() { return nextInt.incrementAndGet(); } @Dependent @Produces Long produceLong() { return nextLong.incrementAndGet(); } } class Consumer { @Inject Instance<Long> longInstance; @Inject @WithCaching Instance<Integer> intInstance; // this method should always return true // Producer#produceInt() is only called once boolean pingInt() { return intInstance.get().equals(intInstance.get()); } // this method should always return false // Producer#produceLong() is called twice per each pingLong() invocation boolean pingLong() { return longInstance.get().equals(longInstance.get()); } }

It is also possible to clear the cached value via io.quarkus.arc.InjectableInstance.clearCache(). In this case, you’ll need to inject the Quarkus-specific io.quarkus.arc.InjectableInstance instead of javax.enterprise.inject.Instance.

5.16. Declaratively Choose Beans That Can Be Obtained by Programmatic Lookup

It is sometimes useful to narrow down the set of beans that can be obtained by programmatic lookup via javax.enterprise.inject.Instance. Typically, a user needs to choose the appropriate implementation of an interface based on a runtime configuration property.

Imagine that we have two beans implementing the interface org.acme.Service. You can’t inject the org.acme.Service directly unless your implementations declare a CDI qualifier. However, you can inject the Instance<Service> instead, then iterate over all implementations and choose the correct one manually. Alternatively, you can use the @LookupIfProperty and @LookupUnlessProperty annotations. @LookupIfProperty indicates that a bean should only be obtained if a runtime configuration property matches the provided value. @LookupUnlessProperty, on the other hand, indicates that a bean should only be obtained if a runtime configuration property does not match the provided value.

@LookupIfProperty Example

interface Service { String name(); } @LookupIfProperty(name = "service.foo.enabled", stringValue = "true") @ApplicationScoped class ServiceFoo implements Service { public String name() { return "foo"; } } @ApplicationScoped class ServiceBar implements Service { public String name() { return "bar"; } } @ApplicationScoped class Client { @Inject Instance<Service> service; void printServiceName() { // This will print "bar" if the property "service.foo.enabled" is NOT set to "true" // If "service.foo.enabled" is set to "true" then service.get() would result in an AmbiguousResolutionException System.out.println(service.get().name()); } }

5.17. Injecting Multiple Bean Instances Intuitively

In CDI, it’s possible to inject multiple bean instances (aka contextual references) via the javax.enterprise.inject.Instance which implements java.lang.Iterable. However, it’s not exactly intuitive. Therefore, a new way was introduced in Quarkus - you can inject a java.util.List annotated with the io.quarkus.arc.All qualifier. The type of elements in the list is used as the required type when performing the lookup.

@ApplicationScoped public class Processor { @Inject @All List<Service> services; (1) (2) }

1 The injected instance is an immutable list of the contextual references of the disambiguated beans.
2 For this injection point the required type is Service and no additional qualifiers are declared.

The list is sorted by priority as defined by io.quarkus.arc.InjectableBean#getPriority(). Higher priority goes first. In general, the @javax.annotation.Priority and @io.quarkus.arc.Priority annotations can be used to assign the priority to a class bean, producer method or producer field.

If an injection point declares no other qualifier than @All then @Any is used, i.e. the behavior is equivalent to @Inject @Any Instance<Service>.

You can also inject a list of bean instances wrapped in io.quarkus.arc.InstanceHandle. This can be useful if you need to inspect the related bean metadata.

@ApplicationScoped public class Processor { @Inject @All List<InstanceHandle<Service>> services; public void doSomething() { for (InstanceHandle<Service> handle : services) { if (handle.getBean().getScope().equals(Dependent.class)) { handle.get().process(); break; } } } }

Neither a type variable nor a wildcard is a legal type parameter for an @All List<> injection point, i.e. @Inject @All List<?> all is not supported and results in a deployment error.

It is also possible to obtain the list of all bean instance handles programmatically via the Arc.container().listAll() methods.

5.18. Ignoring Class-Level Interceptor Bindings for Methods and Constructors

If a managed bean declares interceptor binding annotations on the class level, the corresponding @AroundInvoke interceptors will apply to all business methods. Similarly, the corresponding @AroundConstruct interceptors will apply to the bean constructor.

For example, suppose we have a logging interceptor with the @Logged binding annotation and a tracing interceptor with the @Traced binding annotation:

@ApplicationScoped @Logged public class MyService { public void doSomething() { ... } @Traced public void doSomethingElse() { ... } }

In this example, both doSomething and doSomethingElse will be intercepted by the hypothetical logging interceptor. Additionally, the doSomethingElse method will be intercepted by the hypothetical tracing interceptor.

Now, if that @Traced interceptor also performed all the necessary logging, we’d like to skip the @Logged interceptor for this method, but keep it for all other methods. To achieve that, you can annotate the method with @NoClassInterceptors:

@Traced @NoClassInterceptors public void doSomethingElse() { ... }

The @NoClassInterceptors annotation may be put on methods and constructors and means that all class-level interceptors are ignored for these methods and constructors. In other words, if a method/constructor is annotated @NoClassInterceptors, then the only interceptors that will apply to this method/constructor are interceptors declared directly on the method/constructor.

This annotation affects only business method interceptors (@AroundInvoke) and constructor lifecycle callback interceptors (@AroundConstruct).

5.19. Exceptions Thrown By An Asynchronous Observer Method

If an exception is thrown by an asynchronous observer then the CompletionStage returned by the fireAsync() method completes exceptionally so that the event producer may react appropriately. However, if the event producer does not care then the exception is ignored silently. Therefore, Quarkus logs an error message by default. It is also possible to implement a custom AsyncObserverExceptionHandler. A bean that implements this interface should be @javax.inject.Singleton or @javax.enterprise.context.ApplicationScoped.

NoopAsyncObserverExceptionHandler

@Singleton public class NoopAsyncObserverExceptionHandler implements AsyncObserverExceptionHandler { void handle(Throwable throwable, ObserverMethod<?> observerMethod, EventContext<?> eventContext) { // do nothing } }

How are beans managed in Spring boot?

Spring beans are just instance objects that are managed by the Spring container, namely, they are created and wired by the framework and put into a "bag of objects" (the container) from where you can get them later.

What is the default nature of the beans defined in Spring framework?

The default scope is always singleton. However, when you need one and only one instance of a bean, you can set the scope property to singleton in the bean configuration file, as shown in the following code snippet − <!--

Is every class a bean in Spring?

The short answer is No, as you need to inform Spring as to which classes are meant to be "components" and which ones aren't.

How do you define a class as bean in Spring boot?

While the @Component annotation is used to decorate classes that are auto-detected by Spring scanning, the @Bean annotation is used to explicitly declare a bean creation. The @Value annotation is used to set the value of the app.name property into the appName parameter. logger.info("Application name: {}", appName.

Toplist

Neuester Beitrag

Stichworte