MybatisSpring源码分析

编程

分析Mybatis如何利用Spring的扩展点集成到框架中的,Mybatis本身的扩展点不再本次分析范畴

构建环境

上Github上下载https://github.com/mybatis/spring。通过Git的方式试了几次没成功,后来直接Down的zip包,再导入的Idea中的。

导入的过程当中会有点慢,要下载不少东西。记得一定要修改Maven的配置文件和本地仓库地址,否则可能之前你已经下过的相关包会又下载到C盘的本地仓库当中

测试代码

直接在源码目录下新建了一个目录来写测试代码

测试类

@Configuration

@MapperScan("com.jv.mapper")

@ComponentScan("com.jv.scan")

public class TestMybatis {

public static void main(String[] args) {

AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestMybatis.class);

UserService bean = ac.getBean(UserService.class);

System.out.println(bean.query());

}

@Bean

public SqlSessionFactory sqlSessionFactory() throws Exception {

SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();

PooledDataSource dataSource = new PooledDataSource();

dataSource.setDriver("org.mariadb.jdbc.Driver");

dataSource.setUrl("jdbc:mysql://192.168.10.12:3306/acct?useSSL=false&serverTimezone=UTC");

dataSource.setUsername("dev01");

dataSource.setPassword("12340101");

factoryBean.setDataSource(dataSource);

return factoryBean.getObject();

}

}

Service类 

@Service

public class UserService {

@Autowired

private UserMapper userMapper;

public List<User> query(){

return userMapper.query();

}

}

Mapper类

public interface UserMapper {

@Select("SELECT name,age FROM user")

List<User> query();

}

实体类

@ToString

public class User {

private String name;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public Integer getAge() {

return age;

}

public void setAge(Integer age) {

this.age = age;

}

private Integer age;

}

注意:运行之前一定要修改pom.xml。因为Mybatis导入的Spring相关的依赖不在运行时生效

<scope>provided</scope>全部注释掉,否则运行的时候会报好不到类

表结构:

CREATE TABLE `user` (
  `name` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
  `age` int(4) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

insert into user(name,age) values("Messi",35);

commit;

源码分析

从mybatis-spring官方文档中可以找到@MapperScan的用法:http://mybatis.org/spring/mappers.html

注册BeanDefinition

既然和Spring集成是通过@MapperScan完成的,那从它入手

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.TYPE)

@Documented

@Import(MapperScannerRegistrar.class)

@Repeatable(MapperScans.class)

public @interface MapperScan {

....

}

其中@Import(MapperScannerRegistrar.class)是重点,再看MapperScannerRegistrar

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {

/**

* {@inheritDoc}

*

* @deprecated Since 2.0.2, this method not used never.

*/

@Override

@Deprecated

public void setResourceLoader(ResourceLoader resourceLoader) {

// NOP

}

/**

* {@inheritDoc}

*/

@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));

}

}

void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {

/**

* 注册一个MapperScannerConfigurer的BeanDefinition,MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor

* BeanDefinitionRegistryPostProcessor接口的实现类,一旦放入Spring容器中,那么在Spring容器启动的时候它可以担任注册自己需要BeanDefinition的功能

*/

BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);

builder.addPropertyValue("processPropertyPlaceHolders", true);

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);

}

//自定义BeanNameGenerator

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);

}

//自定义sqlSessionTemplate

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"));

}

//生成所有的scan要扫描的基础包路径

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));

//将MapperScannerConfigurer的BeanDefinition注册

registry.registerBeanDefinition(beanName, builder.getBeanDefinition());

}

}

MapperScannerRegistrar implements ImportBeanDefinitionRegistrar

这就是它集成到Spring的关键点,关于ImportBeanDefinitionRegistrar可以参考https://my.oschina.net/u/3049601/blog/3129295

MapperScannerRegistrar 在Spring容器初始化的时候完成从外部导入MapperScannerConfigurer类对应的BeanDefinition

MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor(又是Spring的另外一个扩展点,也是扩展BeanDefinition注册功能,但它要晚于ImportBeanDefinitionRegistrar生效,因为前者的作用触发时机是Spring的ConfigurationClassPostProcessor Implements PriorityOrder。 这个类是扫描路径下所有Mapper的关键类

/**

* BeanDefinitionRegistryPostProcessor递归地从基本包中搜索接口并将它们注册为MapperFactoryBean

* MapperFactoryBean非常重要,它实现了InitializingBean,Spring会让Bean属性设置完之后调用它的抽象方法afterPropertiesSet,完成一些初始化操作

*/

public class MapperScannerConfigurer

implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {

.................省略部分代码..................

@Override

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {

if (this.processPropertyPlaceHolders) {

processPropertyPlaceHolders();

}

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);

//默认是MapperFactoryBean,可以是自定义的

scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);

if (StringUtils.hasText(lazyInitialization)) {

scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));

}

scanner.registerFilters();

//开始扫描

scanner.scan(

StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));

}

.................省略部分代码..................

}

ClassPathMapperScanner 继承自 Spring.ClassPathBeanDefinitionScanner,重写了doScan方法

public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {

.................省略部分代码..................

/**

* 重写了ClassPathBeanDefinitionScanner的doScan方法,但是扫描工作还是由父类的doScan完成

*/

@Override

public Set<BeanDefinitionHolder> doScan(String... basePackages) {

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 {

processBeanDefinitions(beanDefinitions);

}

return beanDefinitions;

}

/**

* 重写了ClassPathBeanDefinitionScanner的processBeanDefinitions方法

* 完成BeanDefinition的属性填充

* 其中的setAutowireMode=AbstractBeanDefinition.AUTOWIRE_BY_TYPE 是Spring根据类型完成自动注入的关键。

* @param beanDefinitions

*/

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {

GenericBeanDefinition definition;

for (BeanDefinitionHolder holder : beanDefinitions) {

definition = (GenericBeanDefinition) holder.getBeanDefinition();

String beanClassName = definition.getBeanClassName();

LOGGER.debug(() -> "Creating MapperFactoryBean with name "" + holder.getBeanName() + "" and "" + beanClassName

+ "" mapperInterface");

// the mapper interface is the original class of the bean

// but, the actual class of the bean is MapperFactoryBean

/**

* 自定义的Mapper接口只是Bean最初的类,当Spring初始化之后Bean的Class实际上是MapperFactoryBean

*/

definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59

//设置BeanClass为MapperFactoryBean

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() + "".");

// 相当的重要,这就是Spring的根据类型进行依赖注入。

// @Autowired使用的时候,其实Spring默认是没有自动注入的,也就是说autowireMode是AUTOWIRE_NO

definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

}

definition.setLazyInit(lazyInitialization);

}

}

.................省略部分代码..................

}

上面的代码完成了对basePackage的类扫描,Mybatis根据扫描生成的BeanDefinition做增强,相当重要的两点:

1.BeanClass=MapperFactoryBean 

也就是说:最开始Spring容器初始化完成之后,所有Mapper并没有真正的实例化(可以通过观察XXXApplicationContext.beanFactory.FactoryBeanObjectCache中是否有对象),而是它们的FactoryBean已经完成了实例化。当需要Mapper时再创建,如果是单例,则将已经实例化的Bean放到FactoryBeanObjectCache中

2.autowireMode=AUTOWIRE_BY_TYPE

按类型自动注入和@Autowired没直接联系,Spring默认是AUTOWIRE_NO,只不过Spring发现你用了@Autowired注解会自动根据类型注入而已。按类型自动注入必须要有setXXX方法。

代表了被扫描的类支持按类型实例化,为什么要设置这个值喃?我们自己写的Mapper紧紧是一个接口,为什么还要注入东西喃。。。根本原因是MapperFactoryBean extends SqlSessionDaoSupport

SqlSessionDaoSupport里面有两个set方法,分别是:

public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory)

public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate)

可以验证一下,将“definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)” 注释掉,报错:

代表着没有注入成功,也验证了AUTOWIRE_BY_TYPE的重要性

 

截止到这里,Mybatis相关类的BeanDefinition(BeanClass=MapperFactoryBean)全部完成了注册,接下来就是实例化

实例化

实例化时有一个非常重要的点:

前面说过完成实例化的Mapper都是MapperFactoryBean,而不是真正的Mapper,为什么喃?

接下来看看MapperFactoryBean的类图

可以看到最终实现了InitializingBean,Spring针对实现了该接口的Bean,在完成属性填充之后会调用实现类的afterPropertiesSet()方法。

来看看afterPropertiesSet()方法干了什么

    public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {

//运行时会调用MapperFactoryBean的checkDaoConfig

this.checkDaoConfig();

try {

this.initDao();

} catch (Exception var2) {

throw new BeanInitializationException("Initialization of DAO failed", var2);

}

}

MapperFactoryBean的checkDaoConfig方法

  protected void checkDaoConfig() {

super.checkDaoConfig();

notNull(this.mapperInterface, "Property "mapperInterface" is required");

Configuration configuration = getSqlSession().getConfiguration();

if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {

try {

//非常重要

configuration.addMapper(this.mapperInterface);

} catch (Exception e) {

logger.error("Error while adding the mapper "" + this.mapperInterface + "" to configuration.", e);

throw new IllegalArgumentException(e);

} finally {

ErrorContext.instance().reset();

}

}

}

configuration.addMapper(this.mapperInterface)完成了MappedStatement添加。

configuration是DefaultSqlSesstionFactory(默认的)中的属性,全局唯一

MappedStatement描述了一个要执行的SQL,参数、返回类型等等

通过这一步,调用真正的query方法所需要的东西都准备好了。

 

总结:Mybatis利用了@Import(class implements ImportBeanDefinitionRegistrar),BeanDefinitionRegistryPostProcessor,InitializingBean三个扩展点来完成整合。

其中Mybatis的如下几个类非常重要:

MapperScan
MapperScannerRegistrar
MapperScannerConfigurer
ClassPathMapperScanner
MapperFactoryBean
DefaultSqlSessionFactory
Configuration
MapperStatement

以上是 MybatisSpring源码分析 的全部内容, 来源链接: utcz.com/z/510979.html

回到顶部