1

这是一个奇特的用例,因此需要一些耐心才能理解,并且可能需要奇特的解决方案。

上下文

我正在制作一个与 Spring 一起使用的库,该库对上下文中存在的特定 bean 实例执行自动操作,由@Bean方法创建,而不是由@ComponentScan. 如果可能的话,bean 应该不是通过类型区分,而是通过其他方式区分,最好是工厂方法上的注释。

这是理想的情况。例如,假设有 2 种 bean 生产方法:

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

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

在这里,第二个 bean 是特殊的,因为@Special创建它的方法上的注释。但任何使其可区分的机制都是一种选择

然后,我想以某种方式只获得特殊的豆子。

警告

我知道如果所有 bean 都实现相同的接口,我可以按类型注入它们。但这应该尽可能透明,需要对现有应用程序进行尽可能少的更改。

潜在的方法

这是我想到的两种广泛的方法:

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
}

然后,注入所有类型的 bean Wrapper。这里的问题显然是重复...或者我可以动态生成一个代理实例来实现一个Wrapper接口,所以它可以同时充当原始 bean包装器。我确实说过我也可以接受异国情调的解决方案,不是吗?

2) Spring 已经将 bean候选者与实际注册的 bean 区分开来(例如@ComponentScan,可以通过包、注释等过滤候选者)。我希望也许可以进入这个过程并获得候选描述符,这些描述符仍然包含一些有用的元数据(如他们的工厂方法),这将允许我以后区分这些 bean 实例。

4

3 回答 3

2

似乎您需要使用@Qualifier它提供区分bean的功能:

@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的一种方法。

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;

如您所见,两个具有相同类型的 bean 可以通过 bean 名称来区分(分配与 @Bean 注释方法相同名称的 Bean)

于 2018-02-05T18:45:28.693 回答