【问题标题】:Constructor injection on abstract class and children抽象类和子类的构造函数注入
【发布时间】:2019-04-02 15:23:53
【问题描述】:

我有一堂课如下

@Component
public abstract class NotificationCenter {
    protected final EmailService emailService;
    protected final Logger log = LoggerFactory.getLogger(getClass());

    protected NotificationCenter(EmailService emailService) {
        this.emailService = emailService;
    }

    protected void notifyOverEmail(String email, String message) {
        //do some work
        emailService.send(email, message);
    }
}

EmailService@Service,应该通过构造函数注入自动连接。

现在我有一个扩展 NotificationCenter 的类,还应该自动连接组件

@Service
public class NotificationCenterA extends NotificationCenter {    
    private final TemplateBuildingService templateBuildingService;

    public NotificationCenterA(TemplateBuildingService templateBuildingService) {
        this.templateBuildingService = templateBuildingService;
    }
}

基于上面的示例,代码不会编译,因为抽象类 NotificationCenter 中没有默认构造函数,除非我将 super(emailService); 作为第一条语句添加到 NotificationCenterA 构造函数但我没有实例emailService 中的一个,并且我不打算从孩子中填充基本字段。

知道处理这种情况的正确方法是什么吗?也许我应该使用场注入?

【问题讨论】:

  • 两者都需要,对吧?注入EmailServiceTemplateBuildingService 的兼容实例,然后为EmailService 调用super。我没有看到任何其他方式。

标签: java spring spring-mvc spring-boot dependency-injection


【解决方案1】:

你也可以使用@Lookup注解来实现。

public abstract class NotificationCenter {
    protected final Logger log = LoggerFactory.getLogger(getClass());

    @Lookup
    protected EmailService getEmailService() {
        return null; // Spring will implement this @Lookup method using a proxy
    }

    protected void notifyOverEmail(String email, String message) {
        //do some work
        EmailService emailService = getEmailService(); // This is return the service bean.
        emailService.send(email, message);
    }
}

【讨论】:

    【解决方案2】:

    第一点:

    @Component 并非设计用于您将显式实现的抽象类。抽象类不能是组件,因为它是抽象的。
    删除它并考虑下一点。

    第二点:

    我不打算从儿童填充基本字段。

    没有 Spring 和 DI,您可以直接在父类中对依赖项进行硬编码,但这是否可取?并不真地。 它隐藏了依赖关系,也使得切换到任何子类甚至测试的另一个实现变得更加复杂。
    因此,正确的方法是在子类中注入依赖项,并在父构造函数中传递注入的 EmailService:

    @Service
    public class NotificationCenterA extends NotificationCenter {    
        private final TemplateBuildingService templateBuildingService;    
    
        public NotificationCenterA(TemplateBuildingService templateBuildingService, EmailService emailService) {
            super(emailService);
            this.templateBuildingService = templateBuildingService;
        }
    }
    

    并且在父类中只需删除无用的@Component 注释。

    知道处理这种情况的正确方法是什么吗?也许我 应该使用场注入?

    不会,它只会使您的代码更难测试/更灵活和更清晰。

    【讨论】:

    • 解决了我的困惑的要点,感谢您的撰写。
    【解决方案3】:

    NotificationCenter 不是一个真正的类而是一个抽象类,所以你不能创建它的实例。另一方面,它有一个必须在构造函数中初始化的字段(最终字段!)EmailService! Setter 在这里不起作用,因为 final 字段只被初始化一次。它是 Java,甚至不是 Spring。

    任何扩展 NotificationCenter 的类继承字段 EmailService 因为这个孩子“是”通知中心

    因此,您必须提供一个构造函数来获取电子邮件服务的实例并将其传递给 super 进行初始化。又是 Java,不是 Spring。

    public class NotificationCenterA extends NotificationCenter {    
        private final TemplateBuildingService templateBuildingService;
    
        public NotificationCenterA(EmailService emailService, TemplateBuildingService templateBuildingService) {
           super(emailService);
           this.templateBuildingService = templateBuildingService;
        }
    } 
    

    现在 spring 为你管理 bean,它初始化它们并注入依赖项。 你写的东西坦白说我看不懂:

    ...作为 NotificationCenterA 构造函数的第一条语句,但我没有 emailService 的实例,并且我不打算从子级填充基本字段。

    但是 Spring 将只管理一个 NotificationCenterA bean(当然还有 EmailService 实现),它不管理抽象类,并且由于 Java 设置了上述限制(出于某种原因),我认为直接回答你的问题是:

    1. 在这种情况下,您不能使用 setter 注入(同样,因为 final,它是 Java,而不是因为 Spring)
    2. 构造函数注入,在一般情况下比 setter 注入更好,可以完全处理您的情况

    【讨论】:

    • 感谢您的澄清。一个问题,如果我将抽象类切换到具体类,我还需要从子类填充 emailService 吗?
    • 是的,如果你创建一个子类,当然现在你可以“自己”创建一个父类,但是在这种情况下,根本不考虑子类
    • 如果我将NotificationCenter 设为类而不是抽象类,我仍然需要调用超级构造函数为其提供 emailService.. 所以我试图避免的主要事情并没有被避免.您能否解释一下为什么在这种情况下根本不考虑子类?
    • 父类可以被创建而不管子类(只要它不是抽象的)。子类必须考虑到父级的状态(字段)...
    【解决方案4】:

    使用字段注入将是可行的方法,因为您提到您不想在子类中使用 emailService

    您可以尝试的另一种方法是将EmailService bean 注入NotificationCenterA 构造函数,然后将其传递给super(emailService)

    所以,应该是这样的:

            @Autowired
            public NotificationCenterA(EmailService emailService, TemplateBuildingService templateBuildingService) {
                super(emailService);
                this.templateBuildingService = templateBuildingService;
            }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-25
      • 1970-01-01
      • 1970-01-01
      • 2010-11-26
      • 2023-03-04
      相关资源
      最近更新 更多