构造函数注入抽象类和儿童

pre*_*oid 5 java spring dependency-injection spring-mvc spring-boot

我的课如下

@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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

    public NotificationCenterA(TemplateBuildingService templateBuildingService) {
        this.templateBuildingService = templateBuildingService;
    }
}
Run Code Online (Sandbox Code Playgroud)

根据上面的示例,该代码将不会编译,因为抽象类中没有默认的构造函数,NotificationCenter除非我将super(emailService);第一个语句添加到NotificationCenterA构造函数中,但是我没有的实例,emailService并且我也不想填充基数孩子们的田野。

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

dav*_*xxx 8

第一点:

@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

并非如此,它只会使您的代码不太可测试/灵活和清晰。


Mar*_*nik 5

NotificationCenter不是一个真正的类,而是一个抽象类,因此您不能创建它的实例。另一方面,它具有EmailService必须在构造函数中初始化的字段(最终字段!)!设置器在这里不起作用,因为最终字段仅被初始化一次。它是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;
    }
} 
Run Code Online (Sandbox Code Playgroud)

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

...作为对NotificationCenterA构造函数的第一条声明,但我没有emailService的实例,也不打算从子级中填充基字段。

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

  1. 在这种情况下,您不能使用setter注入(同样,因为final,它是Java,而不是因为Spring)
  2. 通常情况下,构造函数注入要比setter注入更好