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一個對象的方式。