首页 > 解决方案 > 避免 Spring Boot 中的长依赖列表

问题描述

我正在编写具有越来越多的接口实现的代码VendorService。现在,在使用这些服务的地方,我们在构造函数中将它们全部自动装配,从而导致一长串依赖项。当重复使用单个接口时,是否有首选方法来处理依赖关系?

目前的做法:

private final VendorService xVendorService;
private final VendorService yVendorService;
private final VendorService zVendorService;
...

@Autowired
public VendorDelegateService(XVendorService xVendorService, 
                             YVendorService yVendorService, 
                             ZVendorService zVendorService, 
                             ...) {
    this.xVendorService = xVendorService;
    this.yVendorService = yVendorService;
    this.yVendorService = yVendorService;
    ...
}

public void doSomething(VendorId vendorId) {
    if (vendorId = VendorId.X) {
        xVendorService.doSomething();
    } else if (vendorId = VendorId.Y) {
        yVendorService.doSomething();
    } else if (vendorId = VendorId.Z) {
        zVendorService.doSomething();
    } 
    ...
}

显然,这是非常冗长的,并且每当创建接口的新实现时都需要更新。

另一种方法是从 ApplicationContext 获取 Bean,例如:

private final ApplicationContext context;

@Autowired
public VendorDelegateService(ApplicationContext context) {
    this.context = context;
}

public void doSomething(VendorId vendorId) {
    context.getBean(VendorService.class, vendorId.name()).doSomething();
}

这不需要每个新实现都使用另一个 if/else 括号,但它很钝并且感觉不正确。这个逻辑当然可以在它自己的类中抽象出来以减轻这个问题。

这些在 Spring 和 Java 中哪个更惯用?还有其他我没有考虑过的方法吗?

标签: javaspring-bootdependency-injection

解决方案


我觉得是否有一种惯用的方式是一个偏好问题,但我建议的是以下解决方案:

为所有服务创建一个接口,我们可以称之为VendorService

public interface VendorService {
    void doSomething();
    VendorId getVendorId();
}

现在我们希望为所有服务实现这个接口,例如,可以这样完成XVendorService

@Service
public XVendorService implements VendorService {
    private VendorId vendorId = ....    

    @Override
    public void doSomething() {
        ...
    }

    @Override
    public VendorId getKey() {
        return vendorId;
    }
}

现在VendorDelegateService我们可以这样做:

@Service
public class VendorDelegateService {
    private Map<VendorId, VendorService> services = new HashMap<>();

    @Autowired
    public AllServices(Set<? extends VendorService> serviceSet) {
        serviceSet.stream().forEach(service -> services.put(service.getVendorId(), service));
    }

    public void doSomething(VendorId vendorId) {
        if (services.containsKey(vendorId)) {
            services.get(vendorId).doSomething();
        }
    }
}

请注意,Set<? extends VendorService> serviceSet所有服务都将自动自动连接。通过之后创建一个地图,我们可以将我们的请求发送到基于它的每个服务vendorKey


推荐阅读