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动态代理对象。  

原文地址:https://www.cnblogs.com/yingxiaocao/p/13698185.html