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一个对象的方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值