- Lab
-
Libraries: If you want this lab, consider one of these libraries.
- 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.
Lab 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/javadirectory: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 theDeviceinterface.com.smarthome.automation.Rule: An interface for automation rules.com.smarthome.automation.Scheduleandcom.smarthome.automation.Trigger: Concrete classes implementing theRuleinterface.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 theMainclass 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
solutiondirectory 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.javain thestep2directory). -
Challenge
Declaring Configuration Classes and Beans
Spring Framework provides two primary annotations to configure your application using Java-based configuration instead of XML:
@Configurationand@Bean.The @Configuration Annotation
The
@Configurationannotation 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
@Beanannotation is used on methods within a@Configurationclass 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
@Configurationannotation. --- Now, you'll add the@Beanannotation 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
@Qualifierannotation 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
@Qualifierwith a name to the@Beanmethod - Injection Point: You use the same
@Qualifierat 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
NoUniqueBeanDefinitionExceptionwhen 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
@Importannotation 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
@Importto 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
@Beanmethods.Wiring Beans in Java Configuration
When a
@Beanmethod 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
PaymentServiceandInventoryServicein the context - Pass them as arguments when calling the
orderService()method - Register the returned
OrderServicebean in the context
When multiple beans of the same type exist, you can use
@Qualifierannotations 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
AnnotationConfigApplicationContextclass.Initializing the Spring Context
The
AnnotationConfigApplicationContextis 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 manuallySome important aspects of
AnnotationConfigApplicationContextare:- It scans the provided configuration classes for
@Beanmethods and other Spring annotations - It processes
@Importannotations 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
finallyblock of a traditionaltrystatement:AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); try { // Use the context... } finally { context.close(); }Or by using a
try-with-resourcesstatement (AnnotationConfigApplicationContextimplements theAutoCloseableinterface):try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class)) { // Use the context... } // Context is automatically closed when the try block exitsNext, 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
HomeAutomationServicestarts itsrunAutomationprocess. - All configured devices (living room light, bedroom light, thermostat, security camera) are turned ON.
- The defined
ScheduleandTriggerrules 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
Lightbeans (livingRoomLightandbedroomLight) were successfully retrieved from the context and theirturnOn/turnOffmethods 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
@PostConstructand@PreDestroyto one or more device classes (e.g.,Thermostat). The@PostConstructmethod could print a message like "[Thermostat Name] Initializing..." and the@PreDestroymethod 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 theThermostatbean definition inDeviceConfigand@Profile("advanced")on theSecurityCamerabean definition. InAutomationConfig, add@Profile("advanced")to theTriggerbean definition. ModifyMain.javato 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
AutomationConfigto use component scanning. Remove the@Beanmethods. AnnotateScheduleandTriggerclasses with@Component(or a more specific stereotype like@Serviceif 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!
-
About the author
Real skill practice before real-world application
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.
Learn by doing
Engage hands-on with the tools and technologies you’re learning. You pick the skill, we provide the credentials and environment.
Follow your guide
All labs have detailed instructions and objectives, guiding you through the learning process and ensuring you understand every step.
Turn time into mastery
On average, you retain 75% more of your learning if you take time to practice. Hands-on labs set you up for success to make those skills stick.