Spring应当自动装配哪些类(何时使用依赖注入)?


32

我已经在Spring中使用Dependency Injection已有一段时间了,我了解它的工作原理以及使用它的优缺点。但是,当我创建一个新类时,我常常想知道-该类是否应该由Spring IOC Container管理?

而且我不想谈论@Autowired注释,XML配置,setter注入,构造函数注入等之间的区别。我的问题是一个普遍的问题。

假设我们有一个带有Converter的服务:

@Service
public class Service {

    @Autowired
    private Repository repository;

    @Autowired
    private Converter converter;

    public List<CarDto> getAllCars() {
        List<Car> cars = repository.findAll();
        return converter.mapToDto(cars);
    }
}

@Component
public class Converter {

    public CarDto mapToDto(List<Car> cars) {
        return new ArrayList<CarDto>(); // do the mapping here
    }
}

显然,转换器没有任何依赖关系,因此无需自动接线。但是对我来说,自动接线似乎更好。代码更干净,更易于测试。如果我在没有DI的情况下编写此代码,则该服务将如下所示:

@Service
public class Service {

    @Autowired
    private Repository repository;

    public List<CarDto> getAllCars() {
        List<Car> cars = repository.findAll();
        Converter converter = new Converter();
        return converter.mapToDto(cars);
    }
}

现在,要测试它变得更加困难。而且,即使每次转换操作始终处于相同状态,也会为每个转换操作创建新的转换器,这似乎很麻烦。

Spring MVC中有一些众所周知的模式:使用服务的控制器和使用存储库的服务。然后,如果存储库是自动连线的(通常是这样),那么服务也必须自动连线。这很清楚。但是什么时候使用@Component注释?如果您有一些静态util类(例如转换器,映射器),您会自动为其布线吗?

您是否尝试使所有课程自动布线?然后,所有的类依赖关系都易于注入(再次,易于理解和测试)。还是只在绝对必要时才尝试自动接线?

我花了一些时间寻找有关何时使用自动装配的一般规则,但是找不到任何具体提示。通常,人们谈论“您是否使用DI?(是/否)”或“您更喜欢哪种类型的依赖项注入”,但这并不能回答我的问题。

我将不胜感激有关此主题的任何提示!


3
+1代表“什么时候过分?”
安迪·亨特2014年

看看这个问题和这篇文章
Laiv

Answers:


8

同意@ericW的评论,只是想补充一点,您可以使用初始化程序来使代码紧凑:

@Autowired
private Converter converter;

要么

private Converter converter = new Converter();

或者,如果该类真的没有任何状态

private static final Converter CONVERTER = new Converter();

Spring是否应该实例化并注入一个bean的关键标准之一是,那个bean是否如此复杂以至于您想在测试中对其进行模拟?如果是这样,则注入它。例如,如果转换器往返于任何外部系统,则应使其成为组件。或者,如果返回值的决策树很大,并且基于输入有数十种可能的变化,则可以对其进行模拟。依此类推。

您已经很好地汇总了该功能并对其进行了封装,因此,现在只是一个问题,即它是否足够复杂以至于不能被视为单独的测试“单元”。


5

我不认为您必须@Autowired所有类,它应该取决于实际用法,对于您的方案,最好使用静态方法代替@Autowired。对于那些简单的utils类,我看不到使用@Autowired有任何好处,如果使用不当,那绝对会增加Spring容器的成本。


2

我的经验法则是基于您曾说过的一句话:可测试性。问问自己“ 我可以轻松对其进行单元测试吗? ”。如果答案是肯定的,那么在没有其他原因的情况下,我会同意的。因此,如果您在同时开发单元测试,则将省去很多麻烦。

唯一的潜在问题是,如果转换器失败,则服务测试也将失败。有人会说您应该在单元测试中模拟转换器的结果。这样,您将能够更快地发现错误。但这是有代价的:当真正的转换器可以完成工作时,您必须模拟所有转换器的结果。

我还假设根本没有理由使用不同的dto转换器。


0

TL; DR:DI自动装配和DI的构造函数转发的混合方法可以简化您提供的代码。

由于某些weblogic带有spring框架启动错误/涉及@autowired bean初始化依赖项的复杂性,我一直在寻找类似的问题。我已经开始混用另一种DI方法:构造函数转发。它需要您所呈现的条件(“显然,转换器没有任何依赖关系,因此不需要自动接线。”)尽管如此,我还是非常喜欢它的灵活性,而且还是很简单的。

@Service
public class Service {

    @Autowired
    private Repository repository;

    public List<CarDto> getAllCars(Converter converter) {
        List<Car> cars = repository.findAll();
        return converter.mapToDto(cars);
    }
    public List<CarDto> getAllCars() {
        Converter converter = new Converter();
        return getAllCars(converter);
    }
}

甚至只是罗伯的回答

@Service
public class Service {

    @Autowired
    private Repository repository;

    private final Converter converter = new Converter(); // static if safe for that

    public List<CarDto> getAllCars(Converter converter) {
        List<Car> cars = repository.findAll();
        return converter.mapToDto(cars);
    }
    public List<CarDto> getAllCars() {
        return getAllCars(converter);
    }
}

它可能不需要更改公共接口,但我需要。仍然

public List<CarDto> getAllCars(Converter converter) { ... }

可以设置为受保护或私有,以缩小范围(仅出于测试目的/扩展)。

关键是DI是可选的。提供了默认值,可以直接覆盖默认值。随着字段数量的增加,它具有不足之处,但是对于1个字段(可能是2个字段),在以下情况下,我更喜欢这样做:

  • 极少数字段
  • 因为我喜欢一致性,所以几乎总是使用默认值(DI是测试缝)或几乎从未使用默认值(停止间隙),因此这是决策树的一部分,而不是真正的设计约束

最后一点(有点过时,但与我们如何决定@autowired的位置有关):所提出的转换器类是实用程序方法(没有字段,没有构造函数,可以是静态的)。也许解决方案在Cars类中具有某种mapToDto()方法?也就是说,将转换注入推向Cars定义,这可能已经很紧密地联系在一起了:

@Service
public class Service {

   @Autowired
   private Repository repository;

   public List<CarDto> getAllCars() {
    return repository.findAll().stream.map(c -> c.mapToDto()).collect(Collectors.toList()));
   }
}

-1

我认为这是一个很好的例子,例如SecurityUtils或UserUtils。对于任何管理用户的Spring应用程序,您将需要一些带有一堆静态方法的Util类,例如:

getCurrentUser()

isAuthenticated()

isCurrentUserInRole(授权机构)

等等

而且我从来没有自动接线。JHipster(我用它作为最佳实践的一个很好的判断)没有。


-2

我们可以根据该类的功能(例如控制器,服务,存储库,实体)来分离这些类。我们可能仅将其他类用于我们的逻辑,而不仅仅是出于控制器,服务等目的,在这种情况下,我们可以使用@component对该类进行注释。这将在弹簧容器中自动注册该类。如果已注册,则将由spring容器管理。可以将依赖项注入和控制反转的概念提供给该带注释的类。


3
这篇文章很难阅读(文字墙)。您介意将其编辑为更好的形状吗?
蚊蚋
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.