Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

dependency injection in factory method causes NullPointerException

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.

FormOperation

I created an interface.

public interface FormOperation {

    public  Map<String, Object> fetchLevelsInfo(long levelId);

    public  Object fetchCurrentTargetLevel(long levelId);
}

QuarterLevelStudentInternship

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();
    }

}

QuarterLevelMou

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();
    }

}

FormOperationFactory

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;
    }
}

QuarterLevelOperations

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;

    }
}
like image 590
dhyanandra singh Avatar asked Oct 19 '25 16:10

dhyanandra singh


1 Answers

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;
    }
}
like image 90
davidxxx Avatar answered Oct 21 '25 06:10

davidxxx



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!