自定義Spring FactoryBean

什麼是FactoryBean

factoryBean 從名字上理解也能理解它就是一個Factory,然後作爲Bean的形式的存在於Spring的BeanFactory,換句話說它就是一個起工廠作用的Bean。

FactoryBean有什麼用

	它的作用其實從名稱上也能大概知道它的作用就是起一個工廠作用,就是使用了工廠模式,封裝了創建對象的複雜過程,當所要創建的對象有複雜實例化過程代碼,官網就推薦使用自定義FactoryBean的方式,把這個自定義的FactoryBean注入到容器中。

	在大多數Spring集成其他框架時就是通過擴展這個FactoryBean的方式來用的, 比如集成Feign,Ribbon等框架,就目前來看這個FactoryBean的實現有超過50多個,所以當創建某些類複雜時,就可以利用自定義FactoryBean的這個Spring容器擴展點來做。

如何自定義FactoryBean

大概的過程分爲兩個一是實現Spring的FactoryBean接口,二是通知spring容器哪些類可由自定義的FactoryBean來創建對象,下面就是示例的代碼

  1. 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只是簡單的創建對象

  1. 自定義註解,能這個註解用來標識哪些是需要使用自定義的FactoryBean來創建
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface FooAnnotation {
    }
  1. 需要創建 的Bean,使用了自定的註解來標識
       @FooAnnotation
       public class FooBean {
           public String echoMessage(String message) {
               return "ECHO:" + message;
           }
       }

  1. 綁定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;
           }
       }
  1. 簡單引用示例
       @RestController
       @RequestMapping("/foo")
       public class FooController {
       
           @Autowired
           private FooBean fooBean;
       
           @GetMapping("/{message}")
           public String bar(@PathVariable String message) {
               return fooBean.echoMessage(message)
           }
       
       }
  1. 啓動類時要加上@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,理解這個機制對後續閱讀其他源碼,或自己開發是遇到問題或許可通過這種方式 很容易就解決了問題,多瞭解一下就能相當於口袋裏多了一個工具,什麼時候順手就可能用上了。
示例代碼下載

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