- Lab
- Core Tech

Guided: Spring Certified Professional - Java Configuration and Bean Management
This code lab will teach you how to configure Spring applications using Java-based annotations. You'll practice defining Spring beans, composing configurations across multiple files, and managing dependencies, including handling multiple beans of the same type.

Path Info
Table of Contents
-
Challenge
Introduction
Welcome to the lab Guided: Spring Certified Professional - Java Configuration and Bean Management.
Spring Framework provides a powerful way to configure applications using Java-based configuration. This approach leverages annotations to define beans and their relationships, making your configuration type-safe and refactoring-friendly.
In this lab, you'll work with an application that simulates a Smart Home system that includes the following functionality:
- Defining smart home devices (light, thermostat, security camera) as Spring beans
- Defining automation rules (Schedule, Trigger) as Spring beans
- Wiring these devices and rule beans into a central
HomeAutomationService
- Initializing the Spring application context using Java configuration
- Retrieving and using beans from the context, including handling multiple beans of the same type
By the end of the lab, you'll understand how to leverage Spring's Java configuration to build modular, maintainable, and type-safe applications. ---
Familiarizing with the Program Structure
The application includes the following classes in the
src/main/java
directory:com.smarthome.device.Device
: An interface representing any device in the smart home system.com.smarthome.device.Light
,com.smarthome.device.Thermostat
, andcom.smarthome.device.SecurityCamera
: Concrete classes implementing theDevice
interface.com.smarthome.automation.Rule
: An interface for automation rules.com.smarthome.automation.Schedule
andcom.smarthome.automation.Trigger
: Concrete classes implementing theRule
interface.com.smarthome.service.HomeAutomationService
: The core service that orchestrates devices and rules.com.smarthome.config.AppConfig
,com.smarthome.config.DeviceConfig
, andcom.smarthome.config.AutomationConfig
: Configuration classes that you'll complete to define and wire beans.com.smarthome.Main
: The application entry point that bootstraps the Spring context.
The device and automation classes, as well as the
HomeAutomationService
, are fully implemented. You'll focus on completing the configuration classes (AppConfig
,DeviceConfig
, andAutomationConfig
) and theMain
class to properly initialize and use the Spring context.You can compile and run the application using the Run button. Initially, the application will compile but won't work correctly until you complete the configuration.
Begin by examining the code to understand the program's structure. Once you're ready, start implementing the Spring configuration step by step.
info> If you need help, a
solution
directory is available for reference, containing subdirectories for each step with solution files following the naming convention[step]-[task]-[file].java
(e.g.,2-1-AppConfig.java
in thestep2
directory). -
Challenge
Declaring Configuration Classes and Beans
Spring Framework provides two primary annotations to configure your application using Java-based configuration instead of XML:
@Configuration
and@Bean
.The @Configuration Annotation
The
@Configuration
annotation marks a class as a source of bean definitions. It tells Spring that this class contains methods that will be used to instantiate and configure beans in the Spring application context:@Configuration public class MyApplicationConfig { // Bean definitions go here }
The @Bean Annotation
The
@Bean
annotation is used on methods within a@Configuration
class to define beans. When Spring processes your configuration, it will call these methods to create the beans and register them in the application context.@Configuration public class MyApplicationConfig { @Bean public MyService myService() { return new MyServiceImpl(); } }
When you define a bean, Spring:
- Calls the annotated method to create the bean instance
- Registers the returned object in the Spring application context
- Makes the bean available for dependency injection into other beans
By default, the bean name is the same as the method name. You can customize this by specifying a name in the annotation:
@Bean("customBeanName")
.Now you're ready to start implementing the Spring configuration for the Smart Home application. You'll start with the
@Configuration
annotation. --- Now, you'll add the@Bean
annotation to the methods that create Spring beans. -
Challenge
Qualifying Multiple Beans of the Same Type
When working with Spring applications, you may encounter situations where you need multiple beans of the same type. In such cases, Spring needs a way to distinguish between these beans when injecting them into other components.
Using @Qualifier for Bean Identification
The
@Qualifier
annotation helps Spring identify which specific bean to inject when multiple beans of the same type exist. It works in two parts:- Bean Definition: You add
@Qualifier
with a name to the@Bean
method - Injection Point: You use the same
@Qualifier
at the injection point to specify which bean you want
// Bean definition @Bean @Qualifier("primaryDs") public DataSource primaryDataSource() { return new DataSource("main-db"); } @Bean @Qualifier("backupDs") public DataSource backupDataSource() { return new DataSource("backup-db"); } // Injection point @Autowired @Qualifier("primaryDs") private DataSource dataSource;
Without qualifiers, Spring would throw a
NoUniqueBeanDefinitionException
when trying to autowire a type with multiple bean definitions.Next, you'll implement qualified beans for the Smart Home lights.
- Bean Definition: You add
-
Challenge
Composing Configuration Files
As applications grow, organizing configuration becomes increasingly important. Spring allows you to modularize your configuration by splitting it across multiple files and then combining them when needed.
Composing Configuration with @Import
The
@Import
annotation allows you to import bean definitions from other configuration classes. This promotes modularity and separation of concerns in your configuration:@Configuration @Import({DatabaseConfig.class, SecurityConfig.class}) public class MainConfig { // Additional bean definitions }
These are the benefits of using
@Import
:- Modularity: Group related configurations together
- Reusability: Import the same configuration in multiple places
- Clarity: Keep configuration files focused on specific concerns
- Maintainability: Easier to manage smaller, focused configuration files
When Spring processes a configuration class with
@Import
, it processes all the imported configurations as well, making their beans available in the same application context.Next, you'll use
@Import
to compose the Smart Home configuration. -
Challenge
Wiring Beans
One of the key features of Spring is dependency injection, which allows you to wire beans together. In Java-based configuration, you can inject dependencies by having them as parameters in your
@Bean
methods.Wiring Beans in Java Configuration
When a
@Bean
method has parameters, Spring automatically looks for beans of those types in the application context and provides them as arguments when calling the method.@Configuration public class ServiceConfig { @Bean public OrderService orderService( PaymentService paymentService, InventoryService inventoryService ) { return new OrderServiceImpl(paymentService, inventoryService); } }
In this example, Spring will:
- Find beans of type
PaymentService
andInventoryService
in the context - Pass them as arguments when calling the
orderService()
method - Register the returned
OrderService
bean in the context
When multiple beans of the same type exist, you can use
@Qualifier
annotations on the parameters to specify which bean to inject:@Bean public ReportService reportService( @Qualifier("primaryDs") DataSource primaryDs, @Qualifier("archiveDs") DataSource archiveDs ) { return new ReportServiceImpl(primaryDs, archiveDs); }
Next, you'll wire the Smart Home beans together to create the
HomeAutomationService
. - Find beans of type
-
Challenge
Initializing the Spring Context
Now that you have defined the configuration classes and beans, you need to bootstrap the Spring application context. For Java-based configuration, Spring provides the
AnnotationConfigApplicationContext
class.Initializing the Spring Context
The
AnnotationConfigApplicationContext
is used to create and configure a Spring application context from Java-based configuration classes:// Create a new Spring context using a configuration class AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class); // You can also register multiple configuration classes AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(Config.class); context.register(AdditionalConfig.class); context.refresh(); // Must call refresh() after registering configurations manually
Some important aspects of
AnnotationConfigApplicationContext
are:- It scans the provided configuration classes for
@Bean
methods and other Spring annotations - It processes
@Import
annotations to include additional configurations - It creates and wires all the beans according to your configuration
- It manages the lifecycle of these beans
Remember to close the context when you're done with it to release resources. Either in the
finally
block of a traditionaltry
statement:AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); try { // Use the context... } finally { context.close(); }
Or by using a
try-with-resources
statement (AnnotationConfigApplicationContext
implements theAutoCloseable
interface):try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class)) { // Use the context... } // Context is automatically closed when the try block exits
Next, you'll initialize the Spring context for the Smart Home application.
- It scans the provided configuration classes for
-
Challenge
Retrieving and Using Beans From Context
Once you have initialized the Spring application context, you can retrieve beans from it to use in your application. The context provides several methods to access beans.
Retrieving Beans from the Context
The most common ways to retrieve beans from the Spring context are:
-
By Type: When you know the bean's type but not its name, or when there's only one bean of that type:
UserService userService = context.getBean(UserService.class);
-
By Name and Type: When multiple beans of the same type exist and you need a specific one:
DataSource primaryDs = context.getBean("primaryDataSource", DataSource.class);
-
By Name: When you only know the bean's name (not recommended as it requires casting):
Object bean = context.getBean("userService"); UserService userService = (UserService) bean;
When you annotate a component with, for example,
@Qualifier("foo")
,"foo"
serves as disambiguation metadata, so you can retrieve the qualified bean by its qualifier. However, the qualifier does not become the bean name unless you explicitly give that name to the bean (for example with@Bean("foo")
).Next, you'll retrieve the beans from the Smart Home application context.
-
-
Challenge
Conclusion
Congratulations on successfully completing this Code Lab!
You can compile and run the application either by clicking the Run button in the bottom-right corner of the screen or by using the Terminal with the following commands:
-
Compile and package the application:
mvn clean package
-
Run the application:
java -jar target/smarthome-hub-1.0-SNAPSHOT-jar-with-dependencies.jar
When you run application, you should see messages indicating the following sequence of events:
- The
HomeAutomationService
starts itsrunAutomation
process. - All configured devices (living room light, bedroom light, thermostat, security camera) are turned ON.
- The defined
Schedule
andTrigger
rules are executed, printing their descriptions. - The thermostat's temperature is adjusted, and the security camera simulates recording.
- Finally, all devices are turned OFF as the automation sequence concludes.
- After the main automation, separate messages will confirm that the specifically qualified
Light
beans (livingRoomLight
andbedroomLight
) were successfully retrieved from the context and theirturnOn
/turnOff
methods were invoked individually.
Seeing this output confirms that your Spring configuration correctly defined all the beans, handled qualified beans, imported configurations, injected dependencies into the
HomeAutomationService
, and allowed retrieval of specific beans from the application context. ---Extending the Program
Here are some ideas to further enhance your skills and extend the application's capabilities:
-
Bean Lifecycle Callbacks: Add methods annotated with
@PostConstruct
and@PreDestroy
to one or more device classes (e.g.,Thermostat
). The@PostConstruct
method could print a message like "[Thermostat Name] Initializing..." and the@PreDestroy
method could print "[Thermostat Name] Shutting down...". Observe when these messages appear in the console output relative to the application startup and shutdown (when the context closes). -
Conditional Configuration with Profiles: Define two profiles, for example, "simple" and "advanced". Use the
@Profile("simple")
annotation on theThermostat
bean definition inDeviceConfig
and@Profile("advanced")
on theSecurityCamera
bean definition. InAutomationConfig
, add@Profile("advanced")
to theTrigger
bean definition. ModifyMain.java
to create the context without arguments, set the active profile, register your @Configuration classes, and then refresh. Run with different active profiles and observe which beans are created and injected intoHomeAutomationService
(you'll need to define two service beans, one for each profile, or make dependencies optional for this). -
Component Scanning Alternative: Refactor the
AutomationConfig
to use component scanning. Remove the@Bean
methods. AnnotateSchedule
andTrigger
classes with@Component
(or a more specific stereotype like@Service
if appropriate). Add@ComponentScan(basePackages = "com.smarthome.automation")
toAutomationConfig
(orAppConfig
). Verify the application still works as expected to demonstrate this alternative approach to bean discovery.
By implementing these enhancements, you'll gain a deeper understanding of Java Configuration and Bean Management with Spring. ---
Related Courses in Pluralsight's Library
If you'd like to continue building your Spring skills or explore related topics, check out these courses available on this path:
These courses cover a wide range of topics. Explore them to further your learning journey in Spring!
-
What's a lab?
Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.
Provided environment for hands-on practice
We will provide the credentials and environment necessary for you to practice right within your browser.
Guided walkthrough
Follow along with the author’s guided walkthrough and build something new in your provided environment!
Did you know?
On average, you retain 75% more of your learning if you get time for practice.