0
votes

I am working on a spring boot application. I have the below utility class which does conversion from a string to Json object.


public class JsonParser {
  private static ObjectMapper objMapper;
    
    
    private static ObjectMapper getMapper() {
        if (objMapper == null) {
            objMapper = new ObjectMapper();
        }
        return objMapper;
    }
  

  public static <K> K fromJsonToObj(String json, Class<K> kClass) throws IOException {
    return mapper.readValue(json, kClass);
  }
}

And in one of my classes I am using it as below

User user = JsonParser.fromJsonToObj(jsonString, User.class);

But I was told that above approach is not a very good practice,

The best practices for the Jackson Objectmapper suggest only a single instance of the mapper should be created within the application. Part of spring uses jackson to deserialize and serialize the request body attached to requests, so it already has one. To allow Jackson’s best practices to be followed, spring exposed the ObjectMapper as a bean so it can be accessed in other locations. We want to use this object mapper rather than create our own. This also allows us to configure a single object mapper rather than configure multiple object mappers in case we need additional configurations on top of base ObjectMapper.

So if we do need an objectMapper in a static utils class, we should pass in the object mapper as a param and in the service layer or anywhere else, we can use:

@Autowired
private ObjectMapper objectMapper;

to get the global spring object mapper.

But I am not sure how to go about with this above suggested approach. Can anyone throw light on what is the correct way? Are there any configurations that I need to provide.

1
And also best practice - is not use @Autowired with class field. Much better is use Dependency Injection (DI) via constructor. - Zogger

1 Answers

1
votes

You just let spring autowire it like this.

@Service
public class YourService {
    public YourService(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }
}

Then when spring boot finds that class it will create a spring bean from it and it will inject the ObjectMapper to the service in the constructor.

When spring boot scans and finds your component it will by default only be added to the context once. So you will get what you want. I would suggest. That instead of creating a singleton class by yourself then you create your own mapper component like this.

@Component
public class MapperComponent {
    public MapperComponent(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }
    
    public static <K> K fromJsonToObj(String json, Class<K> kClass) throws IOException {
         return objectMapper.readValue(json, kClass);
    }
}

And then your service which needs the mapper would look like this.

@Service
public class YourService {
    private MapperComponent mapperComponent
    public YourService(MapperComponent mapperComponent) {
        this.mapperComponent = mapperComponent;
    }
}

You cant access the objectmapper from the spring context from a singleton the way you are trying to do it. But you are right you can just give it to the singleton on every call but do you really need a static class when you can just autowire a component instead?