mybaits3 源码深度解析-mybatis与spring 整合

  • mybaits spring 实现原理
  • spring 容器启动过程

Spring 中常见类介绍

1 BeanDefinition 用于封装 spring bean 的配置信息

2 BeanDefinitionRegistry 用于存放解析后的BeanDefinition对象,spring 提供了扩展机制,允许用户在 spring 框架启动时动态的往BeanDefinitionRegistry容器中注册对象

3 BeanFactory 是 Spring 的Bean 工厂,负责 Bean 创建和属性注入

4 BeanFactoryPostProcessor 是 spring 提供的扩展机制,用于在所有 Bean 配置信息解析完成后,修改 Bean 工厂信息.例如向BeanDefinitionRegistry注册BeanDefinition对象

5 ImportBeanDefinitionRegistrar接口作用于 Spring 解析 Bean 的配置阶段,当解析@Configuration 注解时调用. 需要配合@Import 注解使用

6 BeanPostProcessor Bean 的后置处理器.在 Bean 初始化方法(init-method 属性指定的方法或者 afterPropertiesSet()方法)调用前后,会执行 BeanPostProcessor的拦截逻辑

7 ClassPathBeanDefinitionScanner 是BeanDefinition扫描器,能够对象指定包下的 Class 进行扫描,将 Class 转换为BeanDefinition对象注册到BeanDefinitionRegistry 容器中

8 FactoryBean 是 Spring 中的工厂 bean,通常用于处理 spring 中配置较为复杂或者由动态代理生成的 Bean 实例.

实现了该接口的 Bean 不能作为普通的 Spring bean 使用而是作为单个对象的工厂.

当通过 Bean 名称获取 FactoryBean 实例时,获取到的并不是FactoryBean对象本身,而是FactoryBean对象 getObject()方法返回的对象

  • BeanDefinition 用于封装 spring bean 的配置信息,spring bean 配置方式有 3 种:
//1 xml 方式配置
<beans>
    <bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>
//2 java 注解, 如:@Service @Component
//3 java config 方式 (spring 3.0 开始支持)
@Configuration
public class AppConfig {
    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }
}

Spring 容器在启动的时候,首先会对 Bean 的配置进行解析,把 Bean 的配置信息转换为BeanDefinition对象.

BeanDefinition是一个接口,通过不同的实现类来描述不同方式配置的 Bean 信息

  • BeanFactoryPostProcessor 是 spring 提供的扩展机制,用于在所有 Bean 配置信息解析完成后,修改 Bean 工厂信息.例如向BeanDefinitionRegistry注册BeanDefinition对象
@FunctionalInterface
public interface BeanFactoryPostProcessor {
   /**
    * 解析完Bean 配置并且注册到BeanDefinitionRegistry容器后,
    * 
    * 就会获取BeanFactoryPostProcessor接口的所有实现类,调用所有BeanFactoryPostProcessor对象的postProcessBeanFactory()方法
    * @param beanFactory
    * @throws BeansException
    */
   void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
  • BeanPostProcessor Bean 的后置处理器.在 Bean 初始化方法(init-method 属性指定的方法或者 afterPropertiesSet()方法)调用前后,会执行 BeanPostProcessor的拦截逻辑
public interface BeanPostProcessor {
   @Nullable
   default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
      return bean;
   }
   @Nullable
   default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
      return bean;
   }
}

spring 容器启动过程

  1. 解析所有Bean 配置信息,包括 xml 配置,java 注解以及 java config方式配置的 Bean,把 Bean 的配置信息转换为BeanDefinition对象.,注册到 BeanDefinitionRegistry容器中
  2. BeanDefinitionRegistry获取实现了BeanFactoryPostProcessor接口的 Bean 定义,然后实例化 Bean.调用所有BeanFactoryPostProcessor对象的 postProcessBeanFactory()方法
  3. 根据 BeanDefinitionRegistry 容器中的BeanDefinition对象.实例化所有的单例 bean,并且对 bean 的属性进行填充
  4. 执行所有实现了BeanPostProcessor接口的postProcessBeforeInitialization()方法
  5. 执行bean的初始化方法(初始化方法: 包括配置bean的 init-method方法或者通过实现 InitializingBean接口重写的afterPropertiesSet()方法)
  6. 执行所有实现了BeanPostProcessor接口的postProcessAfterInitialization()方法

mybatis 与 spring 整合

  • mybatis-spring 中文文档 //mybatis.org/spring/zh/index.html
  • 什么是 MyBatis-Spring?

MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。它将允许 MyBatis 参与到 Spring 的事务管理之中,创建映射器 mapper 和 SqlSession 并注入到 bean 中,以及将 Mybatis 的异常转换为 Spring 的 DataAccessException

  • 要和 Spring 一起使用 MyBatis,需要在 Spring 应用上下文中定义至少两样东西:一个 SqlSessionFactory 和至少一个数据映射器类。
// 1 引入 MyBatis-Spring 依赖,只需要在类路径下包含 mybatis-spring-2.0.7.jar 文件和相关依赖即可
<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis-spring</artifactId>
  <version>2.0.7</version>
</dependency>

// java config 方式配置 SqlSessionFactory,MapperScannerConfigurer
@Configuration
@MapperScan("org.mybatis.spring.sample.mapper") //扫描 mapper 文件
public class MyBatisConfig {
  @Bean
  public SqlSessionFactory sqlSessionFactory() throws Exception {
      SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
      factoryBean.setDataSource(dataSource());
      return factoryBean.getObject();
  }
  @Bean
  public SqlSessionTemplate sqlSession() throws Exception {
  		return new SqlSessionTemplate(sqlSessionFactory());
  }
}

mybatis-spring 中 Mapper 动态代理对象注册过程

  • mybatis-spring 模块提供了@MapperScan注解,该注解用于扫描指定包下的 Mapper 接口并创建 Mapper 动态代理对象
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
//通过@Import 导入 BeanDefinition 注册类 MapperScannerRegistrar
@Import({MapperScannerRegistrar.class})
public @interface MapperScan {
  	//扫描包路径
    String[] value() default {};
	  //扫描包路径
    String[] basePackages() default {};
    //扫描Mapper 接口对应的 class 对象
    Class<?>[] basePackageClasses() default {};
		//bean 名称生成策略
    Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
		//只扫描 某种注解修饰的类或者接口
    Class<? extends Annotation> annotationClass() default Annotation.class;
	  //只扫描某种类型的子类型
    Class<?> markerInterface() default Class.class;
		//指定使用哪个sqlSessionTemplate对象
    String sqlSessionTemplateRef() default "";
    String sqlSessionFactoryRef() default "";
    Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
}

@MapperScan注解通过@Import 导入 BeanDefinition 注册类 MapperScannerRegistrar

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
  private ResourceLoader resourceLoader;
  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    //工具类,用于获取MapperScan注解属性值
    AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
   // 创建spring ClassPathMapperScanner对象
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
		... 配置了xxx 属性则赋值给ClassPathMapperScanner对象
    scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
    scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));

    List<String> basePackages = new ArrayList<String>();
    for (String pkg : annoAttrs.getStringArray("value")) {
      if (StringUtils.hasText(pkg)) {
        basePackages.add(pkg);
      }
    }
    for (String pkg : annoAttrs.getStringArray("basePackages")) {
      if (StringUtils.hasText(pkg)) {
        basePackages.add(pkg);
      }
    }
    for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
      basePackages.add(ClassUtils.getPackageName(clazz));
    }
    scanner.registerFilters();
    //扫描路径下的 java 接口 bean 注册到 spring 容器
    scanner.doScan(StringUtils.toStringArray(basePackages));
    //...接下来去学习 spring 吧
  }


}

spring 学习文档:

  • spring 官方文档中文版 https://www.springcloud.cc/spring-reference.html
  • Spring Framework 5 中文文档 https://www.cntofu.com/book/95/index.html
  • mybatis-spring 中文文档 //mybatis.org/spring/zh/index.html

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注