Importing & Modularizing Configuration
Importing & Modularizing Configuration
As a Spring application grows, packing every @Bean definition into a single @Configuration class becomes unmanageable. Spring provides a clean mechanism for splitting configuration across multiple focused classes and then composing them: the @Import annotation family. This lesson covers how to use it, when to use it, and how to design your configuration layer so it stays easy to understand, test, and change.
Why Modularize Configuration?
A monolithic configuration class typically suffers from several problems:
- Hard to navigate. Security beans, persistence beans, messaging beans and web beans are all tangled in one file.
- Merge conflicts. When multiple developers touch different features they all edit the same file.
- Difficult to test in isolation. Loading the entire context to test one slice is slow and fragile.
- Poor reuse. You cannot bring a focused, self-contained configuration into another project easily.
Modularization solves all of these: each configuration class owns one concern, and they are composed explicitly.
@Import — Composing Configuration Classes
@Import tells Spring to process one or more additional @Configuration classes as if they were declared inline. All beans defined in the imported class become part of the application context.
Each imported class is a plain @Configuration class with its own beans:
@Import is explicit — you name exactly which class to include. Prefer @Import for library/framework configuration and for any class that lives outside your component-scan base package.
Bean Visibility Across Imported Configurations
Once a class is imported — whether directly with @Import or pulled in transitively — its beans are available to every other configuration class in the same context. You can @Autowired-inject them as constructor parameters on a @Configuration class, or let Spring resolve them as method parameters on a @Bean method.
Hierarchical @Import — Transitive Composition
Imported classes can themselves use @Import, creating a tree of configurations. This is how many Spring Boot auto-configuration classes work internally.
When AppConfig imports PersistenceConfig, Spring automatically processes JpaConfig as well. You do not need to list it again in AppConfig.
@ImportResource — Mixing XML with Java Config
Legacy projects often have existing XML bean definitions. @ImportResource lets you pull those into a Java-based configuration without rewriting them:
@ImportResource as a bridge while migrating. Convert XML beans to @Bean methods one module at a time, removing the XML file once the migration of each module is complete. Do not try to rewrite everything at once.
@ImportSelector — Dynamic, Programmatic Imports
When you need to decide at startup which configuration classes to import based on runtime conditions, implement ImportSelector:
This is the mechanism behind Spring Boot's @EnableAutoConfiguration — it uses a sophisticated ImportSelector to pick the right auto-configuration classes based on what is on the classpath.
Recommended Module Boundaries
A practical convention that scales well in real applications:
- PersistenceConfig — DataSource, EntityManagerFactory, TransactionManager
- SecurityConfig — PasswordEncoder, AuthenticationManager, security filter chain
- WebConfig — MVC configuration, converters, CORS, view resolvers
- MessagingConfig — JMS/RabbitMQ/Kafka factories and listeners
- CacheConfig — CacheManager, key generators
- AppConfig (root) — imports all of the above; defines only truly cross-cutting beans
ConfigA imports ConfigB and ConfigB imports ConfigA, Spring will throw a BeanDefinitionParsingException or behave unpredictably. Resolve circular dependencies by extracting shared beans to a third, lower-level configuration class that both can import.
Testing a Single Configuration Module
One of the biggest payoffs of modularization is testability. You can load exactly one configuration class in a test without the overhead of the full context:
Summary
@Import is the primary tool for composing focused @Configuration classes into a coherent application context. You have seen how to use it directly, how transitive imports build a configuration tree, how @ImportResource bridges legacy XML, and how ImportSelector enables runtime-conditional imports. Splitting configuration by concern — persistence, security, messaging — makes each piece independently navigable, testable, and reusable. The next lesson closes the tutorial with a hands-on project that puts multi-environment configuration together end to end.