Spring Dependency Injection Using XML

Let's revisit Core Concepts Of The Spring Framework on How Spring Works:

Spring Container Magic

The Spring IoC Container

In Spring, IoC container receives metadata from either an XML file, Java annotations, or Java code. By reading the configuration metadata provided, the container gets its instructions on what objects to instantiate, configure, and assemble simple Plain Old Java Objects (POJO) to create objects that we call as Spring Beans.

Example Application: Bank of Jakarta

J2EE Tutorial provides an application example known as Duke’s Bank. It's a very good example to showcase how to employ J2EE solutions in real-life scenarios like in banking environment (just put in mind, this is an example application, although the scenario is real-life, but the sample application itself is not). In the same spirit, I bring this famous example to Spring framework (friendly fork), and renamed the solution to Bank of Jakarta (In the spirit of Jakarta EE, and as you know - Jakarta is the capital of Indonesia).

Let's take a look 2 classes in this project:

  • TxServiceImpl class implements interface TxService in service layer
  • TxDaoImpl class implements interface TxDao in Data Access (Dao) layer
public class TxServiceImpl implements TxService {

    public TxServiceImpl(TxDao txDao, AccountDao accountDao, NextIdDao nextIdDao) {
        this.txDao = txDao;
        ...
    }

    ...
}
                    

public class TxDaoImpl implements TxDao {

    private JdbcTemplate jdbcTemplate;

    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }
    
    public TxDaoImpl(DataSource dataSource) {
        setDataSource(dataSource);
    }
    
    ...
}
                    

For data access operation, TxService need to call TxDao. To access database, TxDao(Impl) depends on datasource. Spring will do the plumbing what we need is to declare and configure those two POJO classes in configuration. In this post, we will only talk about XML configuration. The configuration file will be like:

<beans>

    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
        <property name="driverClassName" value="org.postgresql.Driver" />
        <property name="url" value="jdbc:postgresql://localhost/bankofjakarta" />
        <property name="username" value="duke" />
        <property name="password" value="dariawan" />
    </bean>
    
    <bean id="txDao" class="com.dariawan.bankofjakarta.dao.impl.TxDaoImpl">
        <constructor-arg ref="dataSource" />
    </bean>    
    
    <bean id="txService" class="com.dariawan.bankofjakarta.service.impl.TxServiceImpl">
        <constructor-arg ref="txDao" />
        ...
    </bean>    
    
</beans>
                    

And now we are ready to use this Spring application:

// Create the application from the configuration
ApplicationContext appContext = new ClassPathXmlApplicationContext("com/dariawan/bankofjakarta/spring-config.xml");

// Look up the application service interface
TxService txService = appContext.getBean("txService", TxService.class);
try {
    // Use the service
    txService.withdraw(new BigDecimal("100"), "Withdraw 100$", "5008");
} catch (InvalidParameterException | AccountNotFoundException | IllegalAccountTypeException | InsufficientFundsException ex) {
    System.out.println("Exception: " + ex.getMessage());
}
                    

Accessing A Bean

There are multiple ways to accessing a bean:

// Create the application from the configuration
ApplicationContext appContext = new ClassPathXmlApplicationContext("com/dariawan/bankofjakarta/spring-config.xml");

/* Accessing service */
// Classic way: cast is needed
TxService ts1 = (TxService) appContext.getBean("txService");
// Since Spring 3.0: no more cast, type is a method param
TxService ts2 = appContext.getBean("txService", TxService.class);
// New in Spring 3.0: No need for bean id if type is unique
TxService ts3 = appContext.getBean(TxService.class );

/* Accessing Dao */
// Classic way: cast is needed
TxDao td1 = (TxDao) appContext.getBean("txDao");
// Use typed method to avoid cast
TxDao td2 = appContext.getBean("txDao", TxDao.class);
// No need for bean id if type is unique
TxDao td3 = appContext.getBean(TxDao.class );
                    

Application Context

Inside the Spring Application Context

Inside the Spring Application Context

Spring will start to initialize Application Context when this code is called:

// Create the application from the configuration
ApplicationContext appContext = new ClassPathXmlApplicationContext("com/dariawan/bankofjakarta/spring-config.xml");
                    

Spring manages the lifecycle of the application, all beans are fully initialized before use. And Spring manage it in 'correct' ways: beans are always created in the right order based on their dependencies,

  1. dataSource is created before txDao (or any other Dao)
  2. txDao is created before txService

As shown in the following logs during Spring application initialization (for display purpose, logs are altered, but sequence remains):

... 01:44:41.949 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'dataSource' 01:44:41.949 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating instance of bean 'dataSource' ... 01:44:42.251 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Finished creating instance of bean 'dataSource' ... 01:44:42.326 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'txDao' 01:44:42.327 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating instance of bean 'txDao' ... 01:44:42.328 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'dataSource' ... 01:44:42.336 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Finished creating instance of bean 'txDao' ... 01:44:42.411 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'txService' 01:44:42.411 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating instance of bean 'txService' ... 01:44:42.414 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'txDao' ... 01:44:42.429 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Finished creating instance of bean 'txService' ...

Each bean is bound to a unique id:

  • Bean id should reflects the service or role the bean provides to clients. Example: txService or txDao
  • Bean id should not contain implementation details. Example: txDaoJdbcTemplate is bad id, id should not restrict implementation - what if later we need to switch to Hibernate?