ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Comparison between Field, Setter, and Constructor Injection
    Framework/SPRING 2020. 5. 23. 12:01

    1. Overview

    In the Spring Framework, the Dependency Injection comes in three types. Those are Field Injection, Setter Injection, and Constructor Injection. You can absolutely use any of them and they result in exactly the same outcome. However, based on ease, readability, coding standards, or better coding practices there are few differences. Before we jump into see what those differences are, very quickly, we will see all three types of Dependency Injection in action.

    It is clear that Construction Based Dependency Injection consistently stands better in all of the cases. Even if we look at our class beyond the perspective of Spring Dependency Injection, the Constructor Injection is still the best option.

    2. Instruction

    2.1 Field Injection

    when injecting directly into fields, you provide no direct way of instantiating the class with all its required dependencies. That means:

    • There is a way (by calling the default constructor) to create an object using new in a state when it lacks some of its mandatory collaborators and usage will result in the NullPointerException.
    • Such a class cannot be reused outside DI containers (tests, other modules) as there is no way except reflection to provide it with its required dependencies.

    Constructor injection gives you more control over the object instantiation since using field injections means to restrict your class creation to reflection and rely on support to these particular injection annotations. Besides that, having the dependencies clearly on the constructor lets the code easier to maintain and to test.

    @Component
    public class MyClass {
     
        @Autowired private DogsController controller;
        @Autowired private DogsService service;
        @Autowired private DogsDao dao;
        @Autowired private ApplicationProperties properties;
     
     
        //...
        // Business methods
        //
    }

    2.2 Setter Based Dependency Injection

    Setters should be used to inject optional dependencies. The class should be able to function when they are not provided. The dependencies can be changed anytime after the object is instantiated. That may on may not be an advantage depending on the circumstances. Sometimes it is desirable to have an immutable object. Sometimes it is good to change the object's collaborators at runtime - such as JMX managed MBeans.

    @Component
    public class MyClass {
     
        private DogsController controller;
        private DogsService service;
        private DogsDao dao;
        private ApplicationProperties properties;
     
        @Autowired
        public void setController(DogsController controller) {
            this.controller = controller;
        }
     
        @Autowired
        public void setService(DogsService service) {
            this.service = service;
        }
     
        @Autowired
        public void setDao(DogsDao dao) {
            this.dao = dao;
        }
     
        @Autowired
        public void setProperties(ApplicationProperties properties) {
            this.properties = properties;
        }
     
     
        //...
        // Business methods
        //
    }

    2.3 Constructor Based Dependency Injection

    Constructor injection is good for mandatory dependencies. Those, which are required for the object to function properly. By supplying those in the constructor, you can be sure that the object is ready to be used the moment it is constructed. Fields assigned in the constructor can also be final, allowing the object to be either completely immutable or at least protect its required fields.

    One consequence of using a constructor to provide dependencies is that circular dependency between two objects constructed in such a way is no longer possible (unlike with setter injection). That is actually a good thing rather than limitation as circular dependencies should be avoided and are usually a sign of bad design. This way such a practice is prevented.

    Another advantage is that if using spring 4.3+, you can completely decouple your class from DI frameworks. The reason is that Spring now supports implicit constructor injection for one constructor scenario. That means you no longer need DI annotations in your classes. Of course, you could achieve the same with explicitly configuring DI in your spring configs for a given class, this just makes this whole lot easier.

    @Component
    public class MyClass {
     
        private DogsController controller;
        private DogsService service;
        private DogsDao dao;
        private ApplicationProperties properties;
     
        @Autowired
        public MyClass(DogsController controller, DogsService service, DogsDao dao, ApplicationProperties properties) {
            this.controller = controller;
            this.service = service;
            this.dao = dao;
            this.properties = properties;
        }
     
        //...
        // Business methods
        //
    }

    3. Comparison

    3.1 Readability

    The readability is humans' judgment about how easy to understand a software program or a piece of software program is. A developer spends 30% of their time writing a piece of software and 70% of the time maintaining it. The readability improves software maintainability. When a developer looks at a class, he/she should quickly be able to focus on vital parts of the class without getting distracted by boilerplate code, or other framework components.

    • Field Injection: The Best. Less boilerplate code. The focus is on business logic.
    • Constructor Injection: Better. Constructors visually stand separate from methods.
    • Setter Injection: Worst. Added 4 instance methods. Takes away focus form business methods.

    3.2 Immutability

    In a Software Programming terms, an Object is called Immutable if, by any means, its state can not be modified after creation. Immutability is a really important principle of good Object-Oriented Programming. The immutability brings thread-safety, state safety, and readability to the classes.

    Unlike constructor, field injection cannot be used to assign dependencies to final fields effectively rendering your objects mutable.

    • Constructor Injection: Supports immutability.
    • Setter Injection: No immutability.
    • Field Injection: No immutability.

    3.3 State Safety

    An object, most likely, is instantiated by the consumers or the underlying framework. The object itself should provide rules or guidelines to the instantiation so that they will invoke the object in a correct state. If the Object doesn’t mandate such state safety, there is a possibility of the objects being instantiated to incomplete or incorrect states.

    Note: All of the above examples are state safe because Spring is resolving their dependencies and Spring will correctly initialize all the fields, those are part of @Autowired. But some consumers may instantiate your object with a new keyword. We should look at state safety beyond the Spring Framework.

    • Constructor Injection: State Safe. The object is instantiated to a full state or is not instantiated at all.
    • Setter Injection: Consumer uses no-argument constructor. And possibility miss calling one of the setters or call the same setter twice with a different value (copy-paste bugs)
    • Field Injection: Consumer uses no-argument constructor. There is no valid way to set the state of the object. The only option is to use Reflection to set the private fields.

    4. Circular Dependency

    It happens when a bean A depends on another bean B, and the bean B depends on the bean A as well:

    Bean A → Bean B → Bean A

    When Spring context is loading all the beans, it tries to create beans in the order needed for them to work completely. For instance, if we didn’t have a circular dependency, like the following case:

    Bean A → Bean B → Bean C

    Spring will create bean C, then create bean B (and inject bean C into it), then create bean A (and inject bean B into it).

    But, when having a circular dependency, Spring cannot decide which of the beans should be created first, since they depend on one another. In these cases, Spring will raise a BeanCurrentlyInCreationException while loading context.

    It can happen in Spring when using constructor injection; if you use other types of injections you should not find this problem since the dependencies will be injected when they are needed and not on the context loading.

    4.1 Resolution

    If Object A and B are dependent each other i.e A is depends ob B and vice-versa. Spring throws 
    ObjectCurrentlyInCreationException 
    while creating objects of A and B bcz A object cannot be created until B is created and vice-versa. So spring can resolve circular dependencies through setter-injection. Objects constructed before setter methods invoked

    5. Reference

    https://www.javacodegeeks.com/2019/02/field-setter-constructor-injection.html

    https://www.vojtechruzicka.com/field-dependency-injection-considered-harmful/

    https://www.baeldung.com/circular-dependencies-in-spring

    https://stackoverflow.com/questions/21182230/spring-circular-dependencies-on-constructor-setter-injection

    https://javarevisited.blogspot.com/2012/11/difference-between-setter-injection-vs-constructor-injection-spring-framework.html#ixzz6NExoJbFC

    https://stackoverflow.com/questions/53397756/difference-between-field-and-constructor-injections-in-dagger

    'Framework > SPRING' 카테고리의 다른 글

    Reactive Programming and WebFlux  (0) 2020.07.05
    Component Scanning  (0) 2020.05.23
    Bean Definition Overriding  (0) 2020.05.23
    Application Context  (0) 2020.04.10
    Portable Service Abstraction (PSA)  (0) 2020.03.06

    댓글

Designed by Tistory.