用例:我通过类路径扫描@ComponentScan 配置了我的容器。对于我的测试配置,我需要能够模拟特定的 bean。
由于加载顺序,通过类路径扫描加载的 bean 在使用 @Configuration 时无法正确覆盖。下面的代码示例演示了该问题。BaseExample.java 展示了如何通过配置覆盖 bean。ScanExample.java 显示覆盖通过 @ComponentScan 加载的 bean 被跳过(见最后注释)。
bitbucket上提供了一个演示项目。
// BaseExample.java
package com.glassworks.mock;
import org.mockito.Mockito;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.google.common.base.Joiner;
public class BaseExample {
private static final Logger log = LoggerFactory.getLogger(BaseExample.class);
private static AnnotationConfigApplicationContext ctx;
public static void main(String args[]) {
ctx = new AnnotationConfigApplicationContext(Config.class, OverrideConfig.class);
String beans[] = ctx.getBeanDefinitionNames();
log.info("{} beans found: {}", beans.length, Joiner.on(",").join(beans));
for(String bean : beans) {
log.info("{}: {}", bean, ctx.getBean(bean));
}
}
@Configuration
public static class Config {
@Bean
public AccountDao accountDao() {
log.debug("Creating accountDao [Config]");
return new AccountDao();
}
}
@Configuration
public static class OverrideConfig {
@Bean
public Object accountDao() {
log.debug("Creating accountDao [OverrideConfig]");
return Mockito.mock(AccountDao.class);
}
}
}
输出:21:05 信息 | com.glassworks.mock.BaseExample | accountDao:模拟 AccountDao,hashCode:666537607
[// ScanExample.java
package com.glassworks.mock;
import org.mockito.Mockito;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import com.google.common.base.Joiner;
public class ScanExample {
private static final Logger log = LoggerFactory.getLogger(ScanExample.class);
private static AnnotationConfigApplicationContext ctx;
public static void main(String args[]) {
ctx = new AnnotationConfigApplicationContext(Config.class, OverrideConfig.class);
String beans[] = ctx.getBeanDefinitionNames();
log.info("{} beans found: {}", beans.length, Joiner.on(",").join(beans));
for(String bean : beans) {
log.info("{}: {}", bean, ctx.getBean(bean));
}
}
@Configuration
@ComponentScan("com.glassworks.services")
public static class Config {
}
@Configuration
public static class OverrideConfig {
@Bean
public AccountDao accountDao() {
log.debug("Creating accountDao [OverrideConfig]");
return Mockito.mock(AccountDao.class);
}
}
}
输出:21:08 信息 | com.glassworks.mock.ScanExample | accountDao:com.glassworks.services.AccountDao@48805ebb
// AccountDao.java
package com.glassworks.services;
import org.springframework.stereotype.Repository;
@Repository
public class AccountDao {
}
注意 值得注意的是,将日志记录设置为调试时,Spring 表示它正在跳过定义。这似乎是一个错误。
21:09 调试 | oscaConfigurationClassBeanDefinitionReader | 跳过加载 [BeanMethod:name=accountDao,declaringClass=com.glassworks.mock.ScanExample$OverrideConfig] 的 bean 定义:bean 'accountDao' 的定义已经存在。这可能是由于 XML 中的覆盖。