【问题标题】:dependency injection in factory method causes NullPointerException工厂方法中的依赖注入导致 NullPointerException
【发布时间】:2018-08-18 17:44:49
【问题描述】:

我正在使用Spring Boot 并尝试在其中实现工厂设计模式。问题是当创建QuarterLevelStudentInternshipQuarterLevelMou 的对象时,这些类中声明的自动装配依赖项设置为null,所以它抛出了NullPointerException

表单操作

我创建了一个界面。

public interface FormOperation {

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

    public  Object fetchCurrentTargetLevel(long levelId);
}

QuarterLevelStudentInternship

该类实现FormOperation接口

@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

该类实现FormOperation接口

@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

​​>

它是一个基于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

这是我的服务类

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

    }
}

【问题讨论】:

    标签: java spring spring-boot factory autowired


    【解决方案1】:

    正如@davidxxx 建议的那样,我以春天的方式实现了这个问题.. 它正在工作。

    @Component("quarterLevelStudentInternship")
            public class QuarterLevelStudentInternship implements FormOperation {....}
    
            @Component("quarterLevelMou")
            public class QuarterLevelMou implements FormOperation  {.....}
    
            @Service("quarterLevelOperations")
            @Transactional
            public class QuarterLevelOperations {
    
                @Autowired  /*@Lazy*/
                @Qualifier("quarterLevelStudentInternship")
                FormOperation internshipOperation;
    
                @Autowired  /*@Lazy*/
                @Qualifier("quarterLevelMou")
                FormOperation mouOperation;
    
                @Autowired  @Lazy
                QuarterLevelResultService resultService;
    
                public List<Map<String, Object>> fetchLevelsInfoForForms(
                     List<Map<String, Object>> quarterLevels, String evidanceForm, 
                                    String year, boolean direction, Long quarterId) {
    
                      for(Map<String, Object> levels :quarterLevels) {
                         long levelId = Long.parseLong(levels.get("id").toString());
                         if (evidanceForm == null) { 
                               levels.put("evidance", resultService.fetchQuaterLevelResultEvidance(levelId));
                              }
                              else if (evidanceForm.equals("Student Internship Form")) {
                                    levels.putAll(internshipOperation.fetchLevelsInfo(levelId));
                              }
                              else if (evidanceForm.equals("MOUS")) {
                                    levels.putAll(mouOperation.fetchLevelsInfo(levelId));
                              }
                          } 
    
                return quarterLevels;
             }
        }
    

    【讨论】:

      【解决方案2】:

      您在FormOperationFactory 类中创建的FormOperation 实例不是Spring Bean,而只是使用new 运算符创建的Java 对象。
      此外,这些类(QuarterLevelMou 和 QuarterLevelStudentInternship)定义了 Spring 依赖项,但也没有定义为 Spring bean。

      所有这些都非常重要,因为 Spring 依赖项自动装配旨在与 Spring bean 一起使用

      关于您的要求,您应该注意FormOperation 子类是不可变的。我不明白为什么您需要在每次调用工厂方法时创建一个新实例。
      相反,您不能将它们设为单例。

      因此,我建议您将工厂及其实例化的类设为 Spring 单例。
      然后在FormOperationFactory 中,注入工厂必须创建的两个实例。 最后在工厂方法中根据客户端传递的参数返回一个或另一个实例。

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

      还有:

      @Component
      public class QuarterLevelMou implements FormOperation  { ...}
      

      还有:

      @Component
      public class QuarterLevelStudentInternship implements FormOperation {
      

      此外,由于您确实需要在不是 Spring bean 的对象中注入依赖项,您可以按照@Simon Berthiaume 向inject a AutowireCapableBeanFactory instance in your factory bean and to use it to inject dependencies 提出的建议。

      例如:

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

      【讨论】:

      • 真的很抱歉,@Component 存在于我的解决方案中我忘了提及它。
      • @dhyanandra singh 没关系。其余内容均适用。
      • 我同意 OP 应该考虑他是否真的需要自己实例化这些类,或者这只是他出于对 Spring 的有限理解而做的事情(我们都去过那里,没有判断力)。如果他真的需要自己创建新实例并且需要为他处理注入,他应该看看AutowireCapableBeanFactory.autowireBean(Object)
      • @Simon Berthiaume 很好的参考。非常感谢,我看着它,但我没能找到!我更新了。
      • @davidxxx 我尝试了你建议的实现,但仍然面临同样的问题,因为@Autowired private AutowireCapableBeanFactory beanFactory;设置为 null 并导致 NullPointerException 作为其他依赖项。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-08-09
      • 1970-01-01
      • 2020-03-18
      • 1970-01-01
      • 1970-01-01
      • 2023-03-20
      • 1970-01-01
      相关资源
      最近更新 更多