我有一个如下的课程
@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
的实例,我也不想从子域中填充基本字段。
任何想法都可以解决这种情况吗?也许我应该使用场注入?
答案 0 :(得分:4)
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;
}
}
现在spring为您管理bean,它初始化它们并注入依赖项。 坦白地说,你写的东西我听不懂:
...作为NotificationCenterA构造函数的第一条语句,但我没有emailService的实例,也不打算从子级填充基字段。
但是Spring将仅管理NotificationCenterA
bean(当然还有EmailService实现),它不管理抽象类,并且由于Java设置了上述限制(出于某种原因),我认为直接您的问题的答案将是:
答案 1 :(得分: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
注释。
任何想法都可以解决这种情况吗?也许我 应该使用场注入?
不是,这只会使您的代码缺乏可测试性/灵活性和清晰性。
答案 2 :(得分:0)
使用字段注入将是您要走的路,因为您提到不想在子类中使用emailService
。
您可以尝试的另一种方法是将EmailService
bean注入NotificationCenterA
构造函数中,然后将其传递给super(emailService)
。
因此,它将类似于:
@Autowired
public NotificationCenterA(EmailService emailService, TemplateBuildingService templateBuildingService) {
super(emailService);
this.templateBuildingService = templateBuildingService;
}
答案 3 :(得分:0)
您也可以通过使用 @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);
}
}