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框架单独使用
    • 2、spring整合mybatis使用
  • 二、spring整合mybatis原理
    • 1、@MapperScan注解原理
      • 1.1、MapperScannerRegistrar类的核心方法(实现ImportBeanDefinitionRegistrar接口方法)
      • 1.2、MapperScannerConfigurer类的作用
    • 2、SqlSessionFactoryBean类
      • 2.1、FactoryBean接口
    • 3、MapperFactoryBean类
      • 3.1、FactoryBean接口
      • 3.2、DaoSupport抽象类
      • 3.3、SqlSessionDaoSupport抽象类
      • 3.4、MapperFactoryBean的checkDaoConfig方法
    • 4、总结Mapper对象的创建过程

一、Mybatis的使用介绍

1、mybatis框架单独使用

  • 通过sqlSession获取对应的代理对象
  • 代理对象执行sql完成数据库操作
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配置创建一个SqlSessionFactorySqlSessionFactory sqlSessionFactory = builder.build(resourceAsStream);// 3.通过SqlSessionFactory,创建一个SqlSessionSqlSession sqlSession = sqlSessionFactory.openSession();// 4.获取一个代理对象UserMapper mapper = sqlSession.getMapper(UserMapper.class);// 5.调用代理对象的方法System.out.println("代理对象查询结果:" + mapper.selectOne(1));}
}

2、spring整合mybatis使用

  • SqlSession对象以及代理对象都交给spring容器来创建
  • 直接从容器中获取Mapper接口的实现类(代理对象)即可
@Configuration //声明该类是核心配置类
@ComponentScan("com.xc") //开启spring注解扫描
@MapperScan("com.xc.mapper") //MyBatis扫描Mapper接口
public class MybatisConfig {@Beanpublic 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;}@Beanpublic 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
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {// basePackages属性的别名,等价于basePackagesString[] 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来管理// 实现这个功能的就是这个MapperFactoryBeanClass<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;// 是否对mapper进行懒加载,默认为falseString lazyInitialization() default "";}

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

  • 核心内容就是注册MapperScannerConfigurer.class的bean
  • @MapperScan注解中的属性都添加到这个bean中
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {// 获取到@MapperScan这个注解中的属性AnnotchaationAttributes mapperScanAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));if (mapperScanAttrs != null) {// 紧接着开始向Spring容器中注册bdregisterBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,generateBaseBeanName(importingClassMetadata, 0));}
}void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,BeanDefinitionRegistry registry, String beanName) {// 打算注册到容器中的bd的beanClass属性为MapperScannerConfigurer.classBeanDefinitionBuilder 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属性,没有抛异常
@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
    • 所以这里需要重新设置扫描规则
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));
}
  • 注册指定包扫描规则(指定注解指定接口全部
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) {@Overrideprotected 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");});
}
  • 扫描指定package的类路径下的接口
  • ClassPathMapperScanner重新ClassPathBeanDefinitionScanner的doScan方法
    • 调用父类doScan方法、对扫描出来的BeanDefinition做处理
  • super.doScan以前讲过Spring源码解析(二):bean容器的创建、默认后置处理器、扫描包路径bean
    • 核心内容就是扫描指定包将符合要求Class注册bean定义
# 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);}//注册了多少mapperreturn (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接口为构造函数参数
      在这里插入图片描述
// 主要做了这么几件事
// 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属性设置为mapperFactoryBeanClassdefinition.setBeanClass(this.mapperFactoryBeanClass);definition.getPropertyValues().add("addToConfig", this.addToConfig);// 开始检查是否显示的指定了sqlSessionFactory或者sqlSessionTemplateboolean 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定义了三个方法,其源码如下:

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

SqlSessionFactoryBean类中对于FactoryBean的实现:

在这里插入图片描述

进入afterPropertiesSet方法

@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
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方法
public abstract class DaoSupport implements InitializingBean {@Overridepublic 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抽象类

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,确保其不为空@Overrideprotected void checkDaoConfig() {notNull(this.sqlSessionTemplate, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");}
}
  • 创建sqlSessionTemplate对象

在这里插入图片描述

  • Session的代理对象sqlSessionProxy的 invoke 方法
private class SqlSessionInterceptor implements InvocationHandler {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {    // 第一步,获取一个sqlSessionSqlSession 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 {// 关闭sqlSessionif (sqlSession != null) {closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);}}}
}
  • SqlSession的获取,熟悉的味道:sessionFactory.openSession()
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创建了一个sqlSessionLOGGER.debug(() -> "Creating a new SqlSession");session = sessionFactory.openSession(executorType);// 如果开启了事务的话并且事务是由Spring管理的话,会将sqlSession绑定到当前线程上registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);return session;
}

3.4、MapperFactoryBean的checkDaoConfig方法

  • 创建 Mapper接口代理对象初始化时候,会调用此方法
  • 这里就会进入 mybatis 的源码了Mybatis源码解析(八):Mapper代理原理
// 之前分析过了,这个方法会在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生成的代理对象

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/94933.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式学习笔记(1)ARM的编程模式和7种工作模式

ARM提供的指令集 ARM态-ARM指令集&#xff08;32-bit&#xff09; Thumb态-Thumb指令集&#xff08;16-bit&#xff09; Thumb2态-Thumb2指令集&#xff08;16 & 32 bit&#xff09; Thumb指令集是对ARM指令集的一个子集重新编码得到的&#xff0c;指令长度为16位。通常在…

MVC模式分层练习

新建库 新建表 插入点数据 先不用MVC模式写功能,来看下缺点是什么 新建一个空项目 选项项目使用的JDK 自己的IDEA总是要重启下 新建模块 因maven还没教 添加框架支持 添加后项目多了这些 添加些必要依赖 这里注意下,如果导入jar包不对可以重新导入下或者是jar包本身出了问…

11. 微积分 - 偏导数方向导数

文章目录 偏导数方向导数方向余弦投影继续讲方向导数Hi, 大家好。我是茶桁。 我们上节课学习了链式法则,本节课,我们要学习「偏导数」和「方向导数」。 偏导数 偏导数在导论课里面也提到过。偏导数针对多元函数去讲的。 多元函数是什么,我们拿个例子来看: 多元函数: y…

javaScipt

javaScipt 一、JavaScript简介二、javaScript基础1、输入输出语法2、变量3、常量4、数据类型4.1、数字型 number4.2、字符串类型 string4.3、布尔类型 boolean4.4、未定义类型 undefined4.5、null 空类型4.6、typeof 检测变量数据类型 5、数据类型转换5.1、隐式转换5.2、显示转…

ModaHub魔搭社区——未来向量数据库会不像传统数据库那样,在国内涌现 200 多家出来?

I. 引言:数据库市场的持续扩张与向量数据库的崛起 随着技术的迭代速度越来越快,技术门槛也在逐渐降低,数据库市场的持续扩张是不可避免的。当前存在着大量的需求,这将吸引越来越多的数据库甚至向量数据库加入竞争。然而,从业界角度看,这种市场扩张是有利的。它可以促使更…

K8s简介之什么是K8s

目录 1.概述 2.什么是容器引擎&#xff1f; 3.什么是容器 4.什么是容器编排&#xff1f; 5.容器编排工具 6.到底什么是K8s? 7.为什么市场推荐K8s 8.K8s架构 9.K8s组件 Pods API 服务器 调度器 控制器管理器 Etcd 节点 Kubelet Kube代理 Kubectl 1.概述 Kub…

【无公网IP内网穿透】异地远程访问本地SQL Server数据库

目录 1.前言 2.本地安装和设置SQL Server 2.1 SQL Server下载 2.2 SQL Server本地连接测试 2.3 Cpolar内网穿透的下载和安装 2.3 Cpolar内网穿透的注册 3.本地网页发布 3.1 Cpolar云端设置 3.2 Cpolar本地设置 4.公网访问测试 5.结语 1.前言 数据库的重要性相信大家…

禅道项目管理系统 - 操作使用 (2023版)

1. 部门-用户-权限 新增部门 新增用户 设置权限 2. 项目集创建 项目集 - 添加项目集 3. 产品线创建 产品 - 产品线 4. 产品创建 产品 - 产品列表 - 添加产品 5. 产品计划创建 产品 - xx产品 - 计划 - 创建计划 我这里创建3个计划 (一期, 二期, 三期) 6. 研发需求 - 创建模块…

极限五分钟,在宝塔中用 Docker 部署升讯威在线客服系统

最近客服系统成功经受住了客户现场组织的压力测试&#xff0c;获得了客户的认可。 客户组织多名客服上线后&#xff0c;所有员工同一时间打开访客页面疯狂不停的给在线客服发消息&#xff0c;系统稳定无异常无掉线&#xff0c;客服回复消息正常。消息实时到达无任何延迟。 本文…

jmeter 常数吞吐量定时器

模拟固定吞吐量的定时器。它可以控制测试计划中各个请求之间的时间间隔&#xff0c;以达到预期的吞吐量。 参数包括&#xff1a; Target Throughput&#xff1a;目标吞吐量&#xff08;每分钟请求数&#xff09;Calculate Throughput based on&#xff1a;吞吐量计算基准&…

java八股文面试[多线程]——虚假唤醒

阻塞队列中&#xff0c;如果需要线程挂起操作&#xff0c;判断有无数据的位置采用的是while循环 &#xff0c;为什么不能换成if 肯定是不能换成if逻辑判断 线程A&#xff0c;线程B&#xff0c;线程E&#xff0c;线程C。 其中ABE生产者&#xff0c;C属于消费者 put阻塞代码&a…

FPGA时序分析与约束(4)——时序分析,时序约束,时序收敛

一、前言 在之前的文章中&#xff0c;我们介绍了组合电路的时序和时序电路的时序问题&#xff0c;之后又把理想化的时钟变成了实际的时钟考虑了进来&#xff0c;在阅读本文之前&#xff0c;强烈推荐优先阅读本系列之前的文章&#xff0c;毕竟这是我们继续学习的基础&#xff0c…