• mybatis源码(十二) mybatis-spring的实现原理


    mybatis源码(十二) mybatis-spring的实现原理

    1.Spring中的一些概念:

      1.BeanDefinition:用于描述springbean的配置信息,spring配置Bean的方式通常有3种。

      • xml配置文件的方式
      • java注解的方式。例如@service注解等
      • javaConfig的方式

      spring容器启动后,首先会对bean的配置信息进行解析。把bean的配置信息转换为BeanDefinition对象。BeanDefiniton是一个接口。通过不同的实现类来描述不同方式的配置的Bean信息

      2.BeanDefinitionRegistry:BeanDefinitionRegistry是BeanDefinition容器,所有的Bean配置解析后生成的BeanDefinition对象都会注册到BeanDefinitionRegistry对象中。Spring提供了扩展机制,允许用户在Spring框架启动时,往BeanDefinitionRegistry容器中注册BeanDefinition对象。

      3.BeanFactory:BeanFactory是Spring的Bean工厂,负责Bean的创建及属性注入。它同时是一个Bean容器,Spring框架启动后,会根据BeanDefinition对象创建Bean实例,所有的单例Bean都会注册到BeanFactory容器中。

      4.BeanFactoryPostProcessor:BeanFactoryPostProcessor是Spring提供的扩展机制,用于在所有的Bean配置信息解析完成后修改Bean工厂信息。例如,向BeanDefinitionRegistry容器中增加额外的BeanDefinition对象,或者修改原有的BeanDefinition对象。BeanFactoryPostProcessor是一个接口,该接口中只有一个方法,postProcessBeanFactory。当我们配置的Bean实现该接口时,Spring解析Bean配置完成后,就会调用所有BeanFactoryPostProcessor实现类的postProcessBeanFactory()方法。

      5.importBeanDefinitionRegistry:ImportBeanDefinitionRegistrar是一个接口, 该接口的实现类作用于Spring解析Bean的配置阶段,当解析@Configuration注解时,可以通过ImportBeanDefinitionRegistrar接口的实BeanDefinitionRegistry容器中添加额外的BeanDefinition对象。ImportBeanDefinitionRegistrar接口定义如下:ImportBeanDefinitionRegistrar接实现类的registerBeanDefinitions()方法会在Spring解析@Configuration注解时调用。ImportBeanDefinitionRegistrar 接口需要配合@Import注解使用,importingClassMetadata参数为@Import所在注解的配置信息,registry参数为BeanDefinition容器。

    public interface ImportBeanDefinitionRegistrar {
    
        /**
         * Register bean definitions as necessary based on the given annotation metadata of
         * the importing {@code @Configuration} class.
         * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
         * registered here, due to lifecycle constraints related to {@code @Configuration}
         * class processing.
         * @param importingClassMetadata annotation metadata of the importing class
         * @param registry current bean definition registry
         */
        public void registerBeanDefinitions(
                AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
    
    }
    ImportBeanDefinitionRegistrar

       6.BeanPostProcessor:Bean的后置处理器,在Bean初始化方法(init-method属性指定的方法或afterPropertiesSet()方法)调用前后,会执行BeanPostProcessor中定义的拦截逻辑。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;
        }
    }
    BeanPostProcessor

    BeanPostProcessor接口中定义了两个方法,postProcessBeforelnitialization()方法会在所有Bean初始化方法调用之前执行,postProcessAfterlnitialization()方法会在所有Bean的初始化方
    法调用之后执行。BeanPostProcessor通常用于处理Spring Bean对应的Java类中的注解信息或者创建Bean的代理对象。

      7.ClassPathBeanDefinitionScanner:ClassPathBeanDefinitionScanner是BeanDefinition扫描器,能够对指定包下的Class进行扫描,将Class信息转换为BeanDefinition对象注册到BeanDefinitionRegistry容器中ClassPathBeanDefinitionScanner支持自定义的过滤规则,例如我们可以只对使用某种注解的类进行扫描。Spring中的@Service、@Component等注解配置Bean都是通过ClassPathBeanDefinitionScanner实现的。MyBatis Spring 模块中Mapper接口的扫描使用到了ClassPathBeanDefinitionScanner类

       8.FactoryBean:FactoryBean是Spring中的工厂Bean, 通常用于处理Spring中配置较为复杂或者由动态代理生成的Bean实例。实现了该接口的Bean不能作为普通的Bean使用,而是作为单个对象的工厂。当我们通过Bean名称获取FactoryBean实例时,获取到的并不是FactoryBean对象本身,而是FactoryBean对象的getObject()方 法返回的实例。例如如下Bean配置:

    <bean id="sqlSessionFactory" class="org.mybatis.spring.Sq1SessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    </bean>

    SqlSessionFactoryBean是一个FactoryBean,通过名称sqlSessionFactory从Spring容器中获取Bean时,获取到的实际上是SqlSessionFactoryBean对象的getObject()方法返回的对象。

    2.spring容器启动过程

    Spring框架的启动过程大致可以分为以下几步:
      (1) 对所有Bean的配置信息进行解析,其中包括XML配置文件、Java注解以及Java Config方式配置的Bean。将Bean的配置信息转换为BeanDefinition对象,注册到BeanDefinitionRegistry容器中。
      (2) 从BeanDefinitionRegistry 容器中获取实现了BeanFactoryPostProcessor接口的Bean定义,然后实例化Bean,调用所有BeanFactoryPostProcessor对象的postProcessBeanFactory()方
           法,在postProcessBeanFactory()方法中可以对Bean工厂的信息进行修改。
      (3) 根据BeanDefinitionRegistry容器 中的BeanDefinition对象实例化所有的单例Bean,并对Bean的属性进行填充。
      (4) 执行所有实现了BeanPostProcessor 接口的Bean的postProcessBeforelnitialization()方法。 该方法中可以对原始的Bean进行包装。
      (5) 执行Bean的初始化方法,初始化方法包括配置Bean时通过init-method属性指定的方法,或者通过实现lnitializingBean接口重写的afterPropertiesSet()方法。
      (6) 执行所有实现了BeanPostProcessor接口的Bean的postProcessAferlnitialization()方法。

      

     3.mapper动态代理注册过程

     我们在做项目的时候,经常会用到@MapperScan注解扫描指定的mapper接口,那么他的内部是怎么做的呢

    首先看到@MapperScan的源码

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import(MapperScannerRegistrar.class)
    @Repeatable(MapperScans.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;
      // 指定使用哪个SQLSessionTempleate对象
      String sqlSessionTemplateRef() default "";
      // 指定使用哪个sqlSessionFactory对象
      String sqlSessionFactoryRef() default "";
      // 指定使用自定义的MapperFactoryBean 返回mybatis 代理对象作为spring的bean
      Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
    
    }
    

    通过源码,注意到,使用了一个 @Import(MapperScannerRegistrar.class) ,导入了一个MapperScannerRegistrar类。

    进入该类,我们发现该类实现了ImportBeanDefinitionRegistrar 接口 Spring中的ImportBeanDefinitionRegistrar用于在Spring解析Bean的配置阶段往BeanDefinitionRegistry容器中注册额外的BeanDefinition对象。

    那么MapperScannerRegister做了哪些操作呢

     1 public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
     2 
     3   private ResourceLoader resourceLoader;
     4 
     5   /**
     6    * {@inheritDoc}
     7    */
     8   @Override
     9   public void setResourceLoader(ResourceLoader resourceLoader) {
    10     this.resourceLoader = resourceLoader;
    11   }
    12 
    13   /**
    14    * {@inheritDoc}
    15    */
    16   @Override
    17   public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    18     AnnotationAttributes mapperScanAttrs = AnnotationAttributes
    19         .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    20     if (mapperScanAttrs != null) {
    21       // 调用registerBeanDefinitions()方法注册BeanDefinition对象
    22       registerBeanDefinitions(mapperScanAttrs, registry);
    23     }
    24   }
    25 
    26   void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry) {
    27     // ClassPathMapperScanner是Mybatis Spring模块自定义的BeanDefinition扫描器
    28     ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    29 
    30     if (resourceLoader != null) {
    31       scanner.setResourceLoader(resourceLoader);
    32     }
    33 
    34     Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
    35     if (!Annotation.class.equals(annotationClass)) {
    36       scanner.setAnnotationClass(annotationClass);
    37     }
    38 
    39     Class<?> markerInterface = annoAttrs.getClass("markerInterface");
    40     if (!Class.class.equals(markerInterface)) {
    41       scanner.setMarkerInterface(markerInterface);
    42     }
    43 
    44     Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
    45     if (!BeanNameGenerator.class.equals(generatorClass)) {
    46       scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
    47     }
    48 
    49     // 获取注解配置信息
    50     Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
    51     if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
    52       scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
    53     }
    54 
    55     scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
    56     scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
    57 
    58     List<String> basePackages = new ArrayList<>();
    59     basePackages.addAll(
    60         Arrays.stream(annoAttrs.getStringArray("value"))
    61             .filter(StringUtils::hasText)
    62             .collect(Collectors.toList()));
    63 
    64     basePackages.addAll(
    65         Arrays.stream(annoAttrs.getStringArray("basePackages"))
    66             .filter(StringUtils::hasText)
    67             .collect(Collectors.toList()));
    68     // 添加需要扫描的包
    69     basePackages.addAll(
    70         Arrays.stream(annoAttrs.getClassArray("basePackageClasses"))
    71             .map(ClassUtils::getPackageName)
    72             .collect(Collectors.toList()));
    73     // 注册扫描过滤规则
    74     scanner.registerFilters();
    75     // 对包中的类进行扫描生成BeanDefinition对象
    76     scanner.doScan(StringUtils.toStringArray(basePackages));
    77   }
    78 
    79   /**
    80    * A {@link MapperScannerRegistrar} for {@link MapperScans}.
    81    * @since 2.0.0
    82    */
    83   static class RepeatingRegistrar extends MapperScannerRegistrar {
    84     /**
    85      * {@inheritDoc}
    86      */
    87     @Override
    88     public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
    89         BeanDefinitionRegistry registry) {
    90       AnnotationAttributes mapperScansAttrs = AnnotationAttributes
    91           .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScans.class.getName()));
    92       if (mapperScansAttrs != null) {
    93         Arrays.stream(mapperScansAttrs.getAnnotationArray("value"))
    94             .forEach(mapperScanAttrs -> registerBeanDefinitions(mapperScanAttrs, registry));
    95       }
    96     }
    97   }
    98 
    99 }
    MapperScannerRegistrar

    如上面的代码所示,MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口的registerBeanDefinitions()方法,
    该方法调用了重载的registerBeanDefinitions()进行处理。在重载方法中,首先创建了一个ClassPathMapperScanner对象,然后获取MapperScan注解的属性信息,根据MapperScan的annotationClass和
    markerlnterface属性对扫描的Class进行过滤,最后调用ClassPathMapperScanner对象的doScan()方法进行扫描。ClassPathMapperScanner是Spring中ClassPathBeanDefinitionScanner的子类,用于扫描特定包下的Mapper接口,将Mapper接口信息转换为对应的BeanDefinition对象。下面是ClassPathMapperScanner 类doScan()方法的实现:

     1 /**
     2    * Calls the parent search that will search and register all the candidates.
     3    * Then the registered objects are post processed to set them as
     4    * MapperFactoryBeans
     5    */
     6   @Override
     7   public Set<BeanDefinitionHolder> doScan(String... basePackages) {
     8     // 调用父类的doScan()方法,將包中的Class转换为BeanDefinitionHolder对象
     9     Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
    10 
    11     if (beanDefinitions.isEmpty()) {
    12       LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
    13     } else {
    14       // 对BeanDefinitionHolder进行处理
    15       processBeanDefinitions(beanDefinitions);
    16     }
    17 
    18     return beanDefinitions;
    19   }
    20 
    21   private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    22     GenericBeanDefinition definition;
    23     for (BeanDefinitionHolder holder : beanDefinitions) {
    24       // 获取BeanDefinition对象
    25       definition = (GenericBeanDefinition) holder.getBeanDefinition();
    26       String beanClassName = definition.getBeanClassName();
    27       LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName()
    28           + "' and '" + beanClassName + "' mapperInterface");
    29 
    30       // the mapper interface is the original class of the bean
    31       // but, the actual class of the bean is MapperFactoryBean
    32       definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
    33       // 將BeanDefinition对象的beanClass属性设置为MapperFactoryBean
    34       definition.setBeanClass(this.mapperFactoryBean.getClass());
    35       // 修改BeanDefinition对象的propertyValues属性,將sqlSessionFactory注入到MapperFactoryBean中
    36       definition.getPropertyValues().add("addToConfig", this.addToConfig);
    37 
    38       boolean explicitFactoryUsed = false;
    39       if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
    40         definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
    41         explicitFactoryUsed = true;
    42       } else if (this.sqlSessionFactory != null) {
    43         definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
    44         explicitFactoryUsed = true;
    45       }
    46 
    47       if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
    48         if (explicitFactoryUsed) {
    49           LOGGER.warn(() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
    50         }
    51         definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
    52         explicitFactoryUsed = true;
    53       } else if (this.sqlSessionTemplate != null) {
    54         if (explicitFactoryUsed) {
    55           LOGGER.warn(() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
    56         }
    57         definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
    58         explicitFactoryUsed = true;
    59       }
    60 
    61       if (!explicitFactoryUsed) {
    62         LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
    63         definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
    64       }
    65     }
    66   }
    ClassPathMapperScanner.doScan()

    如上面的代码所示,在ClassPathMapperScanner类的doScan()方法中,做了如下几部操作
      1.首先调用父类的doScan()方法,将指定包下的Mapper接口信息转换为BeanDefinitionHolder对象,BeanDefinitionHolder 中持有一个BeanDefinition对象及Bean的名称和所有别名。
      2.所有的Mapper接口转换为BeanDefinitionHolder对象后,接着调用processBeanDefinitions()方法,对所有BeanDefinitionHolder对象进行处理。
      3.在processBeanDefinitions()方法中,对所有BeanDefinitionHolder对象进行遍历,获取BeanDefinitionHolder对象中持有的BeanDefinition对象。然后对BeanDefinition对象的信息进行修改, 将
    BeanDefinition对象的beanClass属性设置为MapperFactoryBean,并向BeanDefinition 对象中增加几个PropertyValue对象,对应MapperFactoryBean的addToConfig和sq|SessionTemplate等属性。
    将BeanDefinition 对象的beanClass 属性设置为MapperFactoryBean这一步很重要, 当Spring将所有的Bean配置信息转换为BeanDefinition对象后,就会根据BeanDefinition对象来实例化Bean。
    由于BeanDefinition对象的beanClass属性被设置为MapperFactoryBean,因此Spring在创建Bean时实例化的是MapperFactoryBean对象。Spring 会根据BeanDefinition对象中的PropertyValues对象对MapperFactoryBean对象进行属性填充,因MapperFactoryBean对象的addToConfig和sqlSessionTemplate属性会被自动注入。MapperFactoryBean实现了FactoryBean接口。Spring中的FactoryBean, FactoryBean是单个Bean的工厂Bean, 当我们根据Mapper类型从Spring容器中获取FactoryBean时,获取到的并不是FactoryBean本身,而是FactoryBean的getObject()方法返回的对象。

      /**
       * {@inheritDoc}
       */
      @Override
      public T getObject() throws Exception {
        return getSqlSession().getMapper(this.mapperInterface);
      }
    

    MapperFactoryBean的getObject()方法中,调用SqlSession对象的getMapper()方 法返回一个Mapper动态代理对象。  

  • 相关阅读:
    springboot系列九,springboot整合邮件服务、整合定时任务调度
    springboot系列八、springboot整合kafka
    springboot系列六、springboot配置错误页面及全局异常
    springboot系列五、springboot常用注解使用说明
    springboot系列四、配置模板引擎、配置热部署
    springboot系列三、springboot 单元测试、配置访问路径、多个配置文件和多环境配置,项目打包发布
    springboot系列二、springboot项目搭建
    springboot系列一、springboot产生背景及介绍
    kafka系列十、kafka常用管理命令
    Jmeter4.X
  • 原文地址:https://www.cnblogs.com/yingxiaocao/p/13698185.html
Copyright © 2020-2023  润新知