![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0gTMx81dsQWZ4lmZf1GLlpXazVmcvwFciV2dsQXYtJ3bm9CX9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cGcq5yN3QjN3IGZwEjYzEjYyIGZyYzX5EjN1YDMzIzLcdDMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.jpg)
Java程式設計規範中聲明,Java接口類是不能直接執行個體化的,但是我們在平時的開發中經常會遇到隻聲明接口就可以直接使用的。
eg:
- Mybatis中隻用使用
聲明要掃描的Mapper接口類就可以直接從Spring中擷取使用,進行操作資料庫@MapperScan
- Dubbo中隻要用Dubbo提供的
注解,同樣可以直接從Spring中擷取使用進行遠端調用。@Service
那麼以上這些功能在Spring中是如何實作的呢?
由此就引出本篇主要介紹的接口
FactoryBean
public interface FactoryBean<T> {
@Nullable
T getObject() throws Exception;
@Nullable
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
在Spring中當發現一個Bean的類型是
FactoryBean
,此時執行個體化時候就會執行,該對象的
getObject()
方法進而來進行執行個體化。那麼如何擷取真實
FactoryBean
呢?隻需要在執行個體化的Bean的name前面加
&
符号才是擷取真正
FactoryBean
的執行個體對象。
Java程式設計規範中聲明,Java接口類是不能直接執行個體化的,Spring實作接口的執行個體化操作,本質上隻是調用
FactoryBean
的
getObject()
方法,而真正的執行個體化操作,還是有開發者來實作的。以我們常見的使用架構為例。
MyBatis
and
Dubbo
- MyBatis中實作FactoryBean的類
MapperFactoryBean
- Dubbo中實作FactoryBean的類
ReferenceBean
在此我們以MyBatis為例,講述MyBatis是如何實作FactoryBean來實作接口執行個體化操作的。 對Spring的源碼有研究的同學知道,在Spring中Bean的讀取會生成BeanDefinition對象,執行個體化實際就是找到Bean對象的BeanDefinition對象,然後根據 BeanDefinition資訊來執行個體的。那麼在這裡我們首先要看下MyBatis是如何為接口生成BeanDefinition對象的吧。
我們一起看下
ImportBeanDefinitionRegistrar
接口。ImportBeanDefinitionRegistrar接口就是允許開發者來根據開發者的規則來生成BeanDefinition的并注冊到
BeanDefinitionRegistry
中。因為Spring預設是根據自己的規則去生成BeanDefinition的,但是這裡也提供了一個切口,供開發者使用。
public interface ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}
1
2
3
4
5
MyBatis中
MapperScannerRegistrar
實作了
ImportBeanDefinitionRegistrar
。掃描Mapper接口所在的包,為每個接口生成特定的BeanDefinition
# MapperScannerRegistrar
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//掃描Mapper接口所在的包
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
....
//為每個接口生成特定的BeanDefinition
scanner.doScan(StringUtils.toStringArray(basePackages));
}
1
2
3
4
5
6
7
8
# ClassPathMapperScanner
在doScan方法為每個标記的Mapper接口生成一個BeanName。而執行個體化工廠都指定為
MapperFactoryBean
。隻用調用其
getObject()
方法即可完成接口的執行個體化。
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
logger.warn("No MyBatis mapper 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();
//設定每個Bean的工廠類,MapperFactoryBean
definition.setBeanClass(this.mapperFactoryBean.getClass());
...
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
我們舉一個例子
@Mapper
public interface TUserMapper {
int insert(TUser record);
List<TUser> selectAll();
TUser selectOne(@Param("id") Integer id);
TUser selectByName(@Param("name") String name);
}
1
2
3
4
5
6
7
8
9
10
11
這裡BeanDefinition的名字就是
TUserMapper
,而工廠方法就是
MapperFactoryBean
。如下僞代碼,getBean("TUserMapper"),就是調用
MapperFactoryBean.getObject()
,而
getBean("&TUserMapper")
才是擷取
MapperFactoryBean
的執行個體。
@Test
public void factoryBeanTest(){
System.out.println(applicationContextTools.getApp().getBean("&TUserMapper"));
//org.mybatis.spring.mapper.MapperFactoryBean@3ab6678b
}
1
2
3
4
5
# 我們如何定制自己的解析的注解呢?
編寫一個類似于
MapperScan
的注解類,
MyMapperScan
。
注意: 自定義的注解隻能聲明在配置類上才有效,配置類就是一定要被
@Configuration
修飾。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MyMapperScannerRegistrar.class)
public @interface MyMapperScan {
String value();
}
public class MyMapperScannerRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MyMapperScan.class.getName()));
String value = annoAttrs.getString("value");
System.out.println(value);
System.out.println("配置類:"+importingClassMetadata.getClassName());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 驗證
@Configuration
@MyMapperScan(value = "ConfigBean")
public class ConfigBean {
}
@SpringBootApplication //被@Configuration修飾就等同于配置類
@MyMapperScan(value = "test")
@MapperScan(value = "orm.example.dal.mapper")
public class LxchinesszzMybatisStudyApplication {
public static void main(String[] args) {
new SpringApplicationBuilder().web(WebApplicationType.NONE).run(args);
}
}
ConfigBean
配置類:orm.example.ConfigBean
test
配置類:orm.example.LxchinesszzMybatisStudyApplication