Search code examples
javadependency-injectionstaticmicronaut

How to inject a static field in micronaut 2?


I have a factory class that creates an object. In my case, this is ObjectMapper (Jackson).

@Factory
public class JacksonFactory {

  @Singleton
  public ObjectMapper getObjectMapper() {
    return new ObjectMapper().configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
  }

}

I want to inject it into a static field of the class:

public class JsonString {

  @Inject
  private static ObjectMapper mapper;

  public static String of(Object object) {
    try {
      return mapper.writeValueAsString(object);
    } catch (JsonProcessingException exception) {
      throw new RuntimeException();
    }
  }

}

But, it doesn't work that way. I get a NullPointer every time I access the mapper static field. If I make the methods and the field non-static everything works well.

I didn’t find an explanation for this in the documentation, maybe I’m missing something or what is the problem with dependency injection?

Many thanks for the help! :)

UPDATE

Ok guys. I am not creating a static class, but creating an object and I want to inject it as a dependency into all objects that will be created. Why is the dependency not injected when I create inheritors of the JsonDto object?

@Singleton
public class JsonString {

  @Inject
  private ObjectMapper mapper;

  public String of(Object object) {
    try {
      return mapper.writeValueAsString(this);
    } catch (JsonProcessingException exception) {
      throw new KyivAirException(exception);
    }
  }

}

I thought that every time when creating an object, Micronaut will find this bin in the context and substitute it. But that doesn't seem to be how it works. I am completely confused -_\

public abstract class JsonDto {

  @Inject
  private JsonString jsonString;

  @Override
  public String toString() {
    return jsonString.of(this);
  }

}

Solution

  • i suggest try something like that:

    First create your object converter which will take of converting the objects

    @Singleton
    public class ObjectConverter (private ObjectMapper mapper) {
    
      public String of(Object object) {
        try {
          return mapper.writeValueAsString(this);
        } catch (JsonProcessingException exception) {
          throw new RuntimeException(exception);
        }
      }
    
    }
    

    Than have your dto like that (i prefer constructor injection over @inject)

    public abstract class MyDto(private ObjectConverter converter)  {
        
      @Override
      public String toString() {
        return converter.of(this);
      }
    
    }
    

    And finally your service, taking care of the dtos

    @Singleton
    public class MyDtoService(private ObjectConverter mapper) {
    
      public String get(Long id) {
      // TODO get the object from somewhere
      return mapper.of(dto)
      }
    
    }
    

    If we are talking about a dto that comes from the database i suggest something like that

    interface MyJsonDbDto {
       String decode(ObjectConverter mapper) {
         return mapper.of(this)
       }
    }
    
    public class MyDbDto implements MyJsonDbDto{
    
    }
    

    and the service therefore

    public class MyDbDtoService(private ObjectConverter converter, private MyDbDtoRepository repository)
          public String get(Long id) {
      // TODO get the object from repository
      return dto.decode(converter)
      }
    }
    

    hope that helps and sorry i am mainly in kotlin, therefore my java is a little rusty ;-)

    EDIT:

    regarding your comment, i have an ugly idea, which could be considered as a some kind of intermediate solution for your refactoring process

    create the service like that (use context, cause its not lazy loaded like a singleton)

    @Context
    public class MyDtoService {
    
      public static ObjectConverter mapper;
    
      public MyDtoService(ObjectConverter mapper) {
         MyDtoService.mapper = mapper;
      }
    
    }
    

    after that you can access it whithin your dto

    public class MyDto {
        
      @Override
      public String toString() {
        return MyDtoService.mapper.of(this);
      }
    
    }