Reputation: 2437
On a spring boot 2.4.3 application with Java:
I am using a DTO to construct the JSON response from the domain model of the application. The DTO is just a plain java object.
I am trying to property inject a new class that I created for data transformation using the @Autowired but I get a nullPointerException on the runtime.
@Data
@NoArgsConstructor
public class FetchSupplierDTO {
private long id;
private String name;
private String description;
private String info;
private List<String> tags;
@Autowired
private TagTranslation tagTranslation;
public FetchSupplierDTO(SupplierEntity supplier) {
this.id = supplier.getId();
this.name = supplier.getDisplayName();
this.description = supplier.getGivenDescription();
this.info = supplier.getInfo();
if (supplier.getTags() != null) {
this.tags = tagTranslation.extractTagsFromEntity(supplier.getTags());
}
}
}
@Service
public class TagTranslation {
public List<String> extractTagsFromEntity(List<TagEntity> tagEntityList) {
List<String> tagStringList = new ArrayList<>();
tagEntityList.forEach(productTag -> { tagStringList.add(productTag.getTag()); });
return tagStringList;
}
}
Upvotes: 0
Views: 2447
Reputation: 126
First of all, looking at the current code I would design it so that the caller of the constructor is responsible for calling the autowired service. Then the DTO really stays a DTO and is not at the same time responsible for calling a service.
If there is really no way around calling a Spring component from inside a DTO then you will have to get the component manually. And call SpringBeanLocator.getBean(TagTranslation.class)
to get that component and insert it in your field.
Spring holds a single instance of each component, on initialization it scans for autowired annotations within annotated classes (@Component, @Service) and initializes those fields. Once you call the constructor of such a class separately, it will not return the instance that is maintained by Spring, it will construct a new instance. Therefore it's autowired fields will be null.
public class SpringBeanLocator implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public final void setApplicationContext(ApplicationContext context) {
validate(getInternalContext());
setContext(context);
}
public static <T> T getBean(Class<T> type) {
context.getBean(type);
}
}
Upvotes: 1