在运行时通过条件进行依赖注入
Dependency injection by the condition in the runtime
假设有如下代码:
@RestController
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
class Controller {
/* inject here */
public @NonNull GenericService service;
public void doo(List<GenericDTO> list, String type) {
service.doo(list);
}
}
class GenericDTO { ... }
class GenericService { ... }
class OfferService extends GenericService { ... }
class OtherService extends GenericService { ... }
如果 doo
方法的 type
等于 "offer"
,我想向 service
注入一个 OfferService
实例。如果我收到 "other"
,我必须使用 OtherService
,依此类推。这可能吗?
我假设你所有的 bean 都是单例。
单例 bean 在应用程序启动时发生的上下文初始化期间全部初始化和注入,即。您的 doo
方法(我假设它是一个处理程序方法)尚未被调用。 Spring 到 guess/know 无法调用某些方法的可能值是什么,或者这些值应该如何以任何方式影响目标注入点。
也许代理可以在调用 doo
期间帮助确定要获取的真实 bean。这很困难,因为您必须以某种方式将 type
的值暴露给执行解析的任何组件。 Spring 开箱即用。您必须编写自己的作品来执行此操作,可能需要借助您自己的 Scope
实现。 (这不是微不足道的。)
最简单的解决方案是在 type
上同时注入两个服务和 if-else
/switch
,然后相应地选择一个。或者将两个服务放在 Map
中,type
是键。
每当我需要在运行时选择实现时,我都会使用以下 2 种模式之一来完成此操作。这绝对不是您要的 - bean 不是有条件地注入的,而是有条件地调用的。
此外,如果您有一小部分 GenericService 实现,这将很优雅。如果它是一个更大的集合或者如果 class 层次结构更深,那么这可能不是那么优雅的解决方案。
但是,这实现了在添加新的 GenericService 实现时不必更改调用代码的目标,同时还使您能够将不同的服务实现子集注入到不同的控制器中。
Class MyController {
Map<String, GenericService> servicesMap = new HashMap<String, GenericService>();
@Autowired
public setServices(List<GenericService> serviceList) {
for (GenericService service : serviceList) {
if (service instanceof OfferService) {
serviceMap.put("offer", service);
}
if (service instanceof OtherService) {
serviceMap.put("other", service);
}
}
public void doo(List<GenericDTO> list, String type) {
GenericService service = serviceMap.get(type);
if (null != service) {
service.doo(list);
}
}
}
另一种可能且可能更优雅的解决方案是让实现 class 指明它实际支持的类型。
Class GenericService {
// This method could very well be an abstract method or a simple
// getter for a property set through ctor
public String getType() {
return "unknown"; // or "generic" if you please
}
}
Class OfferClass extends GenericService {
@Override
public String getType() {
return "offer";
}
}
Class OtherClass extends GenericService {
@Override
public String getType() {
return "other";
}
}
Class MyController {
Map<String, GenericService> servicesMap = new HashMap<String, GenericService>();
// This is just there so that we do not have to process the
// list everytime we need to invoke the service
@Autowired
public setServices(List<GenericService> serviceList) {
for (GenericService service : serviceList) {
serviceMap.put(service.getType(), service)
}
}
public void doo(List<GenericDTO> list, String type) {
GenericService service = serviceMap.get(type);
if (null != service) {
service.doo(list);
}
}
}
假设有如下代码:
@RestController
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
class Controller {
/* inject here */
public @NonNull GenericService service;
public void doo(List<GenericDTO> list, String type) {
service.doo(list);
}
}
class GenericDTO { ... }
class GenericService { ... }
class OfferService extends GenericService { ... }
class OtherService extends GenericService { ... }
如果 doo
方法的 type
等于 "offer"
,我想向 service
注入一个 OfferService
实例。如果我收到 "other"
,我必须使用 OtherService
,依此类推。这可能吗?
我假设你所有的 bean 都是单例。
单例 bean 在应用程序启动时发生的上下文初始化期间全部初始化和注入,即。您的 doo
方法(我假设它是一个处理程序方法)尚未被调用。 Spring 到 guess/know 无法调用某些方法的可能值是什么,或者这些值应该如何以任何方式影响目标注入点。
也许代理可以在调用 doo
期间帮助确定要获取的真实 bean。这很困难,因为您必须以某种方式将 type
的值暴露给执行解析的任何组件。 Spring 开箱即用。您必须编写自己的作品来执行此操作,可能需要借助您自己的 Scope
实现。 (这不是微不足道的。)
最简单的解决方案是在 type
上同时注入两个服务和 if-else
/switch
,然后相应地选择一个。或者将两个服务放在 Map
中,type
是键。
每当我需要在运行时选择实现时,我都会使用以下 2 种模式之一来完成此操作。这绝对不是您要的 - bean 不是有条件地注入的,而是有条件地调用的。
此外,如果您有一小部分 GenericService 实现,这将很优雅。如果它是一个更大的集合或者如果 class 层次结构更深,那么这可能不是那么优雅的解决方案。
但是,这实现了在添加新的 GenericService 实现时不必更改调用代码的目标,同时还使您能够将不同的服务实现子集注入到不同的控制器中。
Class MyController {
Map<String, GenericService> servicesMap = new HashMap<String, GenericService>();
@Autowired
public setServices(List<GenericService> serviceList) {
for (GenericService service : serviceList) {
if (service instanceof OfferService) {
serviceMap.put("offer", service);
}
if (service instanceof OtherService) {
serviceMap.put("other", service);
}
}
public void doo(List<GenericDTO> list, String type) {
GenericService service = serviceMap.get(type);
if (null != service) {
service.doo(list);
}
}
}
另一种可能且可能更优雅的解决方案是让实现 class 指明它实际支持的类型。
Class GenericService {
// This method could very well be an abstract method or a simple
// getter for a property set through ctor
public String getType() {
return "unknown"; // or "generic" if you please
}
}
Class OfferClass extends GenericService {
@Override
public String getType() {
return "offer";
}
}
Class OtherClass extends GenericService {
@Override
public String getType() {
return "other";
}
}
Class MyController {
Map<String, GenericService> servicesMap = new HashMap<String, GenericService>();
// This is just there so that we do not have to process the
// list everytime we need to invoke the service
@Autowired
public setServices(List<GenericService> serviceList) {
for (GenericService service : serviceList) {
serviceMap.put(service.getType(), service)
}
}
public void doo(List<GenericDTO> list, String type) {
GenericService service = serviceMap.get(type);
if (null != service) {
service.doo(list);
}
}
}