DI Container
Overview of Spring Framework’s DI
Dependency injection, or DI, is a process where an object defines its dependencies only through arguments to its constructor or setter method, or properties that are going to be set on an object instance after it’s created.
DI helps to decouple components; when dependencies are declared as interfaces or abstract classes, we can easily replace their implementations with another, or mocks for unit testing.
In Spring Framework, DI container injects dependencies automatically when it creates beans, based on configuration metadata. A bean represents an object which is instantiated, assembled, and managed by DI container. ApplicationContext
interface plays a role as Spring Framework’s DI container; which is responsible for instantiating, configuring, and assembling beans, based on the configuration metadata.
How to Register Beans
The DI container consumes configuration metadata, which represents how components are supposed to be instantiated, configured, and assembled. The two most famous options for declaring configuration metadata are Java-based configuration and annotation-based configuration.
In Java-based configuration, we define beans on a configuration class annotated with @Configuration
. Within that class, each component is annotated with @Bean
to be registered in the DI container.
On the other hand, in annotation-based configuration, each component class is declared with @Component
annotation respectively, and we instruct the DI container to scan those component based on the specified package.
Apparently, annotation-based configuration seems easier. However, when you want to register the bean for types that cannot be annotated with @Component
, such as those from external libraries or third-party classes, you’ll use Java-based configuration.
How to Inject Dependencies into A Bean
There’re the two major DI options; constructor-based DI and setter-based DI.
In constructor-based DI, DI container invokes the constructor annotated as @Autowired
with instantiated beans as its dependencies. On the other hand, in setter-based DI, DI container injects dependencies through its setter method annotated as @Autowired
after invoking its default constructor to instantiate an object instance.
Which option should we go with? The official documentation mentions the following:
The Spring team generally advocates constructor injection, as it lets you implement application components as immutable objects and ensures that required dependencies are not null.
Constructor-based or setter-based DI?
Now, consider the case of injecting a bean whose scope is different from that of another bean it’s injected into. If we inject through @Autowired
annotation, the scope of the injected bean becomes the same as another bean it is injected into, regardless of the bean definition. In order to inject a bean with its intended scope, we can lookup beans managed by the DI container through ApplicationContext.getBean
method instead of @Autowired
annotation.
DispatcherServlet
Overview of Spring MVC architecture
In Spring MVC, DispatcherServlet
plays a central role as a front controller, and delegates actual work to configurable components.
DispatcherServlet
, first, delegates to HandlerMapping
interface to find an appropriate handler. RequestMappingHandlerMapping
, one of the well-known implementations, provides a mapping from HTTP requests to @RequestMapping
annotated methods.
And then, it delegates to HanderAdapter
interface to invoke the handler.
If an exception occurs during that, DispatcherServlet
delegates to HandlerExceptionResolver
to resolve the exception and provide alternative handling. One well-known implementation is ExceptionHandlerExceptionResolver
, which invokes a @ExceptionHandler
annotated method in a @ControllerAdvice
annotated class.
Reference
- https://docs.spring.io/spring-framework/reference/core/beans.html
- https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-servlet.html
- 株式会社NTTデータ (2016) 『Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発』 翔泳社