Setter-based DI is performed by the container calling setters on beans after calling a no-argument constructor or a static no-argument factory method to instantiate the bean.

The following example shows a class whose dependency can only be injected via a setter. This class is a regular Java class. It is a POJO (plain Java object) that does not depend on specific container interfaces, base classes, or annotations.

Java
public class SimpleMovieLister {
    // SimpleMovieLister depends on MovieFinder
    private MovieFinder movieFinder;
    // setter to allow the Spring container to implement MovieFinder
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
    // business logic that actually uses the embedded MovieFinder is omitted...
}
Kotlin
class SimpleMovieLister {
    // lazy-initialized property that allows the Spring container to implement MovieFinder
    lateinit var movieFinder: MovieFinder
    // business logic that actually uses the embedded MovieFinder is omitted...
}

ApplicationContext supports both constructor-based and setter-based DI for the beans it manages. It also supports setter based DI after some dependencies are already injected using the constructor approach. You configure dependencies in the form of BeanDefinition, which you use in conjunction with PropertyEditor instances to convert properties from one format to another. However, most Spring users work with these classes not directly (that is, programmatically), but rather with XML bean definitions annotated components (that is, classes marked with the annotations @Component,@Controller and so on) or methods marked with the @Bean annotation in Java-based classes marked with the @Configuration annotation. These sources are then internally converted into BeanDefinition instances and used to load the entire Spring IoC container instance.

Constructor-based or setter-based DI?

Because it is possible to mix constructor-based DI and setter-based DI, a good rule of thumb is to use constructors for required dependencies and setters or configuration methods for optional dependencies. Note that using the @Required annotation on a setter can be done to turn a property into a required dependency ; however, it is preferable to use constructor injection with programmatic argument checking.

The Spring team generally supports constructor injection because it allows application components to be implemented as immutable objects and ensures that required dependencies are not null. Moreover, components injected through the constructor are always returned to the client (calling) code in a fully initialized state. As a side note, having a lot of constructor arguments is a code smell, which means the class is probably overloaded and needs to be refactored to better distribute tasks.

Setter injection should mostly be used only for optional dependencies that can be given reasonable default values in the class. Otherwise, a check for not-null must be performed wherever a dependency is used in the code. One of the benefits of setter injection is that the setter methods make objects of the class suitable for later reconfiguration or reinjection. Therefore, management via JMX MBeans is an adequate example of using setter injection.

Use the DI style that is most appropriate for the specific class. Sometimes, when it comes to third-party classes for which you don't have the source code, the choice is made for you. For example, if the third-party class does not expose any setter methods, then constructor injection may be the only form of dependency injection available.