The and BeanIntrospection interfaces allow looking up bean introspections that allow you to instantiate and read/write bean properties without using reflection or caching reflective metadata which consumes excessive memory for large beans.

    Unlike the JDK’s every class is not automatically available for introspection, to make a class available for introspection you must as a minimum enable Micronaut’s annotation processor (micronaut-inject-java for Java and Kotlin and micronaut-inject-groovy for Groovy) in your build and ensure you have a runtime time dependency on micronaut-core.

    1. <annotationProcessorPaths>
    2. <path>
    3. <groupId>io.micronaut</groupId>
    4. <artifactId>micronaut-inject-java</artifactId>
    5. <version>2.1.4</version>
    6. </path>
    7. </annotationProcessorPaths>
    1. runtime("io.micronaut:micronaut-core:2.1.4")
    1. <dependency>
    2. <groupId>io.micronaut</groupId>
    3. <artifactId>micronaut-core</artifactId>
    4. <version>2.1.4</version>
    5. <scope>runtime</scope>
    6. </dependency>

    Once your build is configured you have a few ways to generate introspection data.

    Use the @Introspected Annotation

    The annotation can be used on any class which you want to make available for introspection, simply annotate the class with @Introspected:

    1. import io.micronaut.core.annotation.Introspected;
    2. @Introspected
    3. public class Person {
    4. private String name;
    5. private int age = 18;
    6. public Person(String name) {
    7. this.name = name;
    8. }
    9. public String getName() {
    10. return name;
    11. }
    12. public void setName(String name) {
    13. this.name = name;
    14. }
    15. public int getAge() {
    16. return age;
    17. }
    18. public void setAge(int age) {
    19. this.age = age;
    20. }
    21. }
    1. import groovy.transform.Canonical
    2. import io.micronaut.core.annotation.Introspected
    3. @Introspected
    4. @Canonical
    5. class Person {
    6. String name
    7. int age = 18
    8. Person(String name) {
    9. this.name = name
    10. }
    11. }
    1. import io.micronaut.core.annotation.Introspected
    2. @Introspected
    3. data class Person(var name : String) {
    4. var age : Int = 18
    5. }

    Once introspection data has been produced at compilation time you can then retrieve it via the API:

    1. def introspection = BeanIntrospection.getIntrospection(Person) (1)
    2. Person person = introspection.instantiate("John") (2)
    3. println("Hello ${person.name}")
    4. BeanProperty<Person, String> property = introspection.getRequiredProperty("name", String) (3)
    5. property.set(person, "Fred") (4)
    6. String name = property.get(person) (5)
    1. val introspection = BeanIntrospection.getIntrospection(Person::class.java) (1)
    2. val person : Person = introspection.instantiate("John") (2)
    3. print("Hello ${person.name}")
    4. val property : BeanProperty<Person, String> = introspection.getRequiredProperty("name", String::class.java) (3)
    5. val name = property.get(person) (5)
    6. print("Hello ${person.name}")
    1You can retrieve a BeanIntrospection with the getIntrospection static method
    2Once you have a you can instantiate a bean with the instantiate method.
    3A BeanProperty can be retreived from the introspection
    4.. and the set method used to set the property value
    5.. and the get method used to retrieve the property value

    Constructor Methods

    1. import io.micronaut.core.annotation.Creator;
    2. import io.micronaut.core.annotation.Introspected;
    3. import javax.annotation.concurrent.Immutable;
    4. @Introspected
    5. @Immutable
    6. public class Vehicle {
    7. private final String make;
    8. private final String model;
    9. private final int axels;
    10. public Vehicle(String make, String model) {
    11. this(make, model, 2);
    12. }
    13. @Creator (1)
    14. public Vehicle(String make, String model, int axels) {
    15. this.make = make;
    16. this.model = model;
    17. this.axels = axels;
    18. }
    19. public String getMake() {
    20. return make;
    21. }
    22. public String getModel() {
    23. return model;
    24. }
    25. public int getAxels() {
    26. return axels;
    27. }
    28. }
    1. import io.micronaut.core.annotation.Creator
    2. import io.micronaut.core.annotation.Introspected
    3. import javax.annotation.concurrent.Immutable
    4. @Introspected
    5. @Immutable
    6. class Vehicle {
    7. private final String make
    8. private final String model
    9. private final int axels
    10. Vehicle(String make, String model) {
    11. this(make, model, 2)
    12. }
    13. @Creator (1)
    14. Vehicle(String make, String model, int axels) {
    15. this.make = make
    16. this.model = model
    17. this.axels = axels
    18. }
    19. String getMake() {
    20. make
    21. }
    22. String getModel() {
    23. model
    24. }
    25. int getAxels() {
    26. axels
    27. }
    1. import io.micronaut.core.annotation.Creator
    2. import io.micronaut.core.annotation.Introspected
    3. import javax.annotation.concurrent.Immutable
    4. @Introspected
    5. @Immutable
    6. constructor(make: String, model: String) : this(make, model, 2) {}
    7. }
    1The @Creator annotation is used to denote which constructor should be used

    Static Creator Methods

    For the use case of static methods being used to instantiate the class, the @Creator annotation can be applied to those methods to allow for the introspection to execute the method.

    1. import io.micronaut.core.annotation.Creator;
    2. import io.micronaut.core.annotation.Introspected;
    3. import javax.annotation.concurrent.Immutable;
    4. @Introspected
    5. @Immutable
    6. public class Business {
    7. private String name;
    8. private Business(String name) {
    9. this.name = name;
    10. }
    11. @Creator (1)
    12. public static Business forName(String name) {
    13. return new Business(name);
    14. }
    15. public String getName() {
    16. return name;
    17. }
    18. }
    1. import io.micronaut.core.annotation.Creator
    2. import io.micronaut.core.annotation.Introspected
    3. import javax.annotation.concurrent.Immutable
    4. @Introspected
    5. @Immutable
    6. class Business private constructor(val name: String) {
    7. companion object {
    8. @Creator (1)
    9. fun forName(name: String): Business {
    10. return Business(name)
    11. }
    12. }
    13. }
    1The annotation is applied to the static method which will be used to instantiate the class
    There can be multiple “creator” methods annotated. If one exists without arguments, that will be used as the default construction method. The first method with arguments will be used as the primary construction method.

    Enums

    It is possible to introspect enums as well. Simply add the annotation to the enum and it can be constructed through the standard valueOf method.

    Use the @Introspected Annotation on a Configuration Class

    If the class you wish to introspect is already compiled and not under your control an alternative option is to define a configuration class with the classes member of the @Introspected annotation set.

    1. import io.micronaut.core.annotation.Introspected;
    2. @Introspected(classes = Person.class)
    3. public class PersonConfiguration {
    4. }
    1. import io.micronaut.core.annotation.Introspected
    2. @Introspected(classes = Person)
    3. class PersonConfiguration {
    4. }
    1. import io.micronaut.core.annotation.Introspected
    2. @Introspected(classes = [Person::class])
    3. class PersonConfiguration

    In the above example the PersonConfiguration class will generate introspections for the Person class.

    Write an AnnotationMapper to Introspect Existing Annotations

    If there is an existing annotation that you wish to introspect by default you can write an AnnotationMapper.

    The AnnotationMapper should be on the annotation processor classpath.

    A provides raw access to read and write a property value for a given class and does not provide any automatic type conversion.

    It is expected that the values you pass to the set and get methods match the underlying property type otherwise an exception will occur.

    To provide additional type conversion smarts the BeanWrapper interface allows wrapping an existing bean instance and setting and getting properties from the bean, plus performing type conversion as necessary.

    1. final BeanWrapper<Person> wrapper = BeanWrapper.getWrapper(new Person("Fred")); (1)
    2. wrapper.setProperty("age", "20"); (2)
    3. int newAge = wrapper.getRequiredProperty("age", int.class); (3)
    4. System.out.println("Person's age now " + newAge);
    1. final BeanWrapper<Person> wrapper = BeanWrapper.getWrapper(new Person("Fred")) (1)
    2. wrapper.setProperty("age", "20") (2)
    3. int newAge = wrapper.getRequiredProperty("age", Integer) (3)
    4. println("Person's age now $newAge")
    1The getWrapper static method can be used to obtain a for a bean instance.
    2You can set properties and the BeanWrapper will perform type conversion, or throw if conversion is not possible.
    3You can retrieve a property using getRequiredProperty and request the appropriate type. If the property doesn’t exist a IntrospectionException will be thrown and if it cannot be converted a will be thrown.

    Jackson is configured to use the BeanIntrospection API to read and write property values and construct objects resulting in reflection-free serialization/deserialization which is benefitial from a performance perspective and requires less configuring to operate correctly with runtimes such as GraalVM native.

    This feature is enabled by default, you can disable it by setting the jackson.bean-introspection-module configuration to .

    This feature is currently regarded as experimental and may be subject to changes in the future.