Spring 3.1 Java配置和内部bean

@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都可以根据存储库使用它们(他们不应该知道TimedRepositoryRealRepository

在使用基于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; } } } 

您可以手动实例化bean:

 public class BeanThatDependsOnRealRepository() { private final Repository repository; @Inject public BeanThatDependsOnRealRepository(DataSource dataSource) { this.repository = new RealRepository(dataSource); } } 

这基本上是匿名内部bean在XML中的作用。 您只是显式构造它并在封闭类的构造函数中从Spring获取其依赖项。

迟到的答案,但这可能在Spring Core 4+(可能还有Spring Core 3)中有一些技巧。

虽然标准的Spring语义不支持使用JavaConfig创建内部bean,但可以利用内部bean的内部function来产生相同的结果。

内部bean在属性值解析期间由BeanDefinitionValueResolver (请参阅BeanDefinitionValueResolver#resolveValueIfNecessary )。 Spring中“内部bean”的概念主要包含在这个值解析器(内部bean的唯一生产者)和bean工厂中的术语“包含bean”(来自父类DefaultSingletonBeanRegistry )中。

根据BeanDefinition提供的解析策略,我们可以通过将属性定义为BeanDefinition来欺骗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#postProcessBeanFactory配置类中#postProcessBeanDefinitionRegistry
  • 将bean定义附加到包含bean定义的autowired构造函数字段(如果这是您的约定,则为setter字段)

以下是一个例子:

 @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 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 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。 注意事项包括不能将bean作为工厂方法参数注入(尽管你可以通过一些工作来解决这个问题)并且AOP代理将不会应用于@Configuration类中的这些方法(即, realRepository()应该永远不会被调用,因为它不会引用单例内部bean – 相反,应始终引用实例字段)。 需要进一步代理(类似于ConfigurationClassEnhancer.BeanMethodInterceptor )才能应用此代理。