如何加载 @Configuration class 而 @ComponentScan 没有注释相同的 class

How can @Configuration class is loaded while @ComponentScan is not annotated for the same class

我遇到了这个 tutorial,它展示了如何使用 H2 嵌入式数据库 spring 应用程序,它工作正常,没有任何问题。

但是看了代码没看懂怎么配置classDBConfig 正在被发现并将其视为 ApplicationContext 的配置 class,因此可以创建里面的 bean。

请注意,它没有在 AnnotationConfigApplicationContext() 中用作参数,并且 @ComponentScan@Configuration 没有在同一个 class 中注释。

如下所示 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));
    }
}

和classDBConfig

@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;
    }
}

我还做了一些 JUnit 测试,我发现 DBConfig 已经创建,因此在其中定义了 Bean。

@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);
    }

}

我不得不做一些 调试 以弄清楚 spring 如何处理上述情况。

由于我们没有向我们的应用程序上下文提供已配置的 Class,Spring 将使用 @ComponentScan 来获取它。 如何? 通过注释中提供的包 com.zetcode。它扫描 com.zetcode 下的包和所有子包搜索 类:

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

因此,它会找到 3 个:ApplicationDBConfigCar 并针对每个它查看它是否是候选组件。在我们的示例中,通用 Bean 仅为 Application DBConfig。一旦确定了它们,下一步就是检查 @Bean 方法。对于每一个识别出来的对象,它都会创建专用的 Bean。最后它会自动装配。