FactoryBean接口实例化

举报
西魏陶渊明 发表于 2022/09/25 02:39:58 2022/09/25
【摘要】 Java编程规范中声明,Java接口类是不能直接实例化的,但是我们在平时的开发中经常会遇到只声明接口就可以直接使用的。 eg: Mybatis中只用使用@MapperScan声明要扫描的Mapper接口类就可以直接从Spring中获取使用,进行操作数据库Dubbo中只要用Dubbo提供的@Service注解,...

文章头

Java编程规范中声明,Java接口类是不能直接实例化的,但是我们在平时的开发中经常会遇到只声明接口就可以直接使用的。

eg:

  1. Mybatis中只用使用@MapperScan声明要扫描的Mapper接口类就可以直接从Spring中获取使用,进行操作数据库
  2. Dubbo中只要用Dubbo提供的@Service注解,同样可以直接从Spring中获取使用进行远程调用。

那么以上这些功能在Spring中是如何实现的呢?

由此就引出本篇主要介绍的接口FactoryBean


    
  1. public interface FactoryBean<T> {
  2. @Nullable
  3. T getObject() throws Exception;
  4. @Nullable
  5. Class<?> getObjectType();
  6. default boolean isSingleton() {
  7. return true;
  8. }
  9. }
1 2 3 4 5 6 7 8 9 10 11 12

在Spring中当发现一个Bean的类型是FactoryBean,此时实例化时候就会执行,该对象的getObject()方法从而来进行实例化。那么如何获取真实FactoryBean呢?只需要在实例化的Bean的name前面加&符号才是获取真正FactoryBean的实例对象。

Java编程规范中声明,Java接口类是不能直接实例化的,Spring实现接口的实例化操作,本质上只是调用FactoryBeangetObject()方法,而真正的实例化操作,还是有开发者来实现的。以我们常见的使用框架为例。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的,但是这里也提供了一个切口,供开发者使用。


    
  1. public interface ImportBeanDefinitionRegistrar {
  2. public void registerBeanDefinitions(
  3. AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
  4. }
1 2 3 4 5

MyBatis中MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar。扫描Mapper接口所在的包,为每个接口生成特定的BeanDefinition

# MapperScannerRegistrar


    
  1. public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  2. //扫描Mapper接口所在的包
  3. AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
  4. ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
  5. ....
  6. //为每个接口生成特定的BeanDefinition
  7. scanner.doScan(StringUtils.toStringArray(basePackages));
  8. }
1 2 3 4 5 6 7 8

# ClassPathMapperScanner

在doScan方法为每个标记的Mapper接口生成一个BeanName。而实例化工厂都指定为MapperFactoryBean。只用调用其getObject()方法即可完成接口的实例化。


    
  1. public Set<BeanDefinitionHolder> doScan(String... basePackages) {
  2. Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
  3. if (beanDefinitions.isEmpty()) {
  4. logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
  5. } else {
  6. //开始配置自己的规则
  7. processBeanDefinitions(beanDefinitions);
  8. }
  9. return beanDefinitions;
  10. }
  11. private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
  12. GenericBeanDefinition definition;
  13. for (BeanDefinitionHolder holder : beanDefinitions) {
  14. definition = (GenericBeanDefinition) holder.getBeanDefinition();
  15. //设置每个Bean的工厂类,MapperFactoryBean
  16. definition.setBeanClass(this.mapperFactoryBean.getClass());
  17. ...
  18. }
  19. }
  20. }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

我们举一个例子


    
  1. @Mapper
  2. public interface TUserMapper {
  3. int insert(TUser record);
  4. List<TUser> selectAll();
  5. TUser selectOne(@Param("id") Integer id);
  6. TUser selectByName(@Param("name") String name);
  7. }
1 2 3 4 5 6 7 8 9 10 11

这里BeanDefinition的名字就是TUserMapper,而工厂方法就是MapperFactoryBean。如下伪代码,getBean("TUserMapper"),就是调用MapperFactoryBean.getObject(),而getBean("&TUserMapper")才是获取MapperFactoryBean的实例。


    
  1. @Test
  2. public void factoryBeanTest(){
  3. System.out.println(applicationContextTools.getApp().getBean("&TUserMapper"));
  4. //org.mybatis.spring.mapper.MapperFactoryBean@3ab6678b
  5. }
1 2 3 4 5

# 我们如何定制自己的解析的注解呢?

编写一个类似于MapperScan的注解类,MyMapperScan

注意: 自定义的注解只能声明在配置类上才有效,配置类就是一定要被@Configuration修饰。


    
  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target(ElementType.TYPE)
  3. @Documented
  4. @Import(MyMapperScannerRegistrar.class)
  5. public @interface MyMapperScan {
  6. String value();
  7. }
  8. public class MyMapperScannerRegistrar implements ImportBeanDefinitionRegistrar {
  9. @Override
  10. public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  11. AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MyMapperScan.class.getName()));
  12. String value = annoAttrs.getString("value");
  13. System.out.println(value);
  14. System.out.println("配置类:"+importingClassMetadata.getClassName());
  15. }
  16. }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

# 验证


    
  1. @Configuration
  2. @MyMapperScan(value = "ConfigBean")
  3. public class ConfigBean {
  4. }
  5. @SpringBootApplication //被@Configuration修饰就等同于配置类
  6. @MyMapperScan(value = "test")
  7. @MapperScan(value = "orm.example.dal.mapper")
  8. public class LxchinesszzMybatisStudyApplication {
  9. public static void main(String[] args) {
  10. new SpringApplicationBuilder().web(WebApplicationType.NONE).run(args);
  11. }
  12. }
  13. ConfigBean
  14. 配置类:orm.example.ConfigBean
  15. test
  16. 配置类:orm.example.LxchinesszzMybatisStudyApplication
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

文章来源: springlearn.blog.csdn.net,作者:西魏陶渊明,版权归原作者所有,如需转载,请联系作者。

原文链接:springlearn.blog.csdn.net/article/details/125858083

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。