0

Is there a way to inject dependencies into POJOs supplied by Spring RestControllers? For example, if you wanted to implement polymorphic behavior.

The following example fails with a NullPointerExcetion because lowerCaseService is not injected into the Example POJO:

@RestController
public class ExampleController {

  @PostMapping("/example")
  String example(@RequestBody Example example) {
    return example.valueToLowerCase();
  }

}

@Data
@NoArgsConstructor
public class Example {

  private String value;

  @Autowired
  private LowerCaseService lowerCaseService;

  public String valueToLowerCase() {
    return lowerCaseService.toLowerCase(getValue());
  }

}

@Service
public class LowerCaseService {
  public String toLowerCase(String value) {
    return value != null ? value.toLowerCase() : null;
  }
}

Note that this contrived example is intentionally simple and doesn't need polymorphic behavior. I created it this way to help responders make quick sense of it and not get bogged down by Jackson's annotations. In my actual use case Jackson will produce subclasses of Example, where each needs to do very different things, with different dependencies.

3 Answers3

1

By definition, a POJO (Plain Old Java Object), is a normal Java object class (that is, not a JavaBean, EntityBean etc.) and does not serve any other special role nor does it implement any special interfaces of any of the Java frameworks. This term was coined by Martin Fowler, Rebbecca Parsons and Josh MacKenzie who believed that by creating the acronym POJO, such objects would have a "fancy name", thereby convincing people that they were worthy of use. Link : https://www.webopedia.com/TERM/P/POJO.html

In other words, the POJO should only contain attributes and nothing else.

I think in this case, we can tackle the problem by injecting the service into the controller method.

@RestController
public class ExampleController {
  @Autowired
  private LowerCaseService lowerCaseService;

  @PostMapping("/example")
  String example(@RequestBody Example example) {
    return lowerCaseService. toLowerCase(example.getValue);
  }

}
Thina Garan
  • 106
  • 5
0

The simple answer is no, you can't, because dependencies are injected when the bean is instantiated in Spring's Context, and this POJO is just a instance mapped (probably) by Jackson.

This answer has a bit more info about it: How does autowiring work in Spring?

But peharps more important than that is an architectural principle, you should really not place your business service in your outer model (Example), as it's clearly an separation of concerns violation.

You should inject your service in the controller class and pass the DTO as a parameter to its method.

Hope it's helpful!

Tomaz Fernandes
  • 2,429
  • 2
  • 14
  • 18
0

You can achieve what you are trying to do implementing your own RequestBodyAdviceAdapter... basically there are 3 steps involved:

  1. Create a class that extends RequestBodyAdviceAdapter and implements ApplicationContextAware (so u have access to the application context).
  2. Implement the supports() method:
return ((Class) targetType).isAssignableFrom(Example.class);
  1. Override the afterRead() methods:
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        final Example example = (Example) super.afterBodyRead(body, inputMessage, parameter, targetType, converterType);
        final AutowireCapableBeanFactory autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
        autowireCapableBeanFactory.autowireBeanProperties(example, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);
        return example;
}
Mauro Monti
  • 1,068
  • 2
  • 12
  • 21