A little bit of Spring

Didem AĞDOĞAN
7 min readDec 7, 2024

--

After diving into Beans in detail in the previous post, let’s take a look at other conveniences offered by Spring.

@SpringBootApplication is an annotation that marks the starting point of your application. While it is not mandatory for a Spring Boot project, we usually prefer to use it as it simplifies certain functionalities.

@SpringBootApplication is a combination of several annotations:

  • @EnableAutoConfiguration: This annotation enables automatic configuration during the application’s startup. Thanks to this annotation, Spring Boot automatically loads and executes the necessary configurations for your application.
  • @SpringBootConfiguration: Used at the class level and is similar to the @Configuration annotation.
  • @ComponentScan: Automatically scans and binds the components (such as @Component, @Service, @Repository) within the specified package and its sub-packages. The @ComponentScan annotation is typically used with the @Configuration annotation to define the package that Spring should scan for components. Additionally, @ComponentScan detects, scans, and registers beans for classes. It can take specific values, but if used without arguments, it instructs Spring to scan the current package and all its sub-packages.

@SpringBootApplication is not mandatory but serves as a structure that simplifies tasks in Spring Boot projects. If you choose not to use it, you will need to manually include the annotations mentioned above.

Annotations in Spring make dependency injection easier. Instead of using XML configuration files, we can define beans using Spring Bean annotations on classes and methods.

IoC (Inversion of Control) Container: The Spring IoC container configures and manages beans. Also known as the DI (Dependency Injection) container, it is used to implement automatic dependency injection. It handles object creation and lifecycle management, injecting dependencies into classes as needed.

The IoC container creates an object from the specified class, injects all dependency objects at runtime through a method, and disposes of them when appropriate. This means we don’t have to manually create and manage objects ourselves. (Thanks, Spring!)

ApplicationContext: The Spring IoC container is responsible for instantiating objects, wiring them together, configuring them, and managing their lifecycle. Both BeanFactory and ApplicationContext represent different types of Spring IoC containers.

BeanFactory:

Lazy Initialization: BeanFactory loads and creates beans only when they are actually needed (i.e., when the bean is called or used). This can help in more efficient memory usage and consumes fewer resources initially.

Minimal Functionality: BeanFactory provides the basic IoC features and handles dependency management. It is generally preferred for lightweight applications where resource usage is critical.

Explicit Configuration: BeanFactory is simpler and has fewer features. It provides a more direct and explicit way of configuring beans, meaning it offers less abstraction and therefore fewer additional functionalities compared to ApplicationContext.

Lazy Initialization: A bean is created only when it is actually called or used. This means the bean’s initialization and instantiation are deferred until it is needed, which can save resources and improve performance by not loading beans that are not used immediately.

Eager Initialization: A bean is created at the time of application startup. This means the bean is instantiated and initialized as part of the Spring container’s startup process, regardless of whether the bean will be used immediately or not. This can lead to quicker access times for the bean but may consume more resources at startup.

ApplicationContext:

Eager Initialization: The ApplicationContext loads and creates all beans at the time of application startup. This can require more resources initially, but it ensures that the application is ready to use quickly and consistently throughout its runtime.

Enhanced Functionality: ApplicationContext includes all the functionalities provided by BeanFactory along with additional features. For example, it supports event publishing and listening, messaging, integration with AOP (Aspect-Oriented Programming), and internationalization (i18n).

Convenience: Working with ApplicationContext is generally easier because it provides more configuration and management tools. It is particularly preferred for complex and large-scale applications due to its richer set of features and capabilities.

While both containers manage and inject Spring beans, they use different approaches and provide varying features, making ApplicationContext a more feature-rich choice for most Spring applications.

Dependency Injection (DI): Dependency Injection is not exclusive to the Spring Framework; it is a design pattern and a software development technique used broadly in Object-Oriented Programming (OOP). For instance, it is aligned with the SOLID principles, particularly the Dependency Inversion Principle (DIP).

The primary goal of DI is to enable a class to obtain its dependencies from an external source rather than creating them itself. This means that a class does not instantiate its dependencies directly. Instead, dependencies are provided to a class from outside, typically by a container such as the Spring IoC container. This approach decouples the class from its dependencies, making the system more modular and easier to test and maintain.

The goal of Dependency Injection (DI) is to separate the creation of dependencies from the class that requires them. This approach allows for loose coupling between classes, making the code more modular, testable, and maintainable. DI is typically managed by an IoC (Inversion of Control) container, with the Spring Framework effectively implementing this principle.

DI is essentially a pattern that enables IoC. It provides the necessary dependencies for our program. In simple terms, Spring brings the required bean to the class and injects it. There are three primary ways to do this with Spring:

  1. Constructor Injection: Dependencies are provided via the constructor of the class. This method ensures that all dependencies are injected during the object creation phase, making it a straightforward and clear method.
  2. Setter Injection: Dependencies are provided through setter methods. After the object is created, the IoC container calls the setter methods to inject the dependencies.
  3. Field Injection: Dependencies are directly assigned to the fields of a class. However, this method is less commonly used due to its more brittle nature and potential for creating tightly coupled code.

Each method has its advantages, and the choice of which to use often depends on the specific requirements and coding standards of the project.

At first glance, Field Injection appears simpler and cleaner because dependencies are directly assigned to the fields of a class. However, this method uses reflection to inject dependencies, which can be a performance concern.

  • Reflection: In Field Injection, the IoC container uses reflection to access and modify private fields at runtime to inject the required dependencies. While reflection provides a way to dynamically access and set fields, it is generally slower compared to constructor or setter-based dependency injection.
  • Performance Impact: Since reflection introduces additional overhead by operating at runtime, Field Injection can be slower than constructor or setter-based injection. This is because it involves additional checks and operations that are not required in the latter methods. Thus, while Field Injection might seem simpler for configuration, it can introduce unnecessary performance costs, especially in applications where performance is crucial.

Overall, while Field Injection is useful in specific scenarios, its use is typically discouraged in favor of constructor or setter injection for most applications due to the latter’s better performance and clearer separation of concerns.

In some cases, constructor injection alone may not be sufficient to handle certain dependency scenarios, particularly circular dependencies. A circular dependency occurs when two or more classes are mutually dependent on each other, directly or indirectly, which can lead to issues in a Spring-based application.

Circular Dependency Issue:

Problem with Constructor Injection: If all classes involved in a circular dependency use constructor injection, Spring will encounter a circular dependency error during bean creation. This is because Spring tries to create beans in a specific order, and a circular dependency prevents it from resolving the dependencies correctly.

Solution with Setter Injection: To handle circular dependencies, you can switch at least one of the involved classes to use setter injection instead of constructor injection. With setter injection:

Bean Creation: Spring first creates the bean without its dependencies.

Dependency Injection: After the bean is created, Spring injects its dependencies using the setter methods. This approach allows Spring to resolve dependencies without the strict order required by constructor injection, thus avoiding circular dependency issues.

By using setter injection, Spring can safely inject dependencies even when a circular dependency is present. This method allows Spring to delay the injection process until all dependencies are resolved, thereby preventing the exception that would be thrown if constructor injection was used exclusively.

Which Design Patterns Spring Use?

  1. Singleton: By default, Spring defines all Beans as Singleton and creates only one instance for each Bean. This behavior can be customized with the @Scope annotation.
  2. Factory Method: This pattern is used to abstract the object creation process. BeanFactory and ApplicationContext are built on this pattern. Spring’s IoC container dynamically creates objects based on Bean definitions.
  3. Proxy: One of the most commonly used design patterns in the Spring Framework. It provides additional functionality (e.g., security, performance measurement) by dynamically replacing an object with proxy objects. Spring AOP (Aspect-Oriented Programming) utilizes the proxy pattern.
  4. Prototype: The ApplicationContext creates a new instance for every getBean('beanName') call, which adheres to the prototype approach.
  5. Template Method: Allows subclasses to define their own custom behavior by abstracting the common parts of the same operations. JdbcTemplate, HibernateTemplate, JpaTemplate, and RestTemplate use this pattern.
  6. Observer: Allows changes in one object to be propagated to dependent objects. Spring’s event mechanism uses ApplicationEvent and ApplicationListener to implement this pattern.
  7. Front Controller: Used to route all requests through a central controller. DispatcherServlet in Spring MVC implements this pattern.
  8. Command Pattern: Encapsulates a request as an object, thereby allowing the parameterization of clients with different requests. Spring Batch uses this pattern to apply each step as an independent operation.

Spring Framework offers powerful features and principles that facilitate the software development process. In this article, we discussed the fundamental building blocks of Spring and diving into commonly used design patterns.

See you in the next article! 🙂

--

--

Didem AĞDOĞAN
Didem AĞDOĞAN

Written by Didem AĞDOĞAN

Software Developer, Traveller, Curious

No responses yet