Spring源码解析(十):spring整合mybatis源码

Spring源码系列文章

Spring源码解析(一):环境搭建

Spring源码解析(二):bean容器的创建、默认后置处理器、扫描包路径bean

Spring源码解析(三):bean容器的刷新

Spring源码解析(四):单例bean的创建流程

Spring源码解析(五):循环依赖

Spring源码解析(六):bean工厂后置处理器ConfigurationClassPostProcessor

Spring源码解析(七):bean后置处理器AutowiredAnnotationBeanPostProcessor

Spring源码解析(八):bean后置处理器CommonAnnotationBeanPostProcessor

Spring源码解析(九):AOP源码之@Aspect所有相关注解解析

Spring源码解析(十):spring整合mybatis源码


目录

一、Mybatis的使用介绍

1、mybatis框架单独使用

  • 通过sqlSession获取对应的代理对象
  • 代理对象执行sql完成数据库操作
java 复制代码
public class MybatisMain {
    public static void main(String[] args) throws Exception {
        String resource = "mybatis-config.xml";
        InputStream resourceAsStream = Resources.getResourceAsStream(resource);
        // 1.解析XML配置
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        // 2.基于解析好的XML配置创建一个SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = builder.build(resourceAsStream);
        // 3.通过SqlSessionFactory,创建一个SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 4.获取一个代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        // 5.调用代理对象的方法
        System.out.println("代理对象查询结果:" + mapper.selectOne(1));
    }
}

2、spring整合mybatis使用

  • SqlSession对象以及代理对象都交给spring容器来创建
  • 直接从容器中获取Mapper接口的实现类(代理对象)即可
java 复制代码
@Configuration //声明该类是核心配置类
@ComponentScan("com.xc") //开启spring注解扫描
@MapperScan("com.xc.mapper") //MyBatis扫描Mapper接口
public class MybatisConfig {
    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
        driverManagerDataSource.setPassword("root");
        driverManagerDataSource.setUsername("root");
        driverManagerDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        driverManagerDataSource.setUrl("jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false");
        return driverManagerDataSource;
    }
    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean() {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource());
        return sqlSessionFactoryBean;
    }
	// 执行操作
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(MybatisConfig.class);
        UserMapper userMapper = context.getBean(UserMapper.class);
        User user = userMapper.selectOne(100);
        System.out.println(user);
    }
}

二、spring整合mybatis原理

  • 根据上面spring整合mybatis的配置类的信息,要想清楚Spring是如何整合Mybatis的,我们只要弄明白两点
    1. @MapperScan注解的作用
    2. SqlSessionFactoryBean对象的作用

1、@MapperScan注解原理

  • @Import注解,那它的作用很明显,就是注册bean
java 复制代码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
 
  // basePackages属性的别名,等价于basePackages
  String[] value() default {};
  
  // 扫描的包名
  String[] basePackages() default {};
 
  // 可以提供一个类,以类的包名作为扫描的包  
  Class<?>[] basePackageClasses() default {};
 
  // BeanName的生成器,一般用默认的就好啦
  Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
 
  // 指定要扫描的注解
  Class<? extends Annotation> annotationClass() default Annotation.class;
 
  // 指定标记接口,只有继承了这个接口才会被扫描
  Class<?> markerInterface() default Class.class;
 
  // 指定SqlSessionTemplate的名称,
  // SqlSessionTemplate是Spring对Mybatis中SqlSession的封装
  String sqlSessionTemplateRef() default "";
 
  //  指定SqlSessionFactory的名称
  String sqlSessionFactoryRef() default "";
 
  // 这个属性是什么意思呢?Spring跟Mybatis整合
  // 最重要的事情就是将Mybatis生成的代理对象交由Spring来管理
  // 实现这个功能的就是这个MapperFactoryBean
  Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
 
  // 是否对mapper进行懒加载,默认为false
  String lazyInitialization() default "";
 
}

1.1、MapperScannerRegistrar类的核心方法(实现ImportBeanDefinitionRegistrar接口方法)

  • 核心内容就是注册MapperScannerConfigurer.class的bean
  • @MapperScan注解中的属性都添加到这个bean中
java 复制代码
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    // 获取到@MapperScan这个注解中的属性
    AnnotchaationAttributes mapperScanAttrs = AnnotationAttributes
      .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    if (mapperScanAttrs != null) {
        // 紧接着开始向Spring容器中注册bd
        registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
                                generateBaseBeanName(importingClassMetadata, 0));
    }
}
 
void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
                             BeanDefinitionRegistry registry, String beanName) {
 
    // 打算注册到容器中的bd的beanClass属性为MapperScannerConfigurer.class
    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
    builder.addPropertyValue("processPropertyPlaceHolders", true);
 
   // 省略部分代码
   // ....
   // 这部分代码就是将注解中的属性获取出来
   // 放到MapperScannerConfigurer这个beanDefinition中
    
   // 最后将这个beanDefinition注册到容器中
    registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
 
}

1.2、MapperScannerConfigurer类的作用

类图:

  • 由图可知,MapperScannerConfigurer是一个Bean工厂的后置处理器,bean初始化也会做一些事情

初始化操作:

  • 内容简单,就是校验下@MapperScan是否有basePackage属性,没有抛异常
java 复制代码
@Override
public void afterPropertiesSet() throws Exception {
  notNull(this.basePackage, "Property 'basePackage' is required");
}

public static void notNull(Object object, String message) {
    if (object == null) {
        throw new IllegalArgumentException(message);
    }
}

bean工厂后置处理器操作bean定义:

  • ClassPathMapperScanner继承ClassPathBeanDefinitionScanner
  • ClassPathBeanDefinitionScanner有扫描包注册bean的功能
    • 默认扫描规则@Component的bean,包括@Controller @Service @Repository
    • 所以这里需要重新设置扫描规则
java 复制代码
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    if (this.processPropertyPlaceHolders) {
        // 处理@MaperScan注解属性中的占位符
        processPropertyPlaceHolders();
    }
 	// 在这里创建了一个ClassPathMapperScanner
    // 这个类继承了ClassPathBeanDefinitionScanner,并复写了它的doScan、registerFilters等方法
 	// 其整体行为跟ClassPathBeanDefinitionScanner差不多,
    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));
    }
    // 这里设置了扫描规则
    scanner.registerFilters();
    scanner.scan(
        StringUtils.tokenizeToStringArray(this.basePackage, 
        ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
  • 注册指定包扫描规则(指定注解指定接口全部
java 复制代码
public void registerFilters() {
    boolean acceptAllInterfaces = true;
 
    // 第一步,判断是否要扫描指定的注解
    // 也就是判断在@MapperScan注解中是否指定了要扫描的注解
    if (this.annotationClass != null) {
        addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
        acceptAllInterfaces = false;
    }
 
    // 第二步,判断是否要扫描指定的接口
    // 同样也是根据@MapperScan注解中的属性做判断
    if (this.markerInterface != null) {
        addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
            @Override
            protected boolean matchClassName(String className) {
                return false;
            }
        });
        acceptAllInterfaces = false;
    }
 
    // 如果既没有指定注解也没有指定标记接口
    // 那么所有.class文件都会被扫描
    if (acceptAllInterfaces) {
        addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
    }
 
    // 排除package-info文件
    addExcludeFilter((metadataReader, metadataReaderFactory) -> {
        String className = metadataReader.getClassMetadata().getClassName();
        return className.endsWith("package-info");
    });
}
java 复制代码
# ClassPathBeanDefinitionScanner类方法
public int ClassPathBeanDefinitionScanner.scan(String... basePackages) {
    //获取容器中已经注册BeanDefinition数量
	int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
    //扫描
	doScan(basePackages);

	// Register annotation config processors, if necessary.
	//是否需要注册注解后置处理,比如注册ConfigurationClassPostProcessor,AutowiredAnnotationBeanPostProcessor等
	//但其实这些处理器已经注册了,这里也不需要再注册了
	if (this.includeAnnotationConfig) {
		AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
	}
       //注册了多少mapper
	return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
	
# ClassPathMapperScanner类方法
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 {
      //对扫描出来的BeanDefinition做属性处理,比如添加SqlSessionFactory等
      processBeanDefinitions(beanDefinitions);
    }

    return beanDefinitions;
}
  • 扫描出的BeanDefinition修改
  • 核心内容:将真实的BeanClass属性设置为MapperFactoryBean.class
    • BeanDefinition当初创建赋值:beanClass = "com.xc.UserMapper"
    • BeanDefinition的beanName依然是"userMapper"
  • BeanDefinition的构造函数添加mapperInterface参数
    • 以后创建MapperFactoryBean时,Mapper接口为构造函数参数
java 复制代码
// 主要做了这么几件事
// 1.将扫描出来的BeanDefinition的beanClass属性设置为MapperFactoryBeanClass.class
// 2.在BeanDefinition的ConstructorArgumentValues添加一个参数
// 限定实例化时使用MapperFactoryBeanClass的带参构造函数
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
        definition = (GenericBeanDefinition) holder.getBeanDefinition();
        String beanClassName = definition.getBeanClassName();
        
        // 往构造函数的参数集合中添加了一个值,那么在实例化时就会使用带参的构造函数
        // 等价于在XML中配置了
        // <constructor-arg name="mapperInterface" value="mapperFactoryBeanClass"/>
        definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); 
        
        // 将真实的BeanClass属性设置为mapperFactoryBeanClass
        definition.setBeanClass(this.mapperFactoryBeanClass);
 
        definition.getPropertyValues().add("addToConfig", this.addToConfig);
  
        // 开始检查是否显示的指定了sqlSessionFactory或者sqlSessionTemplate
        boolean explicitFactoryUsed = false;
        
        // 首先检查是否在@MapperScan注解上配置了sqlSessionFactoryRef属性
        if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
            
            // 如果配置了的话,那么在这个bd的属性集合中添加一个RuntimeBeanReference
            // 等价于在xml中配置了
            // <property name="sqlSessionFactory" ref="sqlSessionFactoryBeanName"/>
            definition.getPropertyValues().add("sqlSessionFactory",
                                               new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
            explicitFactoryUsed = true;
            // 如果@MapperScan上没有进行配置
            // 那么检查是否为这个bean配置了sqlSessionFactory属性
            // 正常来说我们都不会进行配置,会进入自动装配的逻辑
        } else if (this.sqlSessionFactory != null) {
            definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
            explicitFactoryUsed = true;
        }
 
        // 省略sqlSessionTemplate部分代码
        // 逻辑跟sqlSessionFactory属性的处理逻辑一致
        // 需要注意的是,如果同时显示指定了sqlSessionFactory跟sqlSessionTemplate
        // 那么sqlSessionFactory的配置将失效
        // .....
 
        if (!explicitFactoryUsed) {
           // 如果没有显示的配置,那么设置为自动注入
            definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        }
        // 默认不是懒加载
        definition.setLazyInit(lazyInitialization);
    }
}

2、SqlSessionFactoryBean类

类图:

  • FactoryBean接口:意味着由 Spring 最终创建的 bean 并不是 SqlSessionFactoryBean 本身,而是工厂类(SqlSessionFactoryBean)的 getObject() 方法的返回结果
  • InitializingBean:bean初始化时候做一些操作

2.1、FactoryBean接口

FactoryBean定义了三个方法,其源码如下:

java 复制代码
public interface FactoryBean<T> {
	// 当IoC容器通过getBean方法来创建FactoryBean的实例时实际获取的不是FactoryBean 本身
	// 而是具体创建的T泛型实例。
    @Nullable
    T getObject() throws Exception;
	// 返回FactoryBean创建的bean类型。
    @Nullable
    Class<?> getObjectType();
	// 返回由FactoryBean创建的bean实例的作用域是singleton还是prototype。
    default boolean isSingleton() {
        return true;
    }
}

SqlSessionFactoryBean类中对于FactoryBean的实现:

进入afterPropertiesSet方法

java 复制代码
@Override
public void afterPropertiesSet() throws Exception {
  //dataSource是必须要配置的
  notNull(dataSource, "Property 'dataSource' is required");
  notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
  state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
            "Property 'configuration' and 'configLocation' can not specified with together");

  //主要逻辑都在buildSqlSessionFactory方法,创建sqlSessionFactory,getObject就是返回的sqlSessionFactory 
  this.sqlSessionFactory = buildSqlSessionFactory();
}
  • 创建SqlSessionFactory
java 复制代码
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
  // 定义了一个Configuration,叫做targetConfiguration。
  final Configuration targetConfiguration;

  XMLConfigBuilder xmlConfigBuilder = null;
  // 判断 Configuration 对象是否已经存在,也就是是否已经解析过。如果已经有对象,就覆盖一下属性
  if (this.configuration != null) {
    targetConfiguration = this.configuration;
    if (targetConfiguration.getVariables() == null) {
      targetConfiguration.setVariables(this.configurationProperties);
    } else if (this.configurationProperties != null) {
      targetConfiguration.getVariables().putAll(this.configurationProperties);
    }
    // 如果 Configuration 不存在,但是配置了 configLocation 属性,
    // 就根据mybatis-config.xml的文件路径,构建一个xmlConfigBuilder对象。
  } else if (this.configLocation != null) {
    xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
    targetConfiguration = xmlConfigBuilder.getConfiguration();
    // 否则,Configuration 对象不存在,configLocation 路径也没有,
    // 只能使用默认属性去构建去给configurationProperties赋值。
  } else {
    LOGGER.debug(() -> "Property 'configuration' or 'configLocation' not specified,using default MyBatis Configuration");
    targetConfiguration = new Configuration();
    Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
  }
 

  // configuration的属性赋值
  ...

  // 如果xmlConfigBuilder 不为空,也就是上面的第二种情况,
  if (xmlConfigBuilder != null) {
    try {
      // 调用了xmlConfigBuilder.parse()去解析配置文件,最终会返回解析好的Configuration对象
      xmlConfigBuilder.parse();
      LOGGER.debug(() -> "Parsed configuration file: '" + this.configLocation + "'");
    } catch (Exception ex) {
      throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
    } finally {
      ErrorContext.instance().reset();
    }
  }

  // 如果没有明确指定事务工厂 ,默认使用pringManagedTransactionFactory。
  // 它创建的 SpringManagedTransaction 也有getConnection()和close()方法
  // <property name="transactionFactory" value="" />
  targetConfiguration.setEnvironment(new Environment(this.environment,
      this.transactionFactory == null 
      ? new SpringManagedTransactionFactory() 
      : this.transactionFactory,this.dataSource));

  // 指定mybatis-config.xml文件地址,注解开发,不会进去
  if (!isEmpty(this.mapperLocations)) {
    for (Resource mapperLocation : this.mapperLocations) {
      if (mapperLocation == null) {
        continue;
      }
      try {
        XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
         targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
        // 调用xmlMapperBuilder.parse(),
        // 它的作用是把接口和对应的MapperProxyFactory 注册到MapperRegistry 中。
        xmlMapperBuilder.parse();
      } catch (Exception e) {
        throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + e);
      } finally {
        ErrorContext.instance().reset();
      }
      LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
    }
  } else {
    LOGGER.debug(() -> "Property 'mapperLocations' was not specified or no matching resources found");
  }

  // 最后调用 sqlSessionFactoryBuilder.build() 返回一个 DefaultSqlSessionFactory。
  return this.sqlSessionFactoryBuilder.build(targetConfiguration);
}

// SqlSessionFactoryBuilder类方法
public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
}

总结:

SqlSessionFactoryBean就是创建SqlSessionFactory,为以后创建SqlSession做准备

3、MapperFactoryBean类

MapperFactoryBean即创建 mapper接口的 bean 定义被替换的类型

类图:

3.1、FactoryBean接口

  • 创建 Mapper的 bean 定义时候将 Class 类型修改为MapperFactoryBean
  • 实际它是FactoryBean对象,真正返回的对象为 getObject()的结果

3.2、DaoSupport抽象类

  • 初始化时候执行的操作,调用checkDaoConfig方法
java 复制代码
public abstract class DaoSupport implements InitializingBean {
	@Override
	public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
		// 子类可以实现这个方法去检查相关的配置信息
		checkDaoConfig();

		// 子类实现,目前没有实现的子类
		try {
			initDao();
		}
		catch (Exception ex) {
			throw new BeanInitializationException("Initialization of DAO failed", ex);
		}
	}
	protected abstract void checkDaoConfig() throws IllegalArgumentException;

	protected void initDao() throws Exception {
	}
}

3.3、SqlSessionDaoSupport抽象类

java 复制代码
public abstract class SqlSessionDaoSupport extends DaoSupport {
 
  private SqlSessionTemplate sqlSessionTemplate;
 
  //  这个是核心方法
  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
    if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) {
      this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory);
    }
  }
 
  // 省略一些getter/setter方法
 
  // 在初始化时要检查sqlSessionTemplate,确保其不为空
  @Override
  protected void checkDaoConfig() {
    notNull(this.sqlSessionTemplate, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");
  }
}
  • 创建sqlSessionTemplate对象
  • Session的代理对象sqlSessionProxy的 invoke 方法
java 复制代码
private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {    
        // 第一步,获取一个sqlSession
        SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
                                              SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
        try {
            // 第二步,调用sqlSession对应的方法
            Object result = method.invoke(sqlSession, args);
            
            // 检查是否开启了事务,如果没有开启事务那么强制提交
            if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
           
                sqlSession.commit(true);
            }
            return result;
        } catch (Throwable t) {
            // 处理异常
            Throwable unwrapped = unwrapThrowable(t);
            if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
               
                closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
                sqlSession = null;
                Throwable translated = SqlSessionTemplate.this.exceptionTranslator
                    .translateExceptionIfPossible((PersistenceException) unwrapped);
                if (translated != null) {
                    unwrapped = translated;
                }
            }
            throw unwrapped;
        } finally {
            // 关闭sqlSession
            if (sqlSession != null) {
                closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
            }
        }
    }
}
  • SqlSession的获取,熟悉的味道:sessionFactory.openSession()
java 复制代码
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,
    PersistenceExceptionTranslator exceptionTranslator) {
 
	notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
	notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
	
	SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
	
	SqlSession session = sessionHolder(executorType, holder);
	if (session != null) {
	  return session;
	}
	// 看到了吧,在这里调用了SqlSessionFactory创建了一个sqlSession
	LOGGER.debug(() -> "Creating a new SqlSession");
	session = sessionFactory.openSession(executorType);
	// 如果开启了事务的话并且事务是由Spring管理的话,会将sqlSession绑定到当前线程上
	registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
	
	return session;
}

3.4、MapperFactoryBean的checkDaoConfig方法

java 复制代码
// 之前分析过了,这个方法会在MapperFactoryBean进行初始化的时候调用
protected void checkDaoConfig() {
  super.checkDaoConfig();
  Configuration configuration = getSqlSession().getConfiguration();
   //addToConfig默认为true的,将mapper接口添加到mybatis的配置信息中
  if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
    try {
      configuration.addMapper(this.mapperInterface);
    } catch (Exception e) 
      throw new IllegalArgumentException(e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
}
 
// 直接调用了mybatis中现成的方法获取一个代理对象然后放入到容器中
@Override
public T getObject() throws Exception {
  return getSqlSession().getMapper(this.mapperInterface);
}

4、总结Mapper对象的创建过程

  1. 根据@MapperScan注解扫描指定路径下接口
    • 创建BeanDefinition,beanName 为"userMapper"
    • beanClass 修改为MapperFactoryBean.class
  2. 实例化 userMapper也就是MapperFactoryBean
    • 属性注入SqlSessionFactory(由SqlSessionFactoryBean创建而来)
  1. 初始化 userMapper 会调用上面的checkDaoConfig方法
    • mybatis 源码内容,将接口创建代理对象
    • 代理对象统一放入SqlSession的Configuration对象中
  2. 对于context.getBean(UserMapper.class);
    • 由于userMapper的代理对象是MapperFactoryBeanFactoryBean
    • 获取对象时候,实际是获取getObject()返回的结果
    • 此时会从SqlSession的Configuration中获取mybatis生成的代理对象
相关推荐
重生之我要进大厂12 分钟前
LeetCode 876
java·开发语言·数据结构·算法·leetcode
_祝你今天愉快15 分钟前
技术成神之路:设计模式(十四)享元模式
java·设计模式
小筱在线1 小时前
SpringCloud微服务实现服务熔断的实践指南
java·spring cloud·微服务
luoluoal1 小时前
java项目之基于Spring Boot智能无人仓库管理源码(springboot+vue)
java·vue.js·spring boot
ChinaRainbowSea1 小时前
十三,Spring Boot 中注入 Servlet,Filter,Listener
java·spring boot·spring·servlet·web
小游鱼KF1 小时前
Spring学习前置知识
java·学习·spring
扎克begod1 小时前
JAVA并发编程系列(9)CyclicBarrier循环屏障原理分析
java·开发语言·python
青灯文案11 小时前
SpringBoot 项目统一 API 响应结果封装示例
java·spring boot·后端
我就是程序猿1 小时前
tomcat的配置
java·tomcat
阳光阿盖尔1 小时前
EasyExcel的基本使用——Java导入Excel数据
java·开发语言·excel