18.4 Breaking Changes

This section will document breaking changes between Micronaut 1.0 and Micronaut 2.0

Thread Pool Selection Now Manual

In Micronaut 1.x, Micronaut would attempt to automatically deduce the thread pool to use based on the return type of the controller method.

This approach caused some confusion and created a false association between reactive types and blocking operations.

Micronaut 2.x now leaves it up to the user to specify the thread pool to schedule operations on, including blocking operations.

If you are upgrading from Micronaut 1.x and have a controller that executes blocking I/O operations (such as JDBC or JPA) it is recommended that you add the @ExecuteOn annotation to execute the blocking operations on a different thread.

Using @ExecuteOn

  1. import io.micronaut.http.annotation.*;
  2. import io.micronaut.scheduling.TaskExecutors;
  3. import io.micronaut.scheduling.annotation.ExecuteOn;
  4. @Controller("/executeOn/people")
  5. public class PersonController {
  6. private final PersonService personService;
  7. PersonController(
  8. PersonService personService) {
  9. this.personService = personService;
  10. }
  11. @Get("/{name}")
  12. @ExecuteOn(TaskExecutors.IO) (1)
  13. Person byName(String name) {
  14. return personService.findByName(name);
  15. }
  16. }

Using @ExecuteOn

  1. import io.micronaut.http.annotation.*
  2. import io.micronaut.scheduling.TaskExecutors
  3. import io.micronaut.scheduling.annotation.ExecuteOn
  4. @Controller("/executeOn/people")
  5. class PersonController {
  6. private final PersonService personService
  7. PersonController(
  8. PersonService personService) {
  9. this.personService = personService
  10. }
  11. @Get("/{name}")
  12. @ExecuteOn(TaskExecutors.IO) (1)
  13. Person byName(String name) {
  14. return personService.findByName(name)
  15. }
  16. }

Using @ExecuteOn

  1. import io.micronaut.http.annotation.*
  2. import io.micronaut.scheduling.TaskExecutors
  3. import io.micronaut.scheduling.annotation.ExecuteOn
  4. @Controller("/executeOn/people")
  5. class PersonController (private val personService: PersonService) {
  6. @Get("/{name}")
  7. @ExecuteOn(TaskExecutors.IO) (1)
  8. fun byName(name: String): Person {
  9. return personService.findByName(name)
  10. }
  11. }
1The @ExecuteOn annotation is used to execute the operation on the I/O thread pool
If you wish to return to the previous behaviour as defined in Micronaut 1.x you can set micronaut.server.thread-selection to AUTO in configuration.

Micronaut Cache Removed from Core

The micronaut-runtime module in Micronaut 1.x included support for caching AOP with the @Cache annotation. The core caching library and a default implementation based on caffeine were included.

If you are using the caffeine backed cache support, you must add a dependency to continue doing so. Add io.micronaut.cache:micronaut-cache-caffeine to your build to maintain the existing functionality. The new caffeine cache module no longer uses the internal shadowed caffeine version and declares its own normal dependency on caffeine. You may need to exclude the transitive dependency in order to prevent conflicts with other versions that may be being pulled in.

The management endpoint for caches is no longer included by default in the micronaut-management module. If you would like to use the caches endpoint, add a dependency on io.micronaut.cache:micronaut-cache-management.

Support for RxJava 1.x Removed from Core

RxJava 1.x support is no longer included in core by default. You can re-enable support by adding the following dependency:

  1. implementation("io.micronaut.rxjava1:micronaut-rxjava1")
  1. <dependency>
  2. <groupId>io.micronaut.rxjava1</groupId>
  3. <artifactId>micronaut-rxjava1</artifactId>
  4. </dependency>

Support for Reactor Removed from Core

Reactor support is no longer included in core by default. You can re-enable support by adding the following dependency:

  1. implementation("io.micronaut.reactor:micronaut-reactor")
  1. <dependency>
  2. <groupId>io.micronaut.reactor</groupId>
  3. <artifactId>micronaut-reactor</artifactId>
  4. </dependency>

Loggers Endpoint Modifications Now Sensitive

The method on the loggers endpoint to modify logging is now sensitive by default. To revert to the previous behavior, apply the following configuration:

  1. endpoints:
  2. loggers:
  3. write-sensitive: false

Server Side Content Negotiation May Require Test Changes

The new support for server side content negotiation may require changes to tests. For example a test that that makes a call such as:

  1. String result = client.toBlocking().retrieve(
  2. HttpRequest.GET('/test')
  3. .accept("text/plain"), String
  4. )

If the server implementation does not declare the route as @Produces("text/plain") the request will be rejected with a NOT_ACCEPTABLE HTTP status code.

Server Filter Ordering

In Micronaut 1.x almost all built in filters operated on order 0, meaning the order was non deterministic. That caused some compatibility issues with the metrics and security modules. A new class has been added ServerFilterPhase that defines the phases of server filtering and sets an order. Many filters have been updated to now have their order reflect the phase they participate in.

Heartbeat Events No Longer Published if no Discovery Clients present

A HeartbeatEvent is no longer published automatically if there are no DiscoveryClient instances present.

This behaviour can be changed by setting micronaut.heartbeat.enabled to true explicitly in configuration.

Several TypeConverters Are No Longer Beans

Many TypeConverter instances that were previously beans are no longer beans and are deprecated including:

These should not be injected directly but instead used through the ConversionService API.

@Executable No Longer Stereotype of @Around

In Micronaut 1.x the @Executable annotation was a meta-annotation specified on the @Around and @Introduction annotations. This meant that an ExecutableMethod reference was generated for every usage of AOP advice which consumed additional memory unnecessarily.

In Micronaut 2.x and above this is no longer the case and if you need an ExecutableMethod generated you should add the @Executable annotation to any custom AOP advice you specify otherwise the method will not be available via the BeanDefinition interface (using for example the getExecutableMethods method).

Spot Bugs Instead of JSR-305 Nullable/NonNull Annotations

The JSR-305 annotations library (com.google.code.findbugs:jsr305) is no longer a dependency (replaced by spotbugs-annotations). If you need this library you will need to add it manually.

Events Renamed

The following events were renamed to avoid confusion with other events of similar names:

Old NameNew Name

io.micronaut.discovery.event.ServiceStartedEvent

ServiceReadyEvent

io.micronaut.discovery.event.ServiceShutdownEvent

ServiceStoppedEvent

New Package for Netty Specific Classes of HTTP Client

The HTTP client implementation classes including DefaultHttpClient (considered internal in Micronaut 1.x) have been moved to a sub-package called io.micronaut.http.client.netty.

HTTP Clients No Longer Named Beans

HTTP clients declared with micronaut.http.services (see Manual Service Discovery Configuration) are no longer named beans in the context and cannot be injected with javax.inject.Named, for example given the configuration:

Manually configuring services

  1. micronaut:
  2. http:
  3. services:
  4. foo:
  5. urls:
  6. - http://foo1
  7. - http://foo2

You can no longer inject an HTTP client with @Named("foo"):

  1. @Inject
  2. @Named("foo")
  3. RxHttpClient httpClient;

Instead you should always use @Client:

  1. @Inject
  2. @Client("foo")
  3. RxHttpClient httpClient;

Source Retention Annotations No Longer Retained in Runtime Metadata

In Micronaut 1.x annotations specified as source retention were still retained in the AnnotationMetadata interface. As of Micronaut 2.x this is no longer the case with source retention annotations only available within the compiler APIs.

If you wish to retain a particular source level annotation when upgrading you can write an AnnotationTransformer that alters the RetentionPolicy of the annotation.

Iterable Beans No Longer Have An Implicit Primary

In Micronaut 1.x injecting a single instance of an iterable bean without qualifiers would inject the first bean. An iterable bean is typically anything annotated with @EachProperty or @EachBean. Those beans typically are referenced from configuration. The first bean in this context is the first item in configuration that matches what the annotation expects.

For example if you created a bean with @EachProperty("cars"), then specified the following in your config:

  1. cars:
  2. ford:
  3. cylinders: 8
  4. subaru:
  5. cylinders: 4

Requesting a single instance of that bean would result in the “ford” instance being injected. Because that behavior is surprising and inconsistent with other types of beans, that is no longer the case and a NonUniqueBeanException will be thrown.

This change does not apply to an explicit primary defined in the annotation (@EachProperty(value = “cars”, primary = “ford”)), nor requesting the instance by a qualifier (@Named(“ford”) CarConfig carConfig).

No Longer Possible to Return Null to Disable a Bean

It is no longer possible to return null from a @Factory bean method to disable the bean. You should now throw DisabledBeanException instead.

Invalid Configuration File Locations

Specifying a file with micronaut.config.files, either through the system property or environment variable, that does not exist or cannot be read will now result in the application failing to startup. In previous versions of Micronaut a warning would be logged and the file would be ignored.

PropertySourceLoader Changes

Some default interface methods are no longer default and require implementation.

Deprecation Removal

Most if not all deprecated classes and methods have been removed.

Map Property Binding

In Micronaut 1.x java.util.Map properties being bound from config were inconsistently bound as either a nested or flat. Now maps are bound as nested by default and the @MapFormat annotation’s default value for transformation has been changed to reflect that.

For example given the config:

  1. persons:
  2. joe:
  3. age: 30
  4. sally:
  5. age: 25

A map property injected via @Property(name ="persons") may have been injected flat or nested depending on a couple factors.

Flat

  1. {"joe.age": 30, "sally.age": 25}

Nested

  1. {"joe": {"age": 30}, "sally": {"age": 25}}

To bind to a map with flat keys, add the @MapFormat annotation and set the transformation member.

GraalVM BOM Entry

The no longer used group for Graal is no longer part of the bom. While upgrading if you depend on Graal you may see Could not find com.oracle.substratevm:svm:.. To resolve the issue, change the dependency group to org.graalvm.nativeimage.

@Retryable and @CircuitBreaker Exception Handling

@Retryable and @CircuitBreaker in previous versions of Micronaut resolved includes and excludes explicitly. Any exception thrown had to exactly match one of the exceptions specified. This has been changed to now also include subclasses of the exception types specified.

MessageSource API Changes

The semantics of the getMessage method have been changed to also interpolate the message with any provided variables. In previous versions of Micronaut, the raw message was returned from the bundle. To support reading the raw message, a new method getRawMessage has been added.

In addition, escaping of messages with single quotes is now implemented in accordance with the standard Java MessageFormat class. Messages that contain a single quote will now need escaping in order for the quote to output as it did previously. For example:

  1. my.message=We love Micronaut's documentation

Would now be output as We love Micronauts documentation. To achieve the desired result, escape the single quote with another single quote.

  1. my.message=We love Micronaut''s documentation
This change also applies to messages in custom constraint annotations, which interpolate the message via the message source api.

Environment Order Bugfix

Environments specified through the application context builder have priority over environments deduced or supplied through the MICRONAUT_ENVIRONMENTS environment variable, or the equivalent system property. A bug in the logic however did not change the order of a specified environment if it previously was found or deduced. This issue manifested itself with @MicronautTest(environments = "test"). The test environment is already deduced for tests, so it retained the order of other deduced environments, and was able to be overriden by MICRONAUT_ENVIRONMENTS=dev. In Micronaut 1.x configuration for dev would have overridden test. In Micronaut 2.x test will override dev.

Introspections and Inner Classes

A bug in Micronaut 1.x caused bean introspections to be generated for inner classes of classes annotated with @Introspected. That also applies to classes where @Introspection is a meta annotation, like @Entity. This may have an impact for GraalVM users that rely on accesses to those classes without using reflection. For example:

  1. @Entity
  2. public class Pet {
  3. ...
  4. private PetType type = PetType.DOG;
  5. // getters & setters
  6. public enum PetType {
  7. DOG,
  8. CAT
  9. }
  10. }

Previously a bean introspection would have been generated for PetType. That is no longer the case. If the type should be introspected, simply add the annotation.

Executable Methods

A bug in Micronaut 1.x caused bean definitions to be created for classes that only declared executable annotations on one or more methods. Classes with executable methods must now be explicitly declared as a bean with a scope annotation in order for a bean definition to be created.

Super interfaces No Longer Searched for Fallbacks

If the @Fallback annotation is used to specify a fallback for a @Client interface, the super interfaces of the client will no longer be traversed to lookup the fallback to invoke.

In this case it may be necessary to specify the api member of the @Recoverable annotation to ensure the fallback can be looked up. For example:

Specifying the api to recover from

  1. @Client("/Books")
  2. @Recoverable(api = BookApi)
  3. interface BookClient extends BookApi {
  4. @Override
  5. Book get(Long id);
  6. }

AWS FunctionClient Moved to AWS Module

The AWS based function client has been made part of the Micronaut AWS project. If you need this functionality add the following dependency:

  1. implementation("io.micronaut.aws:micronaut-function-client-aws")
  1. <dependency>
  2. <groupId>io.micronaut.aws</groupId>
  3. <artifactId>micronaut-function-client-aws</artifactId>
  4. </dependency>