@Bean
public TimedRepository timedRepository(RealRepository repo) {
return new TimedRepository(repo, timer); // Adds some metrics
}
@Bean
public RealRepository realRepository(DataSource ds) {
return new RealRepository(ds); // The real jdbc implementation
}
在旧的XML时代,我将真正的存储库配置为匿名内部bean。是否可以使用新的Java配置方法执行类似的操作?在timedRepository
工厂方法中实例化实际存储库不是一个选项,因为我希望Spring在RealRepository
上获取注释。
动机是避免任何其他bean获取真正的存储库实现。我还应该提到两个bean都实现了一个Repository
接口,任何bean都可以根据存储库使用它(他们不应该知道TimedRepository
或RealRepository
。
答案 0 :(得分:0)
在使用基于java的配置时,我不认为它们相当于内部或本地bean。我可能会尝试在TimedRepositories bean方法中创建RealRepository,同时请求方法签名中的所有依赖项。但是,如果您真的需要使用spring来处理RealRepository依赖项,则需要使用bean工厂。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader=AnnotationConfigContextLoader.class)
public class ConfigTest {
@Autowired TimedRepository timedRepo;
@Test
public void testRepository() {
Assert.assertNotNull(timedRepo);
}
@Configuration
static class TimedRepositoryConfiguration {
@Autowired
private AutowireCapableBeanFactory beanFactory;
@Bean
public TimedRepository timedRepository() {
RealRepository realRepository = (RealRepository) beanFactory.createBean(RealRepository.class, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, true);
return new TimedRepository(realRepository);
}
public RealRepository realRepository() {
return new RealRepository();
}
}
static class RealRepository {
}
static class TimedRepository {
private RealRepository realRepo;
public TimedRepository(RealRepository r) {
this.realRepo = r;
}
}
}
答案 1 :(得分:0)
您可以手动实例化bean:
public class BeanThatDependsOnRealRepository() {
private final Repository repository;
@Inject
public BeanThatDependsOnRealRepository(DataSource dataSource) {
this.repository = new RealRepository(dataSource);
}
}
这实质上是匿名内部bean在XML中的作用。您只是显式构造它并在封闭类的构造函数中从Spring获取其依赖项。
答案 2 :(得分:0)
最新答案,但这在Spring Core 4+(可能还有Spring Core 3)中可能会有些诡异。
虽然标准Spring语义不支持使用JavaConfig创建内部bean,但是可以利用内部bean周围的内部功能来产生相同的结果。
BeanDefinitionValueResolver
(请参阅BeanDefinitionValueResolver#resolveValueIfNecessary
)在属性值解析期间生成了内部bean。在Spring中,“内部bean”的概念主要包含在该值解析器(它是内部bean的唯一生产者)之内,并且在术语“包含的bean”(来自父类DefaultSingletonBeanRegistry
)的bean工厂中也包含在内。 >
根据BeanDefinition
中提出的解析策略,我们可以通过将属性定义为BeanDefinitionValueResolver
来诱使Spring产生其他内部bean:
@Configuration
public class MyConfiguration {
private static Logger logger = LoggerFactory.getLogger(MyConfiguration.class);
private RealRepository realRepository;
private Timer timer;
public MyConfiguration(@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") RealRepository realRepository, Timer timer) {
this.realRepository = realRepository;
this.timer = timer;
logger.info("Constructed MyConfiguration {}", this);
}
@Bean
public TimedRepository timedRepository() {
TimedRepository timedRepository = new TimedRepository(this.realRepository, this.timer);
logger.info("Created timed repo: {}", timedRepository);
return timedRepository;
}
public RealRepository realRepository(DataSource dataSource) {
RealRepository realRepository = new RealRepository(dataSource);
logger.info("Created real repo: {}", realRepository);
return realRepository;
}
@Override
public String toString() {
return "MyConfiguration{" +
"realRepository=" + realRepository +
", timer=" + timer +
'}';
}
}
@Component
public class InnerBeanInjectionBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered {
@Override
public int getOrder() {
// Preempt execution of org.springframework.context.annotation.ConfigurationClassPostProcessor
return 0;
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
String[] beanDefinitionNameList = ((ConfigurableListableBeanFactory) registry).getBeanNamesForType(MyConfiguration.class, true, false);
assert beanDefinitionNameList.length == 1;
BeanDefinition configurationBeanDefinition = registry.getBeanDefinition(beanDefinitionNameList[0]);
BeanDefinition realRepositoryBeanDefinition = BeanDefinitionBuilder.genericBeanDefinition(MyConfiguration.class)
.setScope(BeanDefinition.SCOPE_SINGLETON)
.setFactoryMethod("realRepository")
.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR)
.getBeanDefinition();
configurationBeanDefinition.getConstructorArgumentValues()
.addGenericArgumentValue(realRepositoryBeanDefinition);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Do nothing
}
}
此解决方案的一个明显问题是,它需要通过BeanDefinitionRegistryPostProcessor
进行手动处理,这在此不费吹灰之力。我建议的是以下内容:
@InnerBean
)@Configuration
类中的方法和所需的候选组件类中BeanDefinitionRegistryPostProcessor
适应扫描带有@InnerBean
注释的静态方法的类(应在#postProcessBeanFactory
中扫描组件类,并在#postProcessBeanDefinitionRegistry
中扫描配置类)以下是示例:
@Target(ElementType.METHOD)
public @interface InnerBean {
}
@Configuration
public class MyConfiguration {
private static Logger logger = LoggerFactory.getLogger(MyConfiguration.class);
private RealRepository realRepository;
private Timer timer;
public MyConfiguration(@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") RealRepository realRepository, Timer timer) {
this.realRepository = realRepository;
this.timer = timer;
logger.info("Constructed MyConfiguration {}", this);
}
@Bean
public TimedRepository timedRepository() {
TimedRepository timedRepository = new TimedRepository(this.realRepository, this.timer);
logger.info("Created timed repo: {}", timedRepository);
return timedRepository;
}
@InnerBean
public static RealRepository realRepository(DataSource dataSource) {
RealRepository realRepository = new RealRepository(dataSource);
logger.info("Created real repo: {}", realRepository);
return realRepository;
}
@Override
public String toString() {
return "MyConfiguration{" +
"realRepository=" + realRepository +
", timer=" + timer +
'}';
}
}
@Component
public class InnerBeanInjectionBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered {
private static Logger logger = LoggerFactory.getLogger(InnerBeanInjectionBeanFactoryPostProcessor.class);
private Set<BeanDefinition> processedBeanDefinitionSet = new HashSet<>();
@Override
public int getOrder() {
// Preempt execution of org.springframework.context.annotation.ConfigurationClassPostProcessor
return 0;
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
ConfigurableListableBeanFactory beanFactory = (ConfigurableListableBeanFactory) registry;
String[] configBeanDefinitionNames = beanFactory.getBeanNamesForAnnotation(Configuration.class);
Arrays.stream(configBeanDefinitionNames)
.map(beanFactory::getBeanDefinition)
.filter(this::isCandidateBean)
.peek(this.processedBeanDefinitionSet::add)
.forEach(this::autowireInnerBeans);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
Arrays.stream(beanFactory.getBeanDefinitionNames())
.map(beanFactory::getBeanDefinition)
.filter(this::isCandidateBean)
.filter(beanDefinition -> !this.processedBeanDefinitionSet.contains(beanDefinition))
.forEach(this::autowireInnerBeans);
}
private boolean isCandidateBean(BeanDefinition beanDefinition) {
return beanDefinition.getBeanClassName() != null && beanDefinition.getBeanClassName().startsWith("com.example.demo.");
}
private void autowireInnerBeans(BeanDefinition beanDefinition) {
// Get @InnerBean methods
assert beanDefinition instanceof AnnotatedBeanDefinition;
AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDefinition;
Set<MethodMetadata> innerBeanMethods = annotatedBeanDefinition.getMetadata().getAnnotatedMethods(InnerBean.class.getName());
// Attach inner beans as constructor parameters
for (MethodMetadata method : innerBeanMethods) {
String innerBeanName = method.getMethodName();
if (!method.isStatic()) {
logger.error("@InnerBean definition [{}] is non-static. Inner beans must be defined using static factory methods.", innerBeanName);
continue;
}
BeanDefinition innerBeanDefinition = BeanDefinitionBuilder.genericBeanDefinition(beanDefinition.getBeanClassName())
.setScope(BeanDefinition.SCOPE_SINGLETON)
.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR)
.setFactoryMethod(innerBeanName)
.getBeanDefinition();
beanDefinition.getConstructorArgumentValues()
.addGenericArgumentValue(new ConstructorArgumentValues.ValueHolder(innerBeanDefinition, method.getReturnTypeName(), method.getMethodName()));
}
}
}
这样做会带来一些好处和警告。一个很大的好处是,bean的生命周期将由Spring IoC容器管理,这意味着将调用生命周期回调(例如@PostConstruct
和@PreDestroy
)。可以根据父代的生命周期自动对其进行管理。需要注意的是,不能将bean作为工厂方法参数注入(尽管您可以做一些工作,但您可能可以解决此问题),并且AOP代理将不会应用于@Configuration
类中的这些方法(即{{ 1}}永远不要被调用,因为它不会引用单例内部Bean;相反,应始终引用实例字段)。为了应用此功能,需要添加进一步的代理(类似于ConfigurationClassEnhancer.BeanMethodInterceptor
)。