1

これは風変わりなユースケースであるため、理解するにはある程度の忍耐が必要であり、風変わりなソリューションが必要になる場合があります。

コンテキスト

@Beanではなくメソッドによって作成された、コンテキストに存在する特定の Bean インスタンスに対して自動アクションを実行する、Spring で使用するライブラリを作成しています@ComponentScan。可能であれば、Bean はタイプではなく、他の手段、できればファクトリ メソッドの注釈によって区別できる必要があります。

これが理想的なケースです。たとえば、Bean を生成する方法が 2 つあるとします。

@Bean
public SomeType makeSome() {...}

@Bean
@Special
public SomeOtherType makeOther() {...}

ここで、2 番目の Bean は、それを作成したメソッドの注釈のために特別です。@Specialしかし、それを区別できるようにするメカニズムはオプションです。

では、どうにかして特別な豆だけを手に入れたい。

警告

すべての Bean が同じインターフェースを実装する場合、タイプごとに注入できることは承知しています。ただし、これは可能な限り透過的に機能する必要があり、既存のアプリへの変更は可能な限り少なくする必要があります。

潜在的なアプローチ

私が念頭に置いている2つの広範なアプローチを次に示します。

1) Bean を登録するプロセスにジャックし、Bean インスタンスをある種のコンテナーに透過的にラップします (この部分は実行可能であると確信しています)。例えば

public void registerBean(Object bean, ApplicationContext ctx) {
   ctx.register(bean); //do the usual
   ctx.register(new Wrapper(bean); //register it wrapped as well
}

次に、 type のすべての Bean をすべて注入しますWrapper。ここでの問題は明らかに重複です...代わりに、インターフェイスを実装するプロキシインスタンスをオンザフライで生成できるWrapperので、同時に元のBeanラッパーとして機能できます。私はエキゾチックなソリューションでも大丈夫だと言いましたよね?

2) Spring はすでに Bean候補を実際の登録済み Bean と区別しています (たとえば@ComponentScan、パッケージ、注釈などで候補をフィルタリングできます)。このプロセスに参加して、後でそれらの Bean インスタンスを区別できるようにするいくつかの有用なメタデータ (ファクトリ メソッドなど) をまだ含んでいる候補記述子を取得したいと考えています。

4

3 に答える 3

2

@QualifierBean を区別する機能を提供するために使用する必要があるようです。

@Bean
@Qualifier("special")
class MyBean {}

@Bean
class OtherBean {
    @Qualifier("special")
    private MyBean bean;
}

詳細については、https ://spring.io/blog/2014/11/04/a-quality-qualifier を参照してください。

UPD(あなたが話していることを理解しました:))BeanDefinitionRegistryPostProcessor

ここに使用例があります:

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import static java.util.Collections.unmodifiableMap;

/**
 * This is hack to collect all beans of some type in single map without eager initialization of those beans
 *
 * Usage:
 * 1. Register new bean of type {@link ServiceTrackingBeanPostProcessor} parametrized with class of
 *    beans you want to collect
 * 2. Now you can inject {@link ServiceTracker} parametrized with your type anywhere
 *
 * @param <T> Located type
 */
public class ServiceTrackingBeanPostProcessor<T> implements BeanPostProcessor, BeanDefinitionRegistryPostProcessor {
    private final ConcurrentMap<String, T> registeredBeans = new ConcurrentHashMap<>();
    private final Class<T> clazz;
    private final String beanName;

    public ServiceTrackingBeanPostProcessor(Class<T> clazz) {
        this.clazz = clazz;
        beanName = "locatorFor" + clazz.getCanonicalName().replace('.', '_');
    }

    @Override
    public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
        return o;
    }

    @Override
    public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
        if (!clazz.isInstance(o)) {
            return o;
        }
        registeredBeans.putIfAbsent(s, (T) o);
        return o;
    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        AnnotatedGenericBeanDefinition def = new AnnotatedGenericBeanDefinition(Wrapper.class);
        registry.registerBeanDefinition(beanName, def);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        beanFactory.registerSingleton(beanName, new Wrapper(unmodifiableMap(registeredBeans)));
    }

    private class Wrapper extends AbstractServiceTracker<T> {
        public Wrapper(Map<String, T> services) {
            super(services);
        }
    }
}

clazz.isInstanceインスタンス化と注釈に関するほぼすべての情報を取得できる Bean 名によるアプリケーションコンテキストからの beanDefinition の取得にチェックの条件を変更するだけです。

于 2018-02-05T16:33:59.177 に答える
1

@Special注釈付きのすべての Bean を取得する 1 つの方法を次に示します。

Map<String,Object> beans = applicationContext.getBeansWithAnnotation(Special.class);

参照: https://stackoverflow.com/a/14236573/1490322

編集:上記の回答は、クラスに の注釈が付けられている場合にのみ機能するように見える@Specialため、シナリオでは機能しません。しかし、同じ質問に対するこの別の答えはうまくいくかもしれません。ConfigurableListableBeanFactory からのメタデータを使用して、メソッドに特定のアノテーションが付けられた Bean を識別します。

于 2018-02-05T16:34:11.690 に答える
0

前述のように、 @Qualifier アノテーションはここに行く方法だと思います。しかし、別の例を示します。

@Bean
public SomeType makeSome() {...}

@Bean
public SomeType makeSomeOther() {...}

この Bean が必要なコンポーネント (@Service) で次のことができます。

@Autowired
@Qualifier("makeSome")
SomeType makeSomeBean;

ご覧のとおり、同じタイプの 2 つの Bean は Bean 名で区別できます (名前が付けられた @Bean アノテーション付きメソッドと同じ名前が割り当てられた Bean)

于 2018-02-05T18:45:28.693 に答える