简介 org.springframework.beans.factory.FactoryBean 用于在 IoC 容器中创建其它 Bean,该接口定义如下:
1 2 3 T getObject () Class<?> getObjectType() boolean isSingleton ()
有哪些现存的 FactoryBean
?例如:
当需要从 JNDI 查找对象(例如 DataSource
)时,可以使用 JndiObjectFactoryBean
。
当使用 Spring AOP 为 bean 创建代理时,可以使用 ProxyFactoryBean
。
当需要在 IoC 容器中创建 Hibernate 的 SessionFactory
时,可以使用 LocalSessionFactoryBean
。
当需要在 IoC 容器中创建 MyBatis 的 SqlSessionFactory
时,可以使用 SqlSessionFactoryBean
。
使用方式 这里我们举一个例子:
如果要为某个接口生成 JDK 动态代理,且将该代理对象放入 Spring IoC 容器,以便后续依赖注入使用,可以自定义实现 FactoryBean
实现如下效果,如图:
实现代码如下,首先创建接口及其代理类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public interface HttpApiService { HttpRespDTO<XxxRespDTO> api1 (XxxReqDTO reqDTO) ; } public class HttpApiServiceProxy implements InvocationHandler { public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { ...... } }
一、手工注册 1.0 如果只需创建一个 FactoryBean
,可以将其作为一个 Java Config 加上 @Configuration
即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Configuration public class HttpApiServiceFactoryBean implements FactoryBean <HttpApiService> { private static final Class<?> API_INTERFACE = HttpApiService.class; @Override public HttpApiService getObject () { return (HttpApiService) Proxy.newProxyInstance( API_INTERFACE.getClassLoader(), new Class []{ API_INTERFACE }, new HttpApiServiceProxy ()); } @Override public Class<?> getObjectType() { return API_INTERFACE; } @Override public boolean isSingleton () { return true ; } }
二、手工注册 2.0 如果需要创建多个 FactoryBean
,可以使用泛型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Setter public class HttpApiServiceFactoryBean <T> implements FactoryBean <T> { private Class<T> apiService; @SuppressWarnings("unchecked") @Override public T getObject () { return (T) Proxy.newProxyInstance( apiService.getClassLoader(), new Class []{ apiService }, new HttpApiServiceProxy ()); } @Override public Class<?> getObjectType() { return apiService; } @Override public boolean isSingleton () { return true ; } }
Java Config 如下,手工创建多个 FactoryBean
:
1 2 3 4 5 6 7 8 9 10 11 12 @Configuration public class HttpApiServiceConfig { @Bean public HttpApiServiceFactoryBean<UserHttpApiService> userHttpApiService () { return new HttpApiServiceFactoryBean (UserHttpApiService.class); } @Bean public HttpApiServiceFactoryBean<RoleHttpApiService> roleHttpApiService () { return new HttpApiServiceFactoryBean (RoleHttpApiService.class); } }
这种方式参考了 Mybatis-Spring 注册映射器 。
三、自动发现 如果想进一步省略 Java Config,做到自动扫描并创建 FactoryBean
,可以创建自动配置类。例如,为添加了 @HttpApi
注解的接口创建相应的 FactoryBean
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 @Configuration @Import({AutoConfiguredHttpApiScannerRegistrar.class}) public class HttpApiServiceAutoConfig {} @Slf4j class AutoConfiguredHttpApiScannerRegistrar implements ImportBeanDefinitionRegistrar , ResourceLoaderAware { private static final String BASE_PKG = Application.class.getPackage().getName(); private ResourceLoader resourceLoader; @Override public void registerBeanDefinitions (AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { try { log.debug("Searching for HttpApiService annotated with @HttpApi from {}" , BASE_PKG); ClassPathHttpApiScanner scanner = new ClassPathHttpApiScanner (registry); if (this .resourceLoader != null ) { scanner.setResourceLoader(this .resourceLoader); } scanner.doScan(BASE_PKG); } catch (IllegalStateException ex) { log.debug("Could not determine auto-configuration package, automatic @HttpApi scanning disabled." , ex); } } @Override public void setResourceLoader (ResourceLoader resourceLoader) { this .resourceLoader = resourceLoader; } } class ClassPathHttpApiScanner extends ClassPathBeanDefinitionScanner { public ClassPathHttpApiScanner (BeanDefinitionRegistry registry) { super (registry, false ); } @Override protected Set<BeanDefinitionHolder> doScan (String... basePackages) { addIncludeFilter(new AnnotationTypeFilter (HttpApi.class)); Set<BeanDefinitionHolder> beanDefinitions = super .doScan(basePackages); if (beanDefinitions.isEmpty()) { logger.warn( "No @HttpApi bean was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration." ); } else { processBeanDefinitions(beanDefinitions); } return beanDefinitions; } private void processBeanDefinitions (Set<BeanDefinitionHolder> beanDefinitions) { GenericBeanDefinition definition; for (BeanDefinitionHolder holder : beanDefinitions) { definition = (GenericBeanDefinition) holder.getBeanDefinition(); String beanClassName = definition.getBeanClassName(); definition.setBeanClass(HttpApiServiceFactoryBean.class); definition.getPropertyValues().add("apiService" , getClass(beanClassName)); } } private Class<?> getClass(String beanClassName){ try { return Class.forName(beanClassName); } catch (ClassNotFoundException e) { throw new IllegalStateException (e); } } @Override protected boolean isCandidateComponent (AnnotatedBeanDefinition beanDefinition) { return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent(); } }
这种方式参考了 Mybatis-Spring 发现映射器 。
需要用到 Spring 的几个类,待补充:
ImportBeanDefinitionRegistrar
ClassPathBeanDefinitionScanner
BeanDefinitionHolder
GenericBeanDefinition
AnnotatedBeanDefinition
参考 《Spring BeanFactory和FactoryBean的区别 》
org.springframework.beans.factory.FactoryBean
ThreadPoolExecutorFactoryBean
LocalSessionFactoryBean
SqlSessionFactoryBean
MyBatis-Spring 注入映射器