Spring中FactoryBean的正確用法

FacotryBean

Spring中一共有兩種Bean。一種是普通的bean,一種是工廠bean。工廠bean注入到spring中的是其getObject()返回的對象實例。
接下來我們通過兩個例子來講解如何正確的使用FactoryBean。
下面這兩個案例分別來自我們團隊維護的兩個開源項目:
基於dubbo的遠程SPI項目
Spring策略框架
大家有興趣可以去了解一下

案例一

實現FactoryBean接口,實現接口中的方法。

public class SpiFactoryBean<T> implements FactoryBean<T> {
    private Class<T> spiInterface;

    public SpiFactoryBean(Class<T> spiInterface) {
        this.spiInterface = spiInterface;
    }

    @Override
    public T getObject() {
        // jdk動態代理類生成
        InvocationHandler invocationHandler = (proxy, method, args) -> SpiRouter.route(spiInterface.getName(), proxy, method, args);
        return spiInterface.cast(Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{spiInterface},
                invocationHandler));
    }

    @Override
    public Class<?> getObjectType() {
        return spiInterface;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

}
public class SpiConsumerBootStrap implements BeanFactoryPostProcessor, ApplicationContextAware {

    private static ApplicationContext applicationContext;
    
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        configCenter.init(appName);
        //獲取BeanFactory
        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory)applicationContext.getAutowireCapableBeanFactory();
        defaultListableBeanFactory.registerBeanDefinition("SpringContextHolder",new RootBeanDefinition(ApplicationContextHolder.class));
        configCenter.getAllSpiConfigDTO().stream().map(SpiConfigDTO::getSpiInterface)
                .distinct()
                .forEach(i->{
                    Class clazz = null;
                    try {
                        clazz = ClassUtils.forName(i,Thread.currentThread().getContextClassLoader());
                    } catch (ClassNotFoundException e) {
                        throw new SpiException("獲取spi接口失敗");
                    }
                    //創建BeanDefinition
                    BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(SpiFactoryBean.class);
                    beanDefinitionBuilder.addConstructorArgValue(clazz);
                    defaultListableBeanFactory.registerBeanDefinition(clazz.getSimpleName(),beanDefinitionBuilder.getBeanDefinition());
                });
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpiConsumerBootStrap.applicationContext=applicationContext;
    }
}

重點關注下面這段代碼,將對應的工廠類注入到Spring容器中,容器中的Bean Name爲clazz.getSimpleName()。對應的Bean對象爲SpiFactoryBean中getObject()方法返回的對象。

BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(SpiFactoryBean.class);
                    beanDefinitionBuilder.addConstructorArgValue(clazz);
                    defaultListableBeanFactory.registerBeanDefinition(clazz.getSimpleName(),beanDefinitionBuilder.getBeanDefinition());

案例二

定義接口

public interface StrategyContainer<T> {
    T getStrategy(String var1);

    void register(String var1, T var2);
}
public class StrategyContainerFactoryBean<T, V extends Annotation> implements FactoryBean<StrategyContainer>, ApplicationContextAware {
    private Class<T> strategyClass;
    private Class<V> strategyAnnotationClass;
    private Function<V, String> identifyCodeGetter;
    private Map<String, T> strategyTable = new HashMap();
    private T defaultStrategy;

    public StrategyContainerFactoryBean() {
    }

    public static <T, V extends Annotation> StrategyContainerFactoryBean<T, V> build(Class<T> strategyClass, Class<V> strategyAnnotationClass, Function<V, String> identifyCodeGetter) {
        StrategyContainerFactoryBean<T, V> factoryBean = new StrategyContainerFactoryBean();
        factoryBean.setStrategyClass(strategyClass);
        factoryBean.setStrategyAnnotationClass(strategyAnnotationClass);
        factoryBean.setIdentifyCodeGetter(identifyCodeGetter);
        return factoryBean;
    }

    public StrategyContainer<T> getObject() throws Exception {
        Assert.notNull(this.strategyClass, "strategyClass must not be null");
        Assert.notNull(this.strategyAnnotationClass, "strategyAnnotationClass must not be null");
        Assert.notNull(this.identifyCodeGetter, "identifyCodeGetter must not be null");
        return new StrategyContainer<T>() {
            public T getStrategy(String identifyCode) {
                return Optional.ofNullable(StrategyContainerFactoryBean.this.strategyTable.get(identifyCode)).orElse(StrategyContainerFactoryBean.this.defaultStrategy);
            }

            public void register(String identifyCode, T strategy) {
                StrategyContainerFactoryBean.this.strategyTable.put(identifyCode, strategy);
            }
        };
    }

    public void setStrategyClass(Class<T> strategyClass) {
        this.strategyClass = strategyClass;
    }

    public void setStrategyAnnotationClass(Class<V> strategyAnnotationClass) {
        this.strategyAnnotationClass = strategyAnnotationClass;
    }

    public void setIdentifyCodeGetter(Function<V, String> identifyCodeGetter) {
        this.identifyCodeGetter = identifyCodeGetter;
    }

    public Class<?> getObjectType() {
        return StrategyContainer.class;
    }

    public boolean isSingleton() {
        return true;
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        String[] names = applicationContext.getBeanNamesForType(this.strategyClass);
        Arrays.stream(names).forEach((name) -> {
            T object = applicationContext.getBean(name, this.strategyClass);
            if (Objects.nonNull(AnnotationUtils.getAnnotation(AopUtils.getTargetClass(object), DefaultStrategy.class))) {
                if (Objects.nonNull(this.defaultStrategy)) {
                    throw new StrategyException("StrategyClass=" + this.strategyClass.getName() + "can only have one default strategy");
                }

                this.defaultStrategy = object;
            }

            List<V> identifiers = Lists.newArrayList();
            identifiers.addAll(AnnotationUtils.getRepeatableAnnotations(AopUtils.getTargetClass(object), this.strategyAnnotationClass));
            if (!CollectionUtils.isEmpty(identifiers)) {
                identifiers.forEach((i) -> {
                    String identifyCode = (String)this.identifyCodeGetter.apply(i);
                    if (Objects.nonNull(this.strategyTable.putIfAbsent(identifyCode, object))) {
                        throw new StrategyException("StrategyClass=" + this.strategyClass.getName() + ",identifyCode=" + identifyCode + "exist multi config");
                    }
                });
            }

        });
    }
}

注入FactoryBean接口的實現類,在此處注入屬性。

@Configuration
public class StrategyConfiguration {

    @Bean(name = "orderSyncParamBuildStrategy")
    public StrategyContainerFactoryBean orderSyncParamBuildStrategy(){
        return StrategyContainerFactoryBean.build(OrderSyncParamBuildStrategy.class, BizCodeIdentifier.class,(a)->a.bizCode().getCode());
    }
}    

總結

通過上面的兩個列子我們,我們可以總結一下FactoryBean的場景以及流程。
FactoryBean用來向Spring容器中動態對象。這個對象的行爲一般都與FactoryBean中的屬性有關,而FactoryBean的屬性可以通過註冊BeanDefinition方式注入或者new一個對象的方式。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章