I am using Spring Boot
and trying to implement factory design pattern in it. The issue is that when the objects of QuarterLevelStudentInternship
and QuarterLevelMou
are created, the autowired dependencies declared inside those classes are set to null, so it throws me NullPointerException
.
I created an interface.
public interface FormOperation {
public Map<String, Object> fetchLevelsInfo(long levelId);
public Object fetchCurrentTargetLevel(long levelId);
}
This class implements FormOperation interface
@Component
public class QuarterLevelStudentInternship implements FormOperation {
@Autowired /*@Lazy*/
StudentInternshipService internshipService;
@Autowired /*@Lazy*/
StudentInternshipRecordService internshipRecordService;
// these two objects are showing null at the time of object generation and cause null pointer exception
@Override
public Map<String, Object> fetchLevelsInfo(long levelId) {
HashMap<String, Object> levels = new HashMap<>();
levels.put("internshipDetails", internshipService.fetchStudentInternship(levelId));
List<Map<String, Object>> internshipRecord = internshipRecordService.fetchStudentInternshipRecords(levelId);
levels.put("internshipRecord", internshipRecord);
levels.put("internshipGraph", internshipRecordService.fetchInternshipRecordsGroupbyOrganization(levelId));
levels.put("currentTargetLevel", internshipRecord.size());
return levels;
}
@Override
public Object fetchCurrentTargetLevel(long levelId) {
List<Map<String, Object>> internshipRecord = internshipRecordService.fetchStudentInternshipRecords(levelId);
return internshipRecord.size();
}
}
This class implements FormOperation interface
@Component
public class QuarterLevelMou implements FormOperation {
@Autowired /*@Lazy*/
MouServices mouService;
@Override
public Map<String, Object> fetchLevelsInfo(long levelId) {
HashMap<String, Object> levels = new HashMap<>();
List<Map<String, Object>> mouRecord = mouService.fetchMouResult(levelId);
levels.put("mouDetails", mouRecord);
levels.put("currentTargetLevel", mouRecord.size());
return levels;
}
@Override
public Object fetchCurrentTargetLevel(long levelId) {
List<Map<String, Object>> mouRecord = mouService.fetchMouResult(levelId);
return mouRecord.size();
}
}
It's a factory class which generate object based on evidanceForm
@Component
public class FormOperationFactory {
public FormOperation createFormOperation(String evidanceForm) {
if (evidanceForm.equals("Student Internship Form"))
return new QuarterLevelStudentInternship();
else if (evidanceForm.equals("MOUS"))
return new QuarterLevelMou();
return null;
}
}
It's my service class
@Service("quarterLevelOperations")
@Transactional
public class QuarterLevelOperations {
@Autowired @Lazy
QuarterLevelResultService resultService;
public List<Map<String, Object>> fetchLevelsInfoForForms(
List<Map<String, Object>> quarterLevels, String evidanceForm,
String year, boolean direction, Long quarterId) {
FormOperationFactory formOperationFactory = new FormOperationFactory();
for(Map<String, Object> levels :quarterLevels) {
//quarterLevels.forEach(levels -> {
long levelId = Long.parseLong(levels.get("id").toString());
if (evidanceForm == null) {
levels.put("evidance", resultService.fetchQuaterLevelResultEvidance(levelId));
}
else if (evidanceForm.equals("Student Internship Form")) {
FormOperation operation = formOperationFactory.createFormOperation(evidanceForm);
levels.putAll(operation.fetchLevelsInfo(levelId));
}
else if (evidanceForm.equals("MOUS")) {
FormOperation operation = formOperationFactory.createFormOperation(evidanceForm);
levels.putAll(operation.fetchLevelsInfo(levelId));
}
} //);
return quarterLevels;
}
}
The FormOperation
instances you are created in the FormOperationFactory
class are not Spring Beans but only Java objects created with the new
operator.
Besides, these classes (QuarterLevelMou and QuarterLevelStudentInternship) define Spring dependencies but are not defined as Spring beans either.
All that matter as the Spring dependency autowiring is designed to work with Spring beans.
About your requirement, you should note that the FormOperation
subclasses are immutable. I don't see why you need to create a new instance at each invocation of the factory method.
Instead, you could not make them singleton.
So I advise you to make the factory and the classes that it instances as Spring singletons.
Then in FormOperationFactory
, inject the two instances that the factory has to create.
At last in the factory method return the one or the other instance according to the parameter passed by the client.
@Component
public class FormOperationFactory {
@Autowired
private QuarterLevelMou quarterLevelMou;
@Autowired
private QuarterLevelStudentInternship quarterLevelStudentInternship ;
public FormOperation createFormOperation(String evidanceForm) {
if (evidanceForm.equals("Student Internship Form"))
return quarterLevelStudentInternship;
else if (evidanceForm.equals("MOUS"))
return quarterLevelMou;
return null;
}
}
And :
@Component
public class QuarterLevelMou implements FormOperation { ...}
And :
@Component
public class QuarterLevelStudentInternship implements FormOperation {
Additionally, as you really need to inject dependencies in an object that is not a Spring bean, you can as proposed by @Simon Berthiaume to inject a AutowireCapableBeanFactory
instance in your factory bean and to use it to inject dependencies.
For example :
@Component
public class FormOperationFactory {
@Autowired
private AutowireCapableBeanFactory beanFactory;
public FormOperation createFormOperation(String evidanceForm) {
FormOperation formOperation = null;
if (evidanceForm.equals("Student Internship Form"))
formOperation = new QuarterLevelStudentInternship();
else if (evidanceForm.equals("MOUS"))
formOperation = new QuarterLevelMou();
if (formOperation != null){
beanFactory.autowireBean(formOperation);
}
return formOperation;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With