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); }
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接口中定义了两个方法,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类实现了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()方法中,做了如下几部操作
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动态代理对象。