Quarkus 4
Quarkus 4
Quarkus 4
@Validated
@NotNull
private String message;
@NotNull
@Valid
@NotNull
@Positive
private Integer prizeAmount;
@NotEmpty
private List<String> recipients;
public Integer getPrizeAmount() { return this.prizeAmount; }
public void setPrizeAmount(Integer prizeAmount) { this.prizeAmount =
prizeAmount; }
public List<String> getRecipients() { return this.recipients; }
public void setRecipients(List<String> recipients) { this.recipients =
recipients; }
}
}
Listing 2.5 shows that required fields must be annotated. Additionally, Spring’s
@Validated annotation must be placed on the class to enable any validation, and
the @Valid annotation must exist on any nested class attributes. Additional bean vali-
dation annotations can be used for more advanced validation, such as the @Positive
annotation placed on the prizeAmount nested class.
Quarkus
There are a few differences in Quarkus. First, Quarkus uses interfaces rather than
approach when it comes to required versus optional properties. By default, all prop-
an Optional
the @Valid or @Validated annotations to perform validation. Quarkus validates a
-
dation extension is present.
@WithName(“recipients”)
List<String> getRecipients();
}
}
The previous section showed how to inject values into properties during configura-
tion. In practice, it is often necessary to inject different sets of configuration values
at different times. For example, the configurations for testing are often different
from the configurations for running the application in production. Both Quarkus
and Spring support this capability by the concept of a Profile. However, one ma-
jor difference is that Quarkus unifies all configurations for all profiles into a single
configuration file.
Description
dev Activated when running in development mode (i.e., mvn quarkus:dev).
test Activated when running tests (i.e., mvn verify).
prod
If both the system property and the environment variable are set, the system property
.env
_{PROFILE-NAME}_KEY=value
.env
Example
Return to the src/main/resources/application.properties
present is the one added previously. The value of the greeting.name property will be
Quarkus (properties)
Your application.properties
greeting.name=Quarkus (properties)
%dev.greeting.name=Quarkus for Spring Developer (dev)
%prod.greeting.name=Quarkus for Spring Developer (prod)
%test.greeting.name=RESTEasy
Return to the browser window and refresh the page. You should now see the text Hello
Quarkus for Spring Developer (dev). Return to the terminal where the ./mvnw
quarkus:dev command is running and terminate dev mode, typically using
keystrokes.
Now run the application outside of dev mode to activate the prod
When the application starts up, it should print out . Also, no-
tice the startup time. In most cases, the startup time will be less than one second. This
Return to the browser window and refresh the page. You should now see the text Hello
Quarkus for Spring Developer (prod). Return to the terminal and terminate the
running application in the same way as before.
Dependency Injection
needs to function correctly. Dependency injection was one of the key components of
Spring Framework. It became so popular that Java and the Java EE platform followed
what Spring was doing. Today, Contexts and Dependency Injection (CDI) for Java 2.0
(JSR-365) [2.23] is the base for ArC [2.24], the dependency injection framework used
by Quarkus.
One of the main differences between dependency injection in Quarkus and Spring
is that Quarkus performs as much of the injection as possible at build time, whereas
Spring performs injection at runtime. Quarkus’s build-time augmentation leads to much
better startup performance because ArC just needs to load all the metadata that was
already computed at build time.
attributes. Additionally, in both Spring and Quarkus, the default process of matching
a bean to an injection point is done via type. This pattern is synonymous with Spring’s
“injection by type.” In Quarkus, exactly one bean must be assignable to an injection
point; otherwise, the build fails. In Spring, if there were multiple beans of the same type,
the application would build but fail to start.
whether the application is correct as part of their local development or CI/CD pipelines.
There is no need to wait until the application is deployed into an environment and start-
Scopes
Quarkus and Spring also support the notion of bean scopes. Table 2.8 describes the
scopes available for a Quarkus application, along with the Spring equivalents. Quarkus
extensions can provide other custom scopes. For example, the quarkus-narayana-jta
extension provides @javax.transaction.TransactionScoped.
@ApplicationScoped or @Singleton?
Quarkus provides two different scopes representing Spring’s singleton scope:
@ApplicationScoped and @Singleton. Which one to use depends on what you are
trying to accomplish.
An @Singleton bean has no proxy. Proxies should be familiar to Spring users because
they are the backbone behind Spring’s dependency injection mechanism. When the
Spring runtime injects a bean into another bean, the injection is usually a proxy to the
actual bean. It’s a placeholder where Spring can wrap the bean with some alternate
implementation or delay the bean’s instantiation.
Because an @Singleton bean lacks a proxy, the bean is created eagerly when an instance
@Singleton bean
can not be mocked. By contrast, an instance of an @ApplicationScoped bean is created
lazily once an instance is acted upon @ApplicationScoped
Example
Let’s take the example being used so far in this chapter and change it to use de-
pendency injection. All the logic for computing a greeting message resides in the
GreetingResource class. Follow these steps to create a new class containing the
required functionality and refactor GreetingResource to inject a new bean.
greeting) {
this.greeting = greeting;
}
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return this.greetingService.getGreeting();
}
}
Return to your browser and refresh the page. You should see the text Hello Quarkus
for Spring Developer (dev). Return to the terminal where the ./mvnw quarkus:dev
command is running and terminate Dev Mode.
-
ples in this book promote constructor injection. Listing 2.10 shows what the Greeting
Resource
Quarkus advises against
using private
@Path(“/hello-resteasy”)
public class GreetingResource {
@Inject up build and application
GreetingService greetingService; startup times and reduces
an application’s memory
@GET footprint.
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return this.greetingService.getGreeting();
}
}
Lifecycle Callbacks
Both Quarkus and Spring support creating lifecycle callbacks on beans, supporting the
and @PreDestroy annotations found in the javax.annotation
package. Methods annotated with are invoked before the bean
instance is placed into service. It is safe to perform bean initialization within the method.
Methods annotated with @PreDestroy are invoked before the destruction of the bean
instance. It is safe to perform cleanup tasks within the method.
Native Image
Quarkus’s design accounted for native compilation from the onset. A Quarkus native
executable starts much faster and uses far less memory than a traditional JVM. Sim-
ilar native image capabilities in Spring are still considered experimental or beta as of
this writing.
To build a native image and run native image tests, you need to install either a distribu-
tion of GraalVM or a working container runtime. Additionally, Maven or Gradle has some Note: Maven and Gradle
scaffolding to facilitate the building of the native image and launching it for tests. scaffolding isn’t discussed in
this book. However, it is doc-
The following example assumes that GraalVM CE or Mandrel has been downloaded, umented extensively in the
and either the GRAALVM_HOME environment variable has been set to its location. Once Quarkus guide for building
all the requirements are met, a native image can be built using a simple command on a native executable [2.25],
the terminal: along with how to use a
container runtime environ-
$ ./mvnw package -Pnative ment instead of installing a
GraalVM distribution locally.
Native compilation can take several minutes to complete. Once complete, the native The code.quarkus.io gen-
executable chapter-2-simple-project-1.0.0-SNAPSHOT-runner can be found in erator provides everything
the project’s target directory. On the terminal, execute the following: needed for Maven or Gradle
to function properly.
$ ./target/chapter-2-simple-project-1.0.0-SNAPSHOT-runner
From this output, notice the application started in just 15 milliseconds. Return to
your browser and refresh the page. You should see the text Hello Quarkus for
Spring Developer (prod).
The 18604 value in the RSS column is the RSS size, in kilobytes, of the running process. Note: The startup time and
Dividing that number by 1024 gives an RSS size of just over 18 megabytes for the run- RSS output was gathered
ning application’s memory footprint. from a terminal running on
macOS. Your startup time
Return to the terminal where the ./target/chapter-2-simple-project-1.0.0- and RSS output may vary.
SNAPSHOT-runner command is running and terminate it the same way as before.
Testing
Testing is essential in any application to ascertain that the required functionality is
implemented correctly. Both Quarkus and Spring include testing frameworks based on
JUnit 5 [2.27] and Mockito [2.28].
Continuous Testing
As mentioned in Chapter 1, Quarkus enables test-driven development by under-
standing which tests are affected by classes and methods within the application. As
changes are made to the application source code, Quarkus can automatically rerun
affected tests in the background, giving developers instant feedback about the code
they are writing.
To enable continuous testing, return to the terminal and execute ./mvnw quarkus:dev.
Once the Quarkus banner appears, notice in the terminal the following text at the very
bottom: Tests paused, press [r] to resume, also shown in Figure 2.6.
Press r in the terminal to enable continuous testing. The console should show output
similar to:
--
All 1 tests are passing (0 skipped), 1 tests were run in 4552ms.
Unit Testing
Unit testing aims to isolate a single class so methods on the class can be tested. The
test retains complete control over any external dependencies the class under test may
have. One way to facilitate control over dependencies is to use constructor injection
when creating classes, as seen in the previous examples. Bootstrapping the entire
application isn’t necessary in most cases when using this pattern with either Spring or
Quarkus. Simply constructing a class and passing mocks or other required classes via
the constructor and then asserting results is good enough.
Let’s see this in action by adding a test class for the GreetingService class created
earlier. Create a new Java class called GreetingServiceTest inside the src/test/
java/org/acme folder. Inside that class, add a single test method and save the changes:
@Test
public void getGreetingOk() {
Assertions.assertEquals(“Hello Quarkus”,
new GreetingService(“Quarkus”).getGreeting());
}
Return to the terminal and notice that Quarkus automatically ran the test successfully.
Also notice that only one test was run: the test that was just created. The existing test in
the GreetingResourceTest.java class was not rerun.
--
All 2 tests are passing (0 skipped), 1 tests were run in 53ms.
There are times when you need the underlying framework to provide capabilities
for you to write tests. One such example is when exposing RESTful endpoints. In
that case, a test would issue an HTTP request with specific parameters, such as a
request path, headers, and request parameters, and then perform assertions on
some returned JSON structure. This kind of testing can’t be done by instantiating a
class and calling methods directly on it, either in Quarkus or in Spring.
.body(is(“Hello RESTEasy”));
}
}
Another thing to notice is the @QuarkusTest annotation at the top of the class. This
annotation is similar to the @SpringBootTest annotation on tests in a Spring Boot
application. The main difference in Quarkus is that the application is started only once
and reused for all tests annotated with @QuarkusTest. In contrast, in Spring Boot,
the application is started before each test class annotated with @SpringBootTest
and then shut down after all tests in the class are executed. Subsequently, a Spring
application restarts when the next test class annotated with @SpringBootTest is
executed.
Additionally, classes annotated with @QuarkusTest can inject other beans, as we’ll
see in the next section. Quarkus ensures that the state of the beans in the running
application matches tests or test class’s requirements without having to restart the
application.
Mocking
Earlier the GreetingResource class was refactored to inject a GreetingService, so
the GreetingResourceTest class test should be refactored as well. The Greetin-
gResource class needs to be tested in isolation, so it would be better for the test to
mock the GreetingService class.
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5-mockito</artifactId>
<scope>test</scope>
</dependency>