Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot used Lombok @Builder with @Introspected in 4.7.1 #11432

Closed
musketyr opened this issue Dec 12, 2024 · 10 comments
Closed

Cannot used Lombok @Builder with @Introspected in 4.7.1 #11432

musketyr opened this issue Dec 12, 2024 · 10 comments
Assignees
Labels
status: in progress status: pr submitted A pull request has been submitted for the issue type: bug Something isn't working

Comments

@musketyr
Copy link
Contributor

Expected Behavior

Given the simple POJO class using Lombok @Builder annotation with @NoArgsConstructor and @Introspected annotation then the introspection is generated and the class can be instantiated using the BeanIntrospector API.

import io.micronaut.core.annotation.Introspected;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.NoArgsConstructor;

// using Builder annotation from Lombok breaks the introspection
@Builder

@AllArgsConstructor
@NoArgsConstructor

@Introspected
public class POJO {

    private String foo;

}
    @Test
    void canInstantiatePOJO() {
        POJO pojo = BeanIntrospector.SHARED.getIntrospection(POJO.class).instantiate();
        Assertions.assertNotNull(pojo);
    }

This was working without issues in 4.7.0 and before.

Actual Behaviour

When trying to instantiate the class using BeanIntrospection API then I got following error

No default constructor exists
io.micronaut.core.reflect.exception.InstantiationException: No default constructor exists
	at io.micronaut.inject.beans.AbstractInitializableBeanIntrospection.instantiate(AbstractInitializableBeanIntrospection.java:326)
	at introspection.issue.IntrospectionTest.canInstantiatePOJO(IntrospectionTest.java:11)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)

Steps To Reproduce

Run IntrospectionTest in the example application

or

  1. create brand new 4.7.1 Micronaut application with Lombok support mn create-app --build=gradle_kotlin --jdk=21 --lang=java --test=junit --features=lombok com.example.demo
  2. create POJO class from the Expected Behaviour description
  3. create InstrospectionTest with the test method from the Expected Behavior description
  4. run the test

Environment Information

Operating system macOS 15.0.1 (aarch64)
Java runtime Azul Zulu OpenJDK Runtime Environment 21.0.5+11-LTS
Java VM Azul Zulu OpenJDK 64-Bit Server VM 21.0.5+11-LTS (mixed mode, sharing)

see https://scans.gradle.com/s/6avzotdmjfhg4#infrastructure

Example Application

https://github.com/musketyr/micronaut-4.7.1-introspection-issue

Version

4.7.1

@musketyr
Copy link
Contributor Author

It's very likely related to #11347 @graemerocher

@graemerocher
Copy link
Contributor

does BeanIntrospector.SHARED.getIntrospection(POJO.class).builder().build() work?

@musketyr
Copy link
Contributor Author

yes, this one works

    @Test
    void canInstantiatePOJOUsingBuilder() {
        POJO pojo = BeanIntrospector.SHARED.getIntrospection(POJO.class).builder().build();
        Assertions.assertNotNull(pojo);
    }

@graemerocher
Copy link
Contributor

so previously it was using the constructor of the type to instantiate it I guess now it goes through the builder. Seems weird to combine 2 different ways to construct the object (builder and no args constructor) but maybe that is just me.

@musketyr
Copy link
Contributor Author

musketyr commented Dec 12, 2024

We often combine these as each of the approaches has different use cases:

  • builder is used by the developers to create the objects in more convenient ways
  • no-args constructor is used by libs such Jackson or DynamoDB integration to instantiate the objects

@fuinbf
Copy link

fuinbf commented Dec 16, 2024

There is also a similar issue when using record entities with automatic timestamps:

import io.micronaut.data.annotation.DateCreated;
import io.micronaut.data.annotation.Id;
import io.micronaut.data.annotation.MappedEntity;
import lombok.Builder;

import java.time.Instant;
import java.util.UUID;

@MappedEntity("myentity")
@Builder
record MyEntity(@Id UUID id, @DateCreated Instant createdAt) { }

The introspection writer marks the createdAt field as immutable and as a result the DefaultEntityEventContext skips updating it leading to null value for the field.

@aoyanb
Copy link

aoyanb commented Dec 17, 2024

I am afraid that my issue is not related to Lombok or introspected, but I have entities with Jakarta persistence API annotations combined with some @transient annotation from micronaut which I did to solve the entity binding issue. It is working on version 4.6.2. Now seems like updating to 4.7.2 does not support this. Any idea what changed!
Even some nullable @manytoone relations showing bind to entity errors. I am using micronaut data JDBC

@graemerocher graemerocher self-assigned this Dec 17, 2024
@graemerocher graemerocher added type: bug Something isn't working status: in progress labels Dec 17, 2024
@graemerocher
Copy link
Contributor

I am looking into it. Thanks.

@graemerocher
Copy link
Contributor

@aoyanb probably the same issue

@graemerocher
Copy link
Contributor

@aoyanb I recommend you create an example that reproduces the issue in an issue report. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: in progress status: pr submitted A pull request has been submitted for the issue type: bug Something isn't working
Projects
No open projects
Status: Done
Development

No branches or pull requests

4 participants