HomeSpring BootSpring XML Configuration: Beans, Namespaces and Migration to Java Config
Spring Boot

Spring XML Configuration: Beans, Namespaces and Migration to Java Config

Learn Spring XML-based configuration: bean declaration, constructor and setter injection, namespaces, importing files, @ImportResource in Spring Boot, and a step-by-step migration to Java config. Includes troubleshooting for common XML errors.

What Is XML-based Configuration?

XML-based configuration was Spring's original configuration model, available since Spring 1.0 in 2004. Beans, dependencies and wiring are declared in XML files, usually named `applicationContext.xml` or `beans.xml`. At startup the container parses the files, turns each declaration into a BeanDefinition, then creates and wires the beans. **Is it still used?** Yes, mostly in legacy code. New projects should use Java config and annotations, but you will meet XML when you: - Maintain or migrate an older Spring application - Work with Spring Batch or Spring Integration setups that were written in XML - Run XML and Java config side by side during an incremental migration Spring Boot still supports XML through `@ImportResource`. The examples below use Spring Framework 6 / Spring Boot 3 conventions.

XML vs Java Config vs Annotations at a Glance

| | XML | Java config (@Configuration) | Annotations (@Component) | |---|---|---|---| | Where beans are declared | XML files | @Bean methods | On the class itself | | Type safety | No, errors appear at startup | Yes, compile-time | Yes | | IDE refactoring support | Limited | Full | Full | | Change without recompiling | Yes | No | No | | Third-party classes | Easy | Easy | Not possible (can't annotate them) | | Recommended for new code | No | Yes | Yes | In practice, new Spring Boot code uses annotations for your own classes and `@Bean` methods for third-party classes. XML is mainly something you read and migrate away from.

Basic Bean Declaration

The `bean` element is the building block. Each one declares a Spring-managed object, with its class, name, scope and dependencies. Scope defaults to `singleton` (one shared instance). Use `prototype` for a new instance per request, and `lazy-init="true"` to delay creation until first use.
XML
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- class is required, id is the bean name -->
    <bean id="passwordEncoder"
          class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
        <constructor-arg value="12" type="int"/>
    </bean>

    <bean id="userRepository" class="com.example.repository.JpaUserRepository"/>

    <!-- ref points to another bean -->
    <bean id="userService" class="com.example.service.UserService">
        <constructor-arg ref="userRepository"/>
        <constructor-arg ref="passwordEncoder"/>
    </bean>

    <!-- new instance on every getBean() -->
    <bean id="reportGenerator" class="com.example.service.ReportGenerator" scope="prototype"/>

    <!-- created on first use -->
    <bean id="analyticsService" class="com.example.service.AnalyticsService" lazy-init="true"/>

    <!-- abstract template, never instantiated -->
    <bean id="baseService" abstract="true">
        <property name="maxRetries" value="3"/>
        <property name="timeout" value="5000"/>
    </bean>

    <!-- inherits maxRetries and timeout from baseService -->
    <bean id="orderService" class="com.example.service.OrderService" parent="baseService">
        <constructor-arg ref="orderRepository"/>
    </bean>

</beans>

Constructor and Setter Injection in XML

Constructor injection uses `constructor-arg`, and setter injection uses `property`. Both accept literal values or references to other beans. Prefer constructor injection for required dependencies and setters for optional ones. When a class has several constructor arguments, match them by `name` for readability, or by `index` or `type` if needed. Matching by `name` requires the code to be compiled with the `-parameters` flag (or debug info) so parameter names are available. **Never put real secrets in XML.** Use placeholders and supply the values from the environment (see the namespaces section).
XML
<!-- Constructor injection by name (most readable) -->
<bean id="paymentService" class="com.example.service.PaymentService">
    <constructor-arg name="apiKey" value="${payment.api-key}"/>
    <constructor-arg name="timeout" value="30"/>
    <constructor-arg name="retryCount" value="3"/>
</bean>

<!-- Setter injection -->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
    <property name="jdbcUrl" value="${spring.datasource.url}"/>
    <property name="username" value="${spring.datasource.username}"/>
    <property name="password" value="${spring.datasource.password}"/>
    <property name="maximumPoolSize" value="10"/>
</bean>

<!-- Mixed: constructor for required, setters for optional -->
<bean id="notificationService" class="com.example.service.NotificationService">
    <constructor-arg ref="userRepository"/>
    <property name="emailService" ref="emailService"/>
    <property name="maxRetries" value="3"/>
</bean>

Injecting Collections

XML has dedicated elements for lists, sets, maps and `java.util.Properties`. Elements can hold literal values or bean references (`<ref bean="..."/>` or `value-ref`).
XML
<!-- List of bean references -->
<bean id="compositeValidator" class="com.example.validation.CompositeValidator">
    <constructor-arg>
        <list>
            <ref bean="emailValidator"/>
            <ref bean="phoneValidator"/>
        </list>
    </constructor-arg>
</bean>

<!-- Map -->
<bean id="handlerRegistry" class="com.example.handler.HandlerRegistry">
    <property name="handlers">
        <map>
            <entry key="ORDER_CREATED" value-ref="orderCreatedHandler"/>
            <entry key="ORDER_CANCELLED" value-ref="orderCancelledHandler"/>
        </map>
    </property>
</bean>

<!-- Set of literals -->
<bean id="roleManager" class="com.example.security.RoleManager">
    <property name="allowedRoles">
        <set>
            <value>ADMIN</value>
            <value>USER</value>
        </set>
    </property>
</bean>

<!-- Properties -->
<bean id="mailConfig" class="com.example.mail.MailConfiguration">
    <property name="mailProperties">
        <props>
            <prop key="mail.smtp.host">smtp.example.com</prop>
            <prop key="mail.smtp.port">587</prop>
        </props>
    </property>
</bean>

XML Namespaces: context, tx, aop and task

Spring's XML namespaces give short, readable elements for common setup: component scanning, property placeholders, transactions, AOP and scheduling. Each namespace must be declared on the root `beans` element, with its schema location, or parsing fails. `<context:component-scan>` already enables annotation processing, so you don't also need `<context:annotation-config/>`.
XML
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           https://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           https://www.springframework.org/schema/context/spring-context.xsd
           http://www.springframework.org/schema/tx
           https://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- Same as @ComponentScan -->
    <context:component-scan base-package="com.example.myapp"/>

    <!-- Load properties, then use ${...} in value attributes -->
    <context:property-placeholder location="classpath:application.properties"/>

    <!-- Same as @EnableTransactionManagement -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

    <bean id="transactionManager"
          class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>

</beans>

XML Element to Java Config Mapping

Use this table when translating XML to Java config. | XML | Java equivalent | |---|---| | `<bean>` | `@Bean` method, or `@Component` on the class | | `<constructor-arg>` / `<property>` | Constructor or `@Bean` method parameters, setters | | `<context:component-scan>` | `@ComponentScan` (included in `@SpringBootApplication`) | | `<context:property-placeholder>` | `@PropertySource` with `@Value` or `@ConfigurationProperties` | | `<tx:annotation-driven>` | `@EnableTransactionManagement` (auto-configured in Boot) | | `<aop:config>` | `@EnableAspectJAutoProxy` with `@Aspect` classes | | `<task:annotation-driven>` | `@EnableAsync` and `@EnableScheduling` | | `<mvc:annotation-driven>` | `@EnableWebMvc`, or Boot's MVC auto-configuration | | `<import resource>` | `@Import` or `@ImportResource` | | `scope`, `lazy-init` | `@Scope`, `@Lazy` | | `init-method` / `destroy-method` | `@PostConstruct` / `@PreDestroy`, or `@Bean(initMethod=..., destroyMethod=...)` | | `depends-on` | `@DependsOn` |

Splitting Configuration Across Multiple Files

Large XML setups are split by layer or concern (data source, security, messaging). Beans in one file can reference beans from imported files. There are two ways to compose files: an `<import>` element in XML, or passing several file names when you create the context manually in a non-Boot application.
XML
<!-- applicationContext.xml -->
<beans ...>
    <import resource="classpath:config/datasource-config.xml"/>
    <import resource="classpath:config/security-config.xml"/>

    <bean id="userService" class="com.example.service.UserService">
        <constructor-arg ref="userRepository"/>   <!-- defined in datasource-config.xml -->
    </bean>
</beans>

Creating the Context Manually (Non-Boot)

Outside Spring Boot you create the `ApplicationContext` yourself. Pass several files, or use a `classpath*:` wildcard. In a Boot application you never do this, because Boot creates the context for you.
Java
// Several files
ApplicationContext context = new ClassPathXmlApplicationContext(
    "applicationContext.xml",
    "config/datasource-config.xml"
);

// Wildcard
ApplicationContext context = new ClassPathXmlApplicationContext(
    "classpath*:config/*-config.xml"
);

Lifecycle Callbacks, depends-on and Aliases

XML supports the full bean lifecycle. Use `init-method` and `destroy-method` for callbacks, `depends-on` to force creation order when no direct reference exists, and `<alias>` to give a bean extra names. A `BeanPostProcessor` is registered like any other bean, and Spring detects it automatically.
XML
<bean id="connectionPool"
      class="com.example.db.DatabaseConnectionPool"
      init-method="initialize"
      destroy-method="shutdown">
    <property name="poolSize" value="10"/>
</bean>

<!-- Ensure the migration runs before the repository is created -->
<bean id="userRepository"
      class="com.example.repository.UserRepository"
      depends-on="databaseMigration">
    <constructor-arg ref="dataSource"/>
</bean>

<bean id="databaseMigration" class="com.example.migration.FlywayMigration">
    <constructor-arg ref="dataSource"/>
</bean>

<!-- Three names, one singleton -->
<alias name="userService" alias="accountService"/>

<!-- Picked up automatically as a BeanPostProcessor -->
<bean class="com.example.processor.CustomBeanPostProcessor"/>

Using XML Configuration in Spring Boot

Spring Boot uses Java config by default, but `@ImportResource` loads XML into the same context. XML beans can be injected into annotated classes, and XML beans can reference Boot's auto-configured beans by name (for example `dataSource`). Two Boot-specific points: - Boot's `Environment` already resolves `${...}` placeholders in XML from application.properties, environment variables and profiles, so you usually don't need `<context:property-placeholder>`. - Put `@ImportResource` on a focused `@Configuration` class when only one module is XML-based, rather than on the main application class.
Java
@SpringBootApplication
@ImportResource("classpath:legacy/applicationContext.xml")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

// Or, more targeted:
@Configuration
@ImportResource({
    "classpath:legacy/batch-jobs.xml",
    "classpath:legacy/legacy-services.xml"
})
public class LegacyConfig { }

// XML-defined beans are injectable like any other:
@Service
public class UserService {

    private final LegacyUserRepository legacyUserRepository;  // defined in XML
    private final PasswordEncoder passwordEncoder;            // auto-configured by Boot

    public UserService(LegacyUserRepository legacyUserRepository,
                       PasswordEncoder passwordEncoder) {
        this.legacyUserRepository = legacyUserRepository;
        this.passwordEncoder = passwordEncoder;
    }
}

Migrating from XML to Java Configuration

Migrate incrementally, with XML and Java config coexisting the whole time: 1. Add `@ImportResource` so the existing XML loads inside a Boot application. 2. Move one bean (or one XML file) at a time to a `@Configuration` class, and delete it from the XML. 3. Run the application and tests after each move. 4. Replace namespace elements with annotations (see the mapping table above). 5. Remove `@ImportResource` once no XML remains. Where it makes sense, replace explicit bean definitions with `@Component`-style annotations and let component scanning find them.
Java
// BEFORE (XML)
//   <bean id="passwordEncoder" class="...BCryptPasswordEncoder">
//       <constructor-arg value="12" type="int"/>
//   </bean>
//   <bean id="userService" class="com.example.service.UserService">
//       <constructor-arg ref="userRepository"/>
//       <constructor-arg ref="passwordEncoder"/>
//   </bean>

// AFTER (Java)
@Configuration
public class AppConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(12);
    }

    @Bean
    public UserService userService(UserRepository userRepository,
                                   PasswordEncoder passwordEncoder) {
        return new UserService(userRepository, passwordEncoder);
    }
}

XML vs Java Config: Trade-offs

**XML advantages** - Configuration can change without recompiling - Wiring is visible in one place, separate from code - Works for classes you can't annotate **Java config advantages** - Type-safe, so mistakes fail at compile time instead of at startup - Full IDE support: navigation, refactoring, find usages - Can use loops, conditions and factory logic when creating beans - No namespace declarations or schema URLs - Easier to test - Spring Boot's auto-configuration is itself written in Java config For most teams, the compile-time safety and tooling of Java config outweigh the "no recompile" benefit of XML, especially since modern deployments rebuild and redeploy anyway.
Java
// XML:
//   <tx:annotation-driven transaction-manager="transactionManager"/>
//   <bean id="transactionManager" class="...JpaTransactionManager">
//       <property name="entityManagerFactory" ref="entityManagerFactory"/>
//   </bean>

// Java:
@Configuration
@EnableTransactionManagement
public class TransactionConfig {
    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
        return new JpaTransactionManager(emf);
    }
}

Troubleshooting Common XML Configuration Errors

| Error | Usual cause | Fix | |---|---|---| | `NoSuchBeanDefinitionException` | Typo in a `ref`, bean in a file that isn't imported or loaded | Check the id spelling and confirm the file is imported or listed in `@ImportResource` | | `FileNotFoundException: class path resource [...]` | Wrong path or file not on the classpath | Put it under `src/main/resources` and use `classpath:` with the exact path | | `Unable to locate Spring NamespaceHandler for XML schema namespace` | Namespace used but its Spring module is missing | Add the dependency (for example spring-aop, spring-tx) | | `cvc-complex-type` / schema errors | Namespace not declared, or `schemaLocation` entry missing | Declare the `xmlns` and add its location pair on the root element | | `Could not resolve placeholder 'x'` | Property undefined, or no placeholder resolution available | Define the property; outside Boot add `<context:property-placeholder>` | | `BeanCurrentlyInCreationException` | Circular dependency via constructor injection | Break the cycle, or use setter injection for one side | | Ambiguous constructor / `UnsatisfiedDependencyException` | Spring can't match arguments to a constructor | Add `name`, `index` or `type` to each `constructor-arg` |

Common Mistakes

- Hardcoding passwords and API keys in XML. Use placeholders and environment variables. - Declaring `<context:annotation-config/>` and `<context:component-scan>` together. Component scan already includes it. - Adding `<context:property-placeholder>` in Boot without needing it, which can hide Boot's own property resolution. - Forgetting to import a file, then chasing a "bean not found" error that is really a loading problem. - Migrating everything at once instead of bean by bean with tests after each step. - Copying old examples that use removed or deprecated classes (for example old Hibernate dialects or annotations like `@Required`). Check them against your Spring version.

When Is XML Still the Right Choice?

Use XML configuration only when: - You are maintaining a legacy application and a rewrite isn't justified - A library or framework module you depend on documents its setup in XML - You need runtime-editable wiring that is deliberately kept outside compiled code Otherwise, choose Java config and annotations. If you're new to Spring, learn dependency injection and Java-based configuration first, and treat XML as something to read, not write.