Introduction
In the previous article (https://referbruv.com/blog/types-of-dependency-injection-in-spring-boot-explained/), we have discussed about Dependency Injection and how it is implemented in Spring Boot framework for Java. We have also seen the different ways in which we can inject dependencies into our application classes when required via Constructor, Field or Setter approaches.
In this article, let us look at what is a Spring Bean and concepts around it.
What is a Spring Bean?
In Java, a Bean is a simple Java class. In the same way, a Spring Bean is a Java Bean that is managed by the Spring Dependency Injection framework. This means that the class will never be explicitly instantiated using the new keyword and will be injected into the code whenever needed by the Spring framework.
What is a Component?
To mark a class as a Spring Bean, we will decorate the class with a @Component
annotation or any other annotations that are derived from the @Component
annotation.
Spring provides several annotations that are derived from @Component
annotation used for specific use cases, such as Service, Repository, RestController etc.
Spring Annotations and their Use Cases
The following table describes all the Annotations that are derived from @Component
annotation and what each of it indicates
Annotation | Indicates |
---|---|
@Service | Class that provides business logic or service layer functionality |
@Repository | Class that interacts with the database. It enables exception translation for database-related exceptions into Spring’s DataAccessException |
@Controller | Class that serves as a web controller in an MVC (Model-View-Controller) architecture. |
@RestController | A specialized version of Controller that combines @Controller and @ResponseBody . Used for RESTful web services. |
When a Spring application first bootstraps, it first scans its entire namespace and sub namespaces for all classes that are annotated with @Component or any of its derivatives and registers them as Spring Beans for Injection.
If any class or namespace is outside of the namespace of the main method that is annotated with @SpringApplication
, it is not considered for Injection.
Registering a Bean – Implicit vs Explicit
The approach mentioned above, where we annotate the class we want to register for Dependency Injection works fine when the classes and the logic are a part of our application. This is called the Implicit registration.
But what if we want to include classes or objects which are not a part of our application but are a part of some third party libraries included via Jar files? Such an approach is called Explicit registration and we do this using @Bean
annotation.
The advantage of Explicit registration is that we can include libraries for object management, even though they are not a part of the Spring Application.
Differences between Implicit and Explicit Bean Registration
Implicit Bean Definition | Explicit Bean Definition |
---|---|
Defined automatically via annotations (@Component , etc.). | Defined manually via @Bean or XML configuration. |
components are scanned via @ComponentScan | components are defined in @Configuration classes or XML. |
Limited customization available (relies on default behavior). | Full control over bean creation and dependencies. |
Used to inject components that are part of the application | Used to inject custom beans or third-party libraries not part of the application code. |
Component Scanning with @ComponentScan annotation
By default SpringBoot scans for all the classes that are registered as Spring Beans using @Component or any of its derivatives within the package of the class annotated with @SpringBootApplication and all of its sub-packages only. If there is any Component or Class that is outside of the package of the SpringBootApplication class, it will not be scanned and registered for Injection. This means that if you try to inject any such class or Bean into the application, it will throw an exception.
To solve this, we need to specify such packages within the scanBasePackages of the ComponentScan annotation. The implementation is simple.
Create a simple class, say AppConfig that is annotated with the @ComponentScan annotation. Within this annotation, pass the packages that need to be included for injection to the scanBasePackages parameter.
Alternatively, you can also pass the same within the @SpringBootApplication annotation, because @SpringBootApplication annotation derives from the @ComponentScan annotation itself!
To custom specify the namespaces to be scanned for Injection, you can use the scanBasePackages parameter inside the @SpringApplication annotation.
Component Scanning in Spring Boot with an Example
For example, let’s say we have created a Service FooService inside our Hello application, but it is placed outside the package of the HelloApplication class as below –
package com.example.utils;
import org.springframework.stereotype.Service;
@Service
public class FooService {
public String getFoo() {
return "Hello there! This is Foo!";
}
}
Now this FooService is injected into FooController which is inside our HelloApplication package and an endpoint calls the getFoo() method. The controller is as below –
package com.example.hello.controllers;
import com.example.utils.FooService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/foo")
public class FooController {
private final FooService fooService;
public FooController(FooService fooService) {
this.fooService = fooService;
}
@GetMapping("/getFoo")
public String getFoo() {
return fooService.getFoo();
}
}
Now when we run this application, the application doesn’t start and we get an exception as below –
2025-01-07T11:47:07.122+05:30 WARN 12356 --- [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'fooController' defined in file [D:\MyProjects\JavaProjects\hello\target\classes\com\example\hello\controllers\FooController.class]: Unsatisfied dependency expressed through constructor parameter 0: No qualifying bean of type 'com.example.utils.FooService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
2025-01-07T11:47:07.126+05:30 INFO 12356 --- [ main] o.apache.catalina.core.StandardService : Stopping service [Tomcat]
2025-01-07T11:47:07.139+05:30 INFO 12356 --- [ main] .s.b.a.l.ConditionEvaluationReportLogger :
Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2025-01-07T11:47:07.161+05:30 ERROR 12356 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.example.hello.controllers.FooController required a bean of type 'com.example.utils.FooService' that could not be found.
Action:
Consider defining a bean of type 'com.example.utils.FooService' in your configuration.
Process finished with exit code 1
As we can observe, the error states that FooService couldn’t be found to inject. This makes sense because the namespace of the FooService (com.example.utils) is not within the base package of the HelloApplication (com.example.hello).
Specifying Base Packages in SpringBootApplication
To fix this, we can simply specify the package of the FooService to be included for Component Scanning. To do this we can add another class and annotate with @ComponentScan
attribute or simply add this package to scan inside the @SpringBootApplication
annotation.
I’ll go with the latter approach because this eliminates the need for adding another class, and keeps the configurations at one place.
package com.example.hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(
scanBasePackages = {"com.example.utils","com.example.hello"}
)
public class HelloApplication {
public static void main(String[] args) {
SpringApplication.run(HelloApplication.class, args);
}
}
Observe that scanBasePackages is of type String[] and so multiple base package names can be passed. Also keep in mind that once you start using this parameter, you need to pass all the packages being used in the application INCLUDING the one where the SpringBootApplication is present.
Once this change is made, we no longer get the error and the application runs as usual.
Conclusion
In this article, we have looked at the fundamental concepts of Spring Dependency Injection framework starting with a Spring Bean – A Spring Bean is any Java Class that is registered to be object managed by the Spring Boot dependency injection framework.
To do this, we have two approaches – implicit, for the class that are written in the application and explicit, for the classes and libraries that are not native to the application and are included via Jar files. By default Spring includes all the classes that are annotated with @Component
and all its derivates.
We have also seen that the Spring DI also takes in all the classes which are annotated in such approach within its base package. We have also seen with an illustrating example that, for classes that we want to include for DI but are outside of the base package of the @SpringBootApplication
, we can do so using the @ComponentScan
annotation, or use the scanBasePackages parameter within the @SpringBootApplication
annotation.