Spring IOC(十一)Mybatis-Spring 驱动原理

前面有几篇单独讲Mybatis的,单独使用Mybatis,有以下几步:

  1. 配置Mybatis-config文件,类似于下面:
<configuration>
    <properties>
        <property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/> <!-- 启用默认值特性 -->
    </properties>

    <typeAliases>
        <!-- 设置别名 -->
        <typeAlias alias="User" type="anla.learn.mybatis.hello.model.auto.User" />
    </typeAliases>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1/df"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mappers/UserMapper.xml"/>
    </mappers>
</configuration>
  1. 在上面配置的Mapper 中配置对应的UserMapper.xml,配置好NameSpace
  2. 使用SqlSessionFactory打开一个Session,并使用Mapper
        try (SqlSession session = sqlSessionFactory.openSession()) {
            UserMapper mapper = session.getMapper(UserMapper.class);
            User user = mapper.selectUser(1L);
            System.out.println(user);
        }

Mybatis提供了基于Spring的继承包,将Mapper 放入到了Spring 容器中统一管理,本篇文章以此分析。

Mybatis-Spring

使用Mybatis-Spring 时,有以下几个步骤:

  1. 声明一个SqlSessionFactory bean,可以使用 SqlSessionFactoryBean 待为生成
  2. 声明mapper location位置。
  3. 可以使用 @Bean 方式声明一个Mapper
    @Bean
    public UserMapper userMapper() throws Exception {
        SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory());
        return sqlSessionTemplate.getMapper(UserMapper.class);
    }
  1. 或者通过 @MapperScan 扫描Mapper类。

本文重点看待2个逻辑

  1. MapperScan 工作机制
  2. SqlSessionFactoryBean 原理
MapperScan 工作机制

MapperScan 注解定义为:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
...
}

MapperScan 使用 Import引入了 MapperScannerRegistrar bean。另一方面, MapperScan 仅在 MapperScannerRegistrar 解析并使用。

MapperScannerRegistrar

MapperScannerRegistrar 定义为:

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
... 
}

ImportBeanDefinitionRegistrar 将在 invokeBeanFactoryPostProcessors 中被同步调用:

  1. ConfigurationClassPostProcessor 对所有 已注册bean的 @ConponentConfiguration注解进行扫描
  2. 扫描到 MybatisCommonConfiguration,因为它上面有 MapperScan 注解
  3. 继而会对 加载 其 @Import 注解内容,并使用registry.registerBeanDefinition放入容器中,这一步在:
ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass
  1. 而后将对所有@Import 内容执行其 registerBeanDefinitions
	private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
		registrars.forEach((registrar, metadata) ->
				registrar.registerBeanDefinitions(metadata, this.registry));
	}
  1. 最终就到了 MapperScannerRegistrarregisterBeanDefinitions 中。
  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    AnnotationAttributes mapperScanAttrs = AnnotationAttributes
        .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    if (mapperScanAttrs != null) {
      registerBeanDefinitions(mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0));
    }
  }

从上面一小节分析来看, @MapperScan 目的是为了引入 MapperScannerRegistrar,而最终是到了 registerBeanDefinitions 方法中。
该方法将 读取 @MapperScan 所有参数,并向Spring 注入一个 MapperScannerConfigurer 类型 BeanDefinition

  void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {
	// 使用 BeanDefinitionBuilder 进行构建
    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
    builder.addPropertyValue("processPropertyPlaceHolders", true);
	// 获取 annotationClass,即只扫描 有特定注解的类。否则扫描所有接口
    Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
    if (!Annotation.class.equals(annotationClass)) {
      builder.addPropertyValue("annotationClass", annotationClass);
    }
	// 父类,即只有某一个父类下类才会被注入
    Class<?> markerInterface = annoAttrs.getClass("markerInterface");
    if (!Class.class.equals(markerInterface)) {
      builder.addPropertyValue("markerInterface", markerInterface);
    }
	// bena 命名器
    Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
    if (!BeanNameGenerator.class.equals(generatorClass)) {
      builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));
    }
	// 自定义MapperFactoryBean
    Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
    if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
      builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
    }
	// 自定义SqlSessionFtemplate
    String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
    if (StringUtils.hasText(sqlSessionTemplateRef)) {
      builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
    }
	// 自定义sqlSessionFactory
    String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");
    if (StringUtils.hasText(sqlSessionFactoryRef)) {
      builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));
    }
// 将所有的factory加入到一起
    List<String> basePackages = new ArrayList<>();
    basePackages.addAll(
        Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));

    basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText)
        .collect(Collectors.toList()));

    basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName)
        .collect(Collectors.toList()));
	// 判断是否懒加载
    String lazyInitialization = annoAttrs.getString("lazyInitialization");
    if (StringUtils.hasText(lazyInitialization)) {
      builder.addPropertyValue("lazyInitialization", lazyInitialization);
    }
    builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
	// 注册bean
    registry.registerBeanDefinition(beanName, builder.getBeanDefinition());

  }
MapperScannerConfigurer

现在 MapperScannerConfigurer 注入进去了,就有一个问题了,那它何时被调用呢?其功能是啥?

public class MapperScannerConfigurer
    implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
}

从上面可以获取以下两个信息:

  1. BeanDefinitionRegistryPostProcessor 类型bean,即在 refresh时候,其 postProcessBeanDefinitionRegistry会在PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors 被调用。具体可以看前面文章中对 ConfigurationClassPostProcessor 分析。
  2. InitializingBean 类型,即在 该 bean BeanDefinitionRegistryPostProcessor 实例化后,设置值时候,其 afterPropertiesSet 方法会被调用。

下面从上面两个点看其内部处理逻辑。

postProcessBeanDefinitionRegistry
  @Override
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    if (this.processPropertyPlaceHolders) {
    // 如果 BeanDefinitionRegistries 在refresh对 BeanFactoryPostProcessors 处理前调用,则需要设置一些属性,否则会报错。
      processPropertyPlaceHolders();
    }
    // 设置 ClassPathMapperScanner 相关属性
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    scanner.setAddToConfig(this.addToConfig);
    scanner.setAnnotationClass(this.annotationClass);
    scanner.setMarkerInterface(this.markerInterface);
    scanner.setSqlSessionFactory(this.sqlSessionFactory);
    scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
    scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
    scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
    scanner.setResourceLoader(this.applicationContext);
    scanner.setBeanNameGenerator(this.nameGenerator);
    scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
    if (StringUtils.hasText(lazyInitialization)) {
      scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
    }
    // 注册过滤类性FilterType
    scanner.registerFilters();
    // 进行扫描
    scanner.scan(
        StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  }
  1. 判断是否在 容器 处理 BeanFactoryPostProcessors 前调用,是,则需要设置一些属性
  2. 构造 ClassPathMapperScanner 并 对所有basePackage进行扫描

ClassPathMapperScanner 中扫描则是对Spring 的 ClassPathBeanDefinitionScanner 进行一层封装:

	public int scan(String... basePackages) {
	// 获取当前bean数量
		int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
		// 扫描
		doScan(basePackages);
		if (this.includeAnnotationConfig) {
		// 如果需要,注册一遍默认的processors
			AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
		}

		return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
	}

最终,Mybatis对 doScan进行了一层包装,不重复造轮子,还是通过Spring 进行扫描:

  @Override
  public Set<BeanDefinitionHolder> doScan(String... basePackages) {
  // 使用Spring 进行扫描
    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 {
    // 扫描后对bean的处理
      processBeanDefinitions(beanDefinitions);
    }

    return beanDefinitions;
  }

doScan 方法前面有类似分析,即扫描初所需要所有bean,主要看 processBeanDefinitions 方法:

  private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
    // 对bean进行解析
      definition = (GenericBeanDefinition) holder.getBeanDefinition();
      // 获取bean名字
      String beanClassName = definition.getBeanClassName();
      LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
          + "' mapperInterface");
      // 使用 mapperFactoryBeanClass 代替该bean,即这个bean,有名无实
      definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
      definition.setBeanClass(this.mapperFactoryBeanClass);
	// 添加属性
      definition.getPropertyValues().add("addToConfig", this.addToConfig);

      boolean explicitFactoryUsed = false;
      if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
        definition.getPropertyValues().add("sqlSessionFactory",
            new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
        explicitFactoryUsed = true;
      } else if (this.sqlSessionFactory != null) {
        definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
        explicitFactoryUsed = true;
      }

      if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
        if (explicitFactoryUsed) {
          LOGGER.warn(
              () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
        }
        definition.getPropertyValues().add("sqlSessionTemplate",
            new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
        explicitFactoryUsed = true;
      } else if (this.sqlSessionTemplate != null) {
        if (explicitFactoryUsed) {
          LOGGER.warn(
              () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
        }
        definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
        explicitFactoryUsed = true;
      }

      if (!explicitFactoryUsed) {
        LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
        // 设置为按类型注入
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      }
      definition.setLazyInit(lazyInitialization);
    }
  }

processBeanDefinitions 获取到了重要的信息:

  1. Mybatis会将所有beanClass 设置为 mapperFactoryBeanClass
  2. 配置 部分 属性
  3. 将该bean 设置为 按类型注入

这就牵扯到了 FactoryBean 类型的初始化:
先看看FactoryBean的定义:

public interface FactoryBean<T> {
	// 返回bean实例
	@Nullable
	T getObject() throws Exception;

	// 返回class类型
	@Nullable
	Class<?> getObjectType();

	// 是否为单例,默认是true
	default boolean isSingleton() {
		return true;
	}

}

举个例子来说,比如要将 SimpleDateFormat 放入Spring bean容器中管理,那么可以用 MapperFactoryBean 实现:

public class DemoDateFormatFactoryBean implements FactoryBean<DateFormat>, InitializingBean {

    private DateFormat dateFormat = new SimpleDateFormat();

    @Override
    public DateFormat getObject() throws Exception {
        return dateFormat;
    }

    @Override
    public Class<?> getObjectType() {
        return dateFormat.getClass();
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        // Asia/Shanghai
        dateFormat.setTimeZone(TimeZone.getTimeZone(ZoneId.SHORT_IDS.get("CTT")));
    }
}

先看看 MapperFactoryBean 的定义:

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {

很明显是装饰器模式,所有操作都交给了 SqlSessionDaoSupport 执行。MapperFactoryBean 只做了一层包装。

直接看 geObject 方法:

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

熟悉的代码,从 SqlSession 中获取对应的Mapper。
这样就完成了 将 Mybatis的Mapper ,统一交给Spring 管理的操作。
实际就是套了一层MapperFactoryBean。

FactoryBean初始化

在 Spring refresh最后阶段,会将所有bean都执行一遍getBean, FactoryBean类型 的bean 会立刻通过getBean获取,而实际的bean 并不会一开始就执行getBean初始化,而是使用时才会被初始化:

	@Override
	public void preInstantiateSingletons() throws BeansException {
		if (logger.isTraceEnabled()) {
			logger.trace("Pre-instantiating singletons in " + this);
		}

		// Iterate over a copy to allow for init methods which in turn register new bean definitions.
		// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
		List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

		// Trigger initialization of all non-lazy singleton beans...
		for (String beanName : beanNames) {
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				if (isFactoryBean(beanName)) {
				// 利用& 符号工厂bean
					Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
					if (bean instanceof FactoryBean) {
						final FactoryBean<?> factory = (FactoryBean<?>) bean;
						boolean isEagerInit;
						if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
							isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
											((SmartFactoryBean<?>) factory)::isEagerInit,
									getAccessControlContext());
						}
						else {
							// 判断是否需要立即加载
							isEagerInit = (factory instanceof SmartFactoryBean &&
									((SmartFactoryBean<?>) factory).isEagerInit());
						}
						if (isEagerInit) {
							getBean(beanName);
						}
					}
				}
				else {
					getBean(beanName);
				}
			}
		}

		// 实例化以及初始化后执行  afterSingletonsInstantiated 
		for (String beanName : beanNames) {
			Object singletonInstance = getSingleton(beanName);
			if (singletonInstance instanceof SmartInitializingSingleton) {
				final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
				if (System.getSecurityManager() != null) {
					AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
						smartSingleton.afterSingletonsInstantiated();
						return null;
					}, getAccessControlContext());
				}
				else {
					smartSingleton.afterSingletonsInstantiated();
				}
			}
		}
	}

getBean 时候,如果判定 为FactoryBean类型,则最终使用getObject 返回实例对象,判断方式如下:

	@Override
	public boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException {
		String beanName = transformedBeanName(name);
		Object beanInstance = getSingleton(beanName, false);
		if (beanInstance != null) {
			return (beanInstance instanceof FactoryBean);
		}
		// No singleton instance found -> check bean definition.
		if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory) {
			// No bean definition found in this factory -> delegate to parent.
			return ((ConfigurableBeanFactory) getParentBeanFactory()).isFactoryBean(name);
		}
		// 上面是直接instanceof,而如果都没有则去beanFactory中mergedBeanDefinitions 寻找
		return isFactoryBean(beanName, getMergedLocalBeanDefinition(beanName));
	}

上面是直接instanceof,而如果都没有则去beanFactory中mergedBeanDefinitions 寻找

	protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
		// Quick check on the concurrent map first, with minimal locking.
		RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
		if (mbd != null) {
			return mbd;
		}
		return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
	}

而bean注册时,beanFactory会缓存下自己和parent bean definition相关信息在 mergedBeanDefinitions 中。

觉得博主写的有用,不妨关注博主公众号: 六点A君。
哈哈哈,一起研究Spring:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值