利用FactoryBean接口实例化,来实现dubbo接口调用和mybatis接口调用

简介: Java编程规范中声明,Java接口类是不能直接实例化的,但是我们在平时的开发中经常会遇到只声明接口就可以直接使用的。eg:1. Mybatis中只用使用`@MapperScan`声明要扫描的Mapper接口类就可以直接从Spring中获取使用,进行操作数据库2. Dubbo中只要用Dubbo提供的`@Service`注解,同样可以直接从Spring中获取使用进行远程调用。

文章头

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

eg:

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

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

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


public interface FactoryBean<T> {
    @Nullable
    T getObject() throws Exception;

    @Nullable
    Class<?> getObjectType();

    default boolean isSingleton() {
        return true;
    }
}

在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的,但是这里也提供了一个切口,供开发者使用。


public interface ImportBeanDefinitionRegistrar {
    public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}

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));
  }

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());
      ...
    }
  }
}

我们举一个例子

@Mapper
public interface TUserMapper {

    int insert(TUser record);

    List<TUser> selectAll();

    TUser selectOne(@Param("id") Integer id);

    TUser selectByName(@Param("name") String name);
}

这里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
    }

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

编写一个类似于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());
    }
}

验证

@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
相关文章
|
2月前
|
SQL Java 数据库连接
mybatis使用四:dao接口参数与mapper 接口中SQL的对应和对应方式的总结,MyBatis的parameterType传入参数类型
这篇文章是关于MyBatis中DAO接口参数与Mapper接口中SQL的对应关系,以及如何使用parameterType传入参数类型的详细总结。
59 10
|
4月前
|
XML Java 数据库连接
MyBatis中的接口代理机制及其使用
【8月更文挑战第5天】MyBatis的接口代理机制是其核心功能之一,允许通过定义接口并在运行时生成代理对象来操作数据库。开发者声明一个带有`@Mapper`注解的接口,MyBatis则依据接口方法、映射配置(XML或注解)及数据库信息动态生成代理类。此机制分为四步:创建接口、配置映射文件或使用注解、最后在业务逻辑中注入并使用代理对象。这种方式简化了数据库操作,提高了代码的可读性和可维护性。例如,在电商系统中可通过`OrderMapper`处理订单数据,在社交应用中利用`MessageMapper`管理消息,实现高效且清晰的数据库交互。
|
5月前
|
Java 数据库连接 Maven
文本,使用SpringBoot工程创建一个Mybatis-plus项目,Mybatis-plus在编写数据层接口,用extends BaseMapper<User>继承实体类
文本,使用SpringBoot工程创建一个Mybatis-plus项目,Mybatis-plus在编写数据层接口,用extends BaseMapper<User>继承实体类
|
6月前
|
Dubbo Java 应用服务中间件
Spring Boot 调用 Dubbo 接口与编写 Dubbo 接口实战
Spring Boot 调用 Dubbo 接口与编写 Dubbo 接口实战
726 1
MybatisPlus--IService接口基本用法,MP提供了Service接口,save(T) 这里的意思是新增了一个T, saveBatch 是批量新增的意思,saveOrUpdate是增或改
MybatisPlus--IService接口基本用法,MP提供了Service接口,save(T) 这里的意思是新增了一个T, saveBatch 是批量新增的意思,saveOrUpdate是增或改
|
5月前
|
XML Java 数据格式
支付系统----微信支付20---创建案例项目--集成Mybatis-plus的补充,target下只有接口的编译文件,xml文件了,添加日志的写法
支付系统----微信支付20---创建案例项目--集成Mybatis-plus的补充,target下只有接口的编译文件,xml文件了,添加日志的写法
接口模板,文本常用的接口Controller层,常用的controller层模板,Mybatisplus的相关配置
接口模板,文本常用的接口Controller层,常用的controller层模板,Mybatisplus的相关配置
|
7月前
|
SQL
【MybatisPlus】条件构造器、自定义SQL、Service接口
【MybatisPlus】条件构造器、自定义SQL、Service接口
113 0
【MybatisPlus】条件构造器、自定义SQL、Service接口
|
6月前
|
存储 Java 数据库连接
SSMP整合案例第三步 业务层service开发及基于Mybatis的接口功能拓展
SSMP整合案例第三步 业务层service开发及基于Mybatis的接口功能拓展
45 0
|
7月前
|
SQL 存储 算法
Mybatis-Plus- CRUD接口-主键策略-自动填充和乐观锁-分页-逻辑删除-条件构造器和常用接口
Mybatis-Plus- CRUD接口-主键策略-自动填充和乐观锁-分页-逻辑删除-条件构造器和常用接口