7
@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ことはオプションではありませんRealRepository

動機は、他の Bean が実際のリポジトリ実装を取得するのを避けることです。Repositoryまた、両方の Beanが、リポジトリに応じて任意の Bean で使用されるインターフェイスを実装していることにも言及する必要があります (TimedRepositoryまたはRealRepository.

4

3 に答える 3

0

Bean を手動でインスタンス化できます。

public class BeanThatDependsOnRealRepository() {

  private final Repository repository;

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

これは本質的に、匿名内部 Bean が XML で行うことです。それを明示的に構築し、包含クラスのコンストラクターで Spring からその依存関係を取得しました。

于 2012-11-28T15:46:16.990 に答える
0

Java ベースの構成を使用する場合、内部またはローカル Bean に相当するものはないと思います。メソッド署名ですべての依存関係を要求することにより、TimedRepositories Bean メソッドでも RealRepository を作成しようとするでしょう。ただし、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;
        }
    }

}
于 2012-11-28T11:31:40.337 に答える
0

遅い答えですが、これはSpring Core 4+(およびおそらくSpring Core 3)で可能です。

標準の Spring セマンティクスは JavaConfig を使用した内部 Bean の作成をサポートしていませんが、内部 Bean に関する内部機能を利用して同じ結果を生成できます。

内部 Bean は、 によるプロパティ値の解決中に生成されますBeanDefinitionValueResolver( を参照BeanDefinitionValueResolver#resolveValueIfNecessary)。Spring 内の「内部 Bean」の概念は、主にこの値リゾルバー (内部 Bean の唯一のプロデューサー) 内と、「含まれる Bean」という用語 (親クラスからDefaultSingletonBeanRegistry) の下の Bean ファクトリ内に含まれています。

BeanDefinitionに示されている解決戦略に従って、プロパティを として定義することにより、Spring をだまして追加の内部 Bean を生成させることができますBeanDefinitionValueResolver

@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
  • Bean 定義を、含まれている Bean 定義の autowired コンストラクター フィールド (またはそれが規則である場合はセッター フィールド) にアタッチします。

次に例を示します。

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

これを行うことには、いくつかの利点と注意事項があります。大きな利点の 1 つは、Bean ライフサイクルが Spring IoC コンテナーによって管理されることです。つまり、ライフサイクル コールバック ( や など@PostConstruct)@PreDestroyが呼び出されます。Bean は、親のライフサイクルに従って自動的に管理できます。警告には、Bean をファクトリ メソッド パラメーターとして注入できないこと (ただし、少し作業すればこれを修正できる可能性があります) と、AOP プロキシが@Configurationクラス内のこれらのメソッドに適用されないこと (つまり、realRepository()呼び出されないようにする必要があること) が含まれます。シングルトンの内部 Bean を参照しないでください。代わりに、インスタンス フィールドを常に参照する必要があります)。これを適用するには、さらにプロキシ ( と同様ConfigurationClassEnhancer.BeanMethodInterceptor) を追加する必要があります。

于 2018-07-31T20:47:45.453 に答える