I come across this tutorial which shows how to use the H2 embedded database in spring application and it is working fine without any issue.
However by checking the code I did not understand how can the configuration class DBConfig
is being discovered and treated as configuration class for the ApplicationContext so can the beans inside be created.
Please note that it is not used as argument in AnnotationConfigApplicationContext()
and @ComponentScan
and @Configuration
are not annotated in the same class.
As you can see below for the class Application
@ComponentScan(basePackages = "com.zetcode")
public class Application {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
// the application context is taking as argument the same Class
var ctx = new AnnotationConfigApplicationContext(Application.class);
var app = ctx.getBean(Application.class);
app.run();
ctx.close();
}
@Autowired
private JdbcTemplate jdbcTemplate;
private void run() {
var sql = "SELECT * FROM cars";
var cars = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Car.class));
cars.forEach(car -> logger.info("{}", car));
}
}
and class DBConfig
@Configuration
public class DBConfig {
@Bean
public DataSource dataSource() {
var builder = new EmbeddedDatabaseBuilder();
var db = builder
.setType(EmbeddedDatabaseType.H2) // HSQL or DERBY
.addScript("db/schema.sql")
.addScript("db/data.sql")
.build();
return db;
}
@Bean
public JdbcTemplate createJdbcTeamplate() {
var template = new JdbcTemplate();
template.setDataSource(dataSource());
return template;
}
}
I also did some JUnit Tests and I found that the DBConfig
was created and so the Beans defined inside it.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=Application.class)
public class EmbeddedTest {
@Autowired
private DBConfig dbConfig;
@Autowired
private DataSource dataSource;
@Test
public void test() {
assertNotNull(dbConfig);
assertNotNull(dataSource);
}
}
I had to do some debugging in order to figure out how spring works for the above case.
As we did not provide the Configured Class to our Application context, Spring will use the @ComponentScan
to fetch it. How ? via the package com.zetcode
provided in the annotation. It scans the package and all subpackages under com.zetcode
searching for classes:
src
├───main
│ ├───java
│ │ └───com
│ │ └───zetcode
│ │ │ Application.java
│ │ ├───config
│ │ │ DBConfig.java
│ │ └───model
│ │ Car.java
│ └───resources
│ │ logback.xml
│ └───db
│ create-db.sql
│ insert-data.sql
└───test
└───java
Thus, it will finds 3 : Application
, DBConfig
, Car
and for each one it sees if it is a candidate components. In our Example, the generic Beans are Application
DBConfig
only. Once they are identified, the next step was to check the @Bean
Method. And for each one identified it will create the dedicated Bean. Finally it will do the autowiring.