IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Spring cookbook - Field injection? Constructor injection? Or setter injection?

    10k发表于 2024-04-30 00:00:00
    love 0

    Field injection? Constructor injection? Or setter injection?

    1. What are they

      Different flavors of dependency injection

      ```java class MyComponent {

      @Inject MyCollaborator collaborator;

      public void myBusinessMethod() { collaborator.doSomething(); } } ```

      DI is the means to connect the two.

    2. Why field injection is evil

      Field injection allows dependencies to be null by default

      Here is another way of injection:

      ```java class MyComponent {

      private final MyCollaborator collaborator;

      @Inject public MyComponent(MyCollaborator collaborator) { Assert.notNull(collaborator, "MyCollaborator must not be null!"); this.collaborator = collaborator; }

      public void myBusinessMethod() { collaborator.doSomething(); } } ```

      1. You can only create instances of MyComponents by providing a MyCollaborator. -> enforcing object has a valid state after construction
      2. You communicate mandatory dependencies publicly.(expose the dependencies on the interface)
      3. Final fields ensure that dependencies cannot be changed after object creation, contributing to the overall safety and predictability of the code.

      If you have too many dependencies, field injection can reduce the pain of setter or constructor injection, but, too many dependencies may remind you that you should split the component.

    3. Testability

      Consider the following class using constructor injection:

      ```java public class MyComponent { private final MyCollaborator collaborator;

      public MyComponent(MyCollaborator collaborator) { this.collaborator = collaborator; }

      public void myBusinessMethod() { collaborator.doSomething(); } } ```

      Now, let's write a unit test for the myBusinessMethod() using constructor injection:

      ```java import static org.mockito.Mockito.*;

      public class MyComponentTest {

      @Test public void testMyBusinessMethod() { // Create a mock collaborator MyCollaborator collaboratorMock = mock(MyCollaborator.class);

       // Create an instance of MyComponent with the mock collaborator
       MyComponent component = new MyComponent(collaboratorMock);
      
       // Call the method under test
       component.myBusinessMethod();
      
       // Verify that collaborator's doSomething() method was called
       verify(collaboratorMock).doSomething();
      

      } } ```

      In this example, we use Mockito to create a mock collaborator object. We then instantiate MyComponent with this mock collaborator injected via the constructor. This allows us to isolate the component under test and verify its behavior by asserting that the collaborator's doSomething() method was called.

      Now, let's consider how we would test the same functionality with field injection:

      ```java public class MyComponent { @Inject private MyCollaborator collaborator;

      public void myBusinessMethod() { collaborator.doSomething(); } } ```

      And the corresponding test using field injection:

      ```java import org.junit.Before; import org.junit.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations;

      import static org.mockito.Mockito.*;

      public class MyComponentTest {

      @Mock private MyCollaborator collaboratorMock;

      @InjectMocks private MyComponent component;

      @Before public void setup() { // Initialize mocks MockitoAnnotations.initMocks(this); }

      @Test public void testMyBusinessMethod() { // Call the method under test component.myBusinessMethod();

       // Verify that collaborator's doSomething() method was called
       verify(collaboratorMock).doSomething();
      

      } } ```

      In this example, we use Mockito annotations (@Mock and @InjectMocks) to inject the mock collaborator into the component under test. However, this approach relies on reflection and can be more error-prone and less readable than constructor injection, as it obscures the dependencies being injected.

      Overall, constructor injection provides a more straightforward and reliable approach to testing by explicitly passing dependencies to the class constructor, resulting in cleaner and more maintainable unit tests.

    4. Lombok

      Helps you finish the "tedious" part of contractor injection

    5. Additional - constructor injection vs setter injection

      We usually advise people to use constructor injection for all mandatory collaborators and setter injection for all other properties.


      Setter injection + @Required can be used as constructor injection.

      Constructor injection should be used for mandatory dependencies (which are those who are required for the functionality of the object )and setter injection for optional dependencies

      Constructor injection must pass the dependencies at first while setter injects can be at the time they are used.

    6. Reference : https://odrotbohm.de/2013/11/why-field-injection-is-evil/



沪ICP备19023445号-2号
友情链接