什麼是FactoryBean
factoryBean 從名字上理解也能理解它就是一個Factory,然後作爲Bean的形式的存在於Spring的BeanFactory,換句話說它就是一個起工廠作用的Bean。
FactoryBean有什麼用
它的作用其實從名稱上也能大概知道它的作用就是起一個工廠作用,就是使用了工廠模式,封裝了創建對象的複雜過程,當所要創建的對象有複雜實例化過程代碼,官網就推薦使用自定義FactoryBean的方式,把這個自定義的FactoryBean注入到容器中。
在大多數Spring集成其他框架時就是通過擴展這個FactoryBean的方式來用的, 比如集成Feign,Ribbon等框架,就目前來看這個FactoryBean的實現有超過50多個,所以當創建某些類複雜時,就可以利用自定義FactoryBean的這個Spring容器擴展點來做。
如何自定義FactoryBean
大概的過程分爲兩個一是實現Spring的FactoryBean接口,二是通知spring容器哪些類可由自定義的FactoryBean來創建對象,下面就是示例的代碼
- FooFactoryBean.java
public class FooFactoryBean implements FactoryBean {
private static final Logger logger = LoggerFactory.getLogger(FooFactoryBean.class);
@Override
public Object getObject() throws Exception {
logger.info("FooFactoryBean create foo...");
return new FooBean();
}
@Override
public Class<?> getObjectType() {
return FooBean.class;
}
@Override
public boolean isSingleton() {
return false;
}
}
這個FactoryBean只是簡單的創建對象
- 自定義註解,能這個註解用來標識哪些是需要使用自定義的FactoryBean來創建
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface FooAnnotation {
}
- 需要創建 的Bean,使用了自定的註解來標識
@FooAnnotation
public class FooBean {
public String echoMessage(String message) {
return "ECHO:" + message;
}
}
- 綁定Bean與FactoryBean的關聯
public class FooFactoryBeanRegister implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
private ResourceLoader resourceLoader;
private Environment environment;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//掃描含有自定義註解FooAnnotation的的所有類
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false, environment);
provider.addIncludeFilter(new AnnotationTypeFilter(FooAnnotation.class));
String basePackage = FooAnnotation.class.getPackage().getName();
Set<BeanDefinition> candidateComponents = provider.findCandidateComponents(basePackage);
for (BeanDefinition beanDefinition : candidateComponents) {
AnnotationMetadata annotationMetadata = ((AnnotatedBeanDefinition) beanDefinition).getMetadata();
registerBeanDefinition(registry, annotationMetadata);
}
}
public void registerBeanDefinition(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata) {
String className = annotationMetadata.getClassName();
//在這裏把FooFactoryBean與Bean綁定起來
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(FooFactoryBean.class);
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className);
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
- 簡單引用示例
@RestController
@RequestMapping("/foo")
public class FooController {
@Autowired
private FooBean fooBean;
@GetMapping("/{message}")
public String bar(@PathVariable String message) {
return fooBean.echoMessage(message)
}
}
- 啓動類時要加上@Import(FooFactoryBeanRegister.class)
@SpringBootApplication
@Import(FooFactoryBeanRegister.class)
public class FactoryBeanDemoApplication {
public static void main(String[] args) {
SpringApplication.run(FactoryBeanDemoApplication.class, args);
}
}
總結
本次的自定義Factory的源由是閱讀SpringClound集成Feign的源碼,集成Feign就是使用這個自定義的FeignClientFactoryBean,理解這個機制對後續閱讀其他源碼,或自己開發是遇到問題或許可通過這種方式 很容易就解決了問題,多瞭解一下就能相當於口袋裏多了一個工具,什麼時候順手就可能用上了。
示例代碼下載