micronaut-core: IntelliJ incremental build not working when modifying a class that is directly injected.
Expected Behavior
Ideally, IntelliJ with enabled “Build and run using” -> “IntelliJ IDEA” should be able to leverage its incremental build when Micronaut managed beans are modified.

Incremental build takes usually few milliseconds comparing to a full module rebuild which may take 30 second or 2 minutes or even longer. In such case, when a Micronaut based application has many beans, development becomes extremely painful especially if people write their code + tests in a TDD cycle with small increments.
Note that the full rebuild is triggered when a particular class that is being injected is changed. Such as
- If an impl class is injected, then when that class is changed, a full rebuild is triggered.
 - if an interface is injected, then when that interface is changed, a full rebuild is triggered.
 
whereas
- If an interface is injected, then modifying its impl doesn’t make IntelliJ to do a full rebuild.
 
Based on what I investigated, Micronaut 2 (2.2.1, 2.4.3, 2.5.13) and 3 (3.4.4) suffer from that behavior. However Micronaut 1 (I tried 1.3.7) was able to do incremental compilation.
In our project with 150 beans we suddenly hit the threshold. The full rebuild is taking more than 1 minute and because we like to inject beans without declaring interfaces for them (some folks are old enough to remember the J2EE days and are refusing to accept a workaround to “always” create an interface) we’re now completely desperate because of the slow turn around.
This is how incremental compilation in IntelliJ looks like:

Actual Behaviour
When modifying a class file of a bean that is injected somewhere else, IntelliJ full rebuild is triggered.
This is what IntelliJ logs when MyService is injected into DemoController directly and is changed:
2022-05-25 15:52:55,193 [  32193]   FINE - #o.j.j.b.j.d.Mappings - Begin of Differentiate:
2022-05-25 15:52:55,193 [  32193]   FINE - #o.j.j.b.j.d.Mappings - Easy mode: false
2022-05-25 15:52:55,198 [  32198]   FINE - #c.i.u.i.FileChannelWithSizeTracking - Inst:/Users/stepan.vavra/Library/Caches/JetBrains/IntelliJIdea2022.1/compile-server/demo_7ab3cf90/mappings/sourceToClass.tab.values@33bd46cc,Thread[JPS thread pool,5,main],java.net.URLClassLoader@2626b418
2022-05-25 15:52:55,198 [  32198]   FINE - #c.i.u.i.FileChannelWithSizeTracking - read:/Users/stepan.vavra/Library/Caches/JetBrains/IntelliJIdea2022.1/compile-server/demo_7ab3cf90/mappings/sourceToClass.tab.values@33bd46cc,Thread[JPS thread pool,5,main],7412,168355
2022-05-25 15:52:55,208 [  32208]   FINE - #o.j.j.b.j.d.Mappings - Processing changed classes:
2022-05-25 15:52:55,209 [  32209]   FINE - #o.j.j.b.j.d.Mappings - Changed: com/example/MyService
2022-05-25 15:52:55,213 [  32213]   FINE - #o.j.j.b.j.d.Mappings - End of changed classes processing
2022-05-25 15:52:55,213 [  32213]   FINE - #o.j.j.b.j.d.Mappings - Checking dependent classes:
2022-05-25 15:52:55,216 [  32216]   FINE - #o.j.j.b.j.d.Mappings - Dependent class: com/example/$MyService$Definition$Reference
2022-05-25 15:52:55,228 [  32228]   FINE - #o.j.j.b.j.d.Mappings - Dependent class: com/example/$DemoController$Definition
2022-05-25 15:52:55,230 [  32230]   FINE - #o.j.j.b.j.d.Mappings - Turning non-incremental for the BuildTarget because dependent class is annotation-processor generated
2022-05-25 15:52:55,231 [  32231]   FINE - #o.j.j.b.j.d.Mappings - Dependent class: com/example/DemoController
2022-05-25 15:52:55,231 [  32231]   FINE - #o.j.j.b.j.d.Mappings - Dependent class: com/example/$MyService$Definition
2022-05-25 15:52:55,232 [  32232]   FINE - #o.j.j.i.Builder - Differentiate Results:
2022-05-25 15:52:55,232 [  32232]   FINE - #o.j.j.i.Builder -    Compiled Files:
2022-05-25 15:52:55,232 [  32232]   FINE - #o.j.j.i.Builder -       /Users/stepan.vavra/Repos/other/micronaut-test-slow-compilation/src/main/java/com/example/MyService.java
2022-05-25 15:52:55,233 [  32233]   FINE - #o.j.j.i.Builder -    Affected Files:
2022-05-25 15:52:55,233 [  32233]   FINE - #o.j.j.i.Builder - End Of Differentiate Results.
2022-05-25 15:52:55,233 [  32233]   INFO - #o.j.j.i.Builder - Non-incremental mode: Marking demo.main and direct dependants for recompilation
and a full rebuild is triggered.
This is how a full rebuild looks like in IntelliJ:

Steps To Reproduce
Consider a simple demo app
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import jakarta.inject.Inject;
@Controller
public class DemoController {
    private final MyService myService;
    @Inject
    public DemoController(MyService myService) {
        this.myService = myService;
    }
    @Get("/message")
    String message() {
        return myService.hello("hello");
    }
}
with a service
import jakarta.inject.Singleton;
@Singleton
class MyService {
    public String hello(String input) {
        // comment and uncomment next 2 lines to get a full rebuild
        return "foo".equals(input) ? "hello3" : "HELLO";
//        return "foo".equals(input.toLowerCase()) ? "hello3" : "HELLO";
    }
}
When the MyService is changed in a way that its bytecode significantly changes (i.e., it’s not enough to change the string constants), that is to do for instance
return "foo".equals(input.toLowerCase()) ? "hello" : "HELLO";
Then when “Build” -> “Build module” is executed, IntelliJ marks the module for a full recompilation
Checking dependencies… [demo.main]
Marking demo.main and direct dependants for recompilation
Environment Information
As mentioned above, this problem happens with Micronaut 2.5.13 and 3.4.4 I tried IntelliJ 2022.1, 2021.3.3, 2021.3.2. JDK 17 and 11 on Mac.
Example Application
https://github.com/stepanv/micronaut-full-rebuild-sample-app
Version
3.4.4
About this issue
- Original URL
 - State: closed
 - Created 2 years ago
 - Comments: 17 (17 by maintainers)
 
Thanks @graemerocher , that works perfectly!
Is the
flaw of Micronaut or is it a Gradle thing? I have the reproducer ready so I’m ready to report it right away.