MyBatis插件原理及Spring集成

ps:迷迷糊糊的,回头再看把

DataSourceFactory
复制代码
public interface DataSourceFactory {
​
  /**
   * 设置属性
   * @param props
   */
  void setProperties(Properties props);
​
  /**
   * 获取数据源
   * @return
   */
  DataSource getDataSource();
​
}
​

ya

unpooled

复制代码
UnpooledDataSourceFactory
复制代码
UnpooledDataSource

负责new数据源单例的

pooled

复制代码
 */
public class PooledDataSourceFactory extends UnpooledDataSourceFactory {
​
  // 构造有池化数据源工厂,并初始化默认 PooledDataSource;无参数
  public PooledDataSourceFactory() {
    this.dataSource = new PooledDataSource();
  }
​
}
​
复制代码
<dataSource type="POOLED">
    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>    
    <property name="url" value="jdbc:mysql://127.0.0.1:3306/blog2026_2_2?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true"/>    
    <property name="username" value="root"/>    
    <property name="password" value="root"/>    
</dataSource>
复制代码
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
  if (context != null) {
    String type = context.getStringAttribute("type");
    Properties props = context.getChildrenAsProperties();
    DataSourceFactory factory = (DataSourceFactory) resolveClass(type).getDeclaredConstructor().newInstance();
    factory.setProperties(props);
    return factory;
  }
  throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}
复制代码
PooledDataSource
复制代码
public PooledDataSource() {
  dataSource = new UnpooledDataSource();
}
获取连接池
复制代码
popConnection
复制代码
public class PoolState {

  // 关联的数据源(用于读取配置与统计展示)
  protected PooledDataSource dataSource;

  // 空闲连接列表
  protected final List<PooledConnection> idleConnections = new ArrayList<>();
  // 活跃连接列表
  protected final List<PooledConnection> activeConnections = new ArrayList<>();

pushConnection

Spring集成mybatis

复制代码
    @Test
    public void test3() throws  Exception{
        // 1.获取配置文件
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        // 2.加载解析配置文件并获取SqlSessionFactory对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = sqlSessionFactoryBuilder.build(in);
        // 3.根据SqlSessionFactory对象获取SqlSession对象
        SqlSession sqlSession = factory.openSession(ExecutorType.BATCH, false);
        // 4.通过SqlSession中提供的 API方法来操作数据库

        List<User> list ;
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        list = mapper.selectUserList();
        for (User user : list) {
            System.out.println(user.getUserName());
        }
        // 5.关闭会话
        sqlSession.close();
    }

谁帮我做了这些

SqlSessionFactory

复制代码
  // 创建SqlSessionFactory
  @Override
  public void afterPropertiesSet() throws Exception {
    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");

    this.sqlSessionFactory = buildSqlSessionFactory();
  }

老朋友都在

复制代码
// 创建 SqlSessionFactory(MyBatis 集成 Spring 的核心入口)
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {

  final Configuration targetConfiguration; // MyBatis 全局配置对象(核心)

  XMLConfigBuilder xmlConfigBuilder = null;

  // ============================
  // 1)加载 MyBatis Configuration
  // ============================

  if (this.configuration != null) {
    // Spring 已经注入了 Configuration(JavaConfig方式)
    targetConfiguration = this.configuration;

    // 合并 Spring 传入的 properties
    if (targetConfiguration.getVariables() == null) {
      targetConfiguration.setVariables(this.configurationProperties);
    } else if (this.configurationProperties != null) {
      targetConfiguration.getVariables().putAll(this.configurationProperties);
    }

  } else if (this.configLocation != null) {
    // 从 mybatis-config.xml 加载配置(XML方式)
    xmlConfigBuilder = new XMLConfigBuilder(
        this.configLocation.getInputStream(), null, this.configurationProperties);
    targetConfiguration = xmlConfigBuilder.getConfiguration();

  } else {
    // 未指定配置文件 → 使用默认 Configuration
    targetConfiguration = new Configuration();
    Optional.ofNullable(this.configurationProperties)
        .ifPresent(targetConfiguration::setVariables);
  }

  // ============================
  // 2)注册 MyBatis 扩展组件
  // ============================

  // ObjectFactory:对象实例化策略(反射创建结果对象)
  Optional.ofNullable(this.objectFactory)
      .ifPresent(targetConfiguration::setObjectFactory);

  // ObjectWrapperFactory:包装对象(MetaObject)
  Optional.ofNullable(this.objectWrapperFactory)
      .ifPresent(targetConfiguration::setObjectWrapperFactory);

  // VFS:资源扫描器(用于扫描 classpath)
  Optional.ofNullable(this.vfs)
      .ifPresent(targetConfiguration::setVfsImpl);

  // ============================
  // 3)扫描别名 typeAliasesPackage
  // ============================

  if (hasLength(this.typeAliasesPackage)) {
    // Spring 扫描包下的实体类并注册别名(User -> user)
    scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType)
        .stream()
        .filter(clazz -> !clazz.isAnonymousClass())
        .filter(clazz -> !clazz.isInterface())
        .forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
  }

  // 手动注册别名
  if (!isEmpty(this.typeAliases)) {
    Stream.of(this.typeAliases).forEach(typeAlias ->
        targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias)
    );
  }

  // ============================
  // 4)注册插件 Interceptor(MyBatis 核心扩展点)
  // ============================

  if (!isEmpty(this.plugins)) {
    // Spring 注入的分页插件、审计插件等在这里加入 MyBatis
    Stream.of(this.plugins).forEach(plugin ->
        targetConfiguration.addInterceptor(plugin)
    );
  }

  // ============================
  // 5)注册 TypeHandler(Java类型 ↔ JDBC类型转换)
  // ============================

  if (hasLength(this.typeHandlersPackage)) {
    // 扫描并注册自定义 TypeHandler
    scanClasses(this.typeHandlersPackage, TypeHandler.class)
        .stream()
        .filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))
        .forEach(targetConfiguration.getTypeHandlerRegistry()::register);
  }

  if (!isEmpty(this.typeHandlers)) {
    // 手动注册 TypeHandler
    Stream.of(this.typeHandlers).forEach(typeHandler ->
        targetConfiguration.getTypeHandlerRegistry().register(typeHandler)
    );
  }

  // 设置默认 Enum 处理器
  targetConfiguration.setDefaultEnumTypeHandler(defaultEnumTypeHandler);

  // ============================
  // 6)注册 LanguageDriver(动态SQL解析器)
  // ============================

  if (!isEmpty(this.scriptingLanguageDrivers)) {
    Stream.of(this.scriptingLanguageDrivers).forEach(languageDriver ->
        targetConfiguration.getLanguageRegistry().register(languageDriver)
    );
  }

  Optional.ofNullable(this.defaultScriptingLanguageDriver)
      .ifPresent(targetConfiguration::setDefaultScriptingLanguage);

  // ============================
  // 7)设置 DatabaseId(适配多数据库方言)
  // ============================

  if (this.databaseIdProvider != null) {
    targetConfiguration.setDatabaseId(
        this.databaseIdProvider.getDatabaseId(this.dataSource)
    );
  }

  // ============================
  // 8)解析 mybatis-config.xml(如果存在)
  // ============================

  if (xmlConfigBuilder != null) {
    xmlConfigBuilder.parse(); // 解析 settings、mappers、plugins 等
  }

  // ==========================================================
  // 9)最关键:MyBatis 集成 Spring 的核心工作
  // 设置 Environment(事务工厂 + DataSource)
  // ==========================================================

  targetConfiguration.setEnvironment(
      new Environment(
          this.environment,

          // SpringManagedTransactionFactory:让 MyBatis 使用 Spring 事务
          this.transactionFactory == null
              ? new SpringManagedTransactionFactory()
              : this.transactionFactory,

          // Spring 注入的数据源 DataSource
          this.dataSource
      )
  );

  // ============================
  // 10)解析 Mapper XML 文件
  // ============================

  if (this.mapperLocations != null) {
    for (Resource mapperLocation : this.mapperLocations) {

      // XMLMapperBuilder:解析每一个 mapper.xml
      XMLMapperBuilder xmlMapperBuilder =
          new XMLMapperBuilder(
              mapperLocation.getInputStream(),
              targetConfiguration,
              mapperLocation.toString(),
              targetConfiguration.getSqlFragments()
          );

      // 注册 SQL 语句到 Configuration:MappedStatement
      xmlMapperBuilder.parse();
    }
  }

  // ============================
  // 11)最终构建 SqlSessionFactory
  // ============================

  return this.sqlSessionFactoryBuilder.build(targetConfiguration);
}

1..Spring 控制 JDBC Connection 的事务

new SpringManagedTransactionFactory()

2.DataSource 由 Spring 注入

this.dataSource

MyBatis 不再自己创建连接池 而是复用 Spring 管理的:

  • HikariCP

  • Druid

  • C3P0

3.Mapper XML 与 Mapper 接口注册进 Spring

复制代码
XMLMapperBuilder.parse();

mapper.xml → MappedStatement

Mapper 接口 → Spring Bean

最终实现:

复制代码
userMapper.selectById()

背后其实是:

复制代码
SqlSessionTemplate.execute(...)

SqlSession

存在数据安全问题

复制代码
  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      // 获取事务工厂
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      // 创建事务
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      // 根据事务工厂和默认的执行器类型,创建执行器 >>
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
复制代码
SqlSessionTemplate
复制代码
@Override
public <K, V> Map<K, V> selectMap(String statement, String mapKey) {
  return this.sqlSessionProxy.selectMap(statement, mapKey);
}
复制代码
private final SqlSession sqlSessionProxy;

代理对象

复制代码
  this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class }, new SqlSessionInterceptor());
复制代码
finally {

        // =====================================================
        // 6)finally:确保 SqlSession 正确关闭
        //   - 如果是事务 SqlSession → 不真正关闭(交给事务管理器)
        //   - 如果非事务 SqlSession → close 释放连接
        // =====================================================
        if (sqlSession != null) {
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }

线程安全问题

复制代码
public abstract class SqlSessionDaoSupport extends DaoSupport {

  private SqlSessionTemplate sqlSessionTemplate;

代理mapper

复制代码
public class MapperScannerConfigurer
    implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {

InitializingBean

复制代码
  @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);
    scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
    if (StringUtils.hasText(lazyInitialization)) {
      scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
    }
    if (StringUtils.hasText(defaultScope)) {
      scanner.setDefaultScope(defaultScope);
    }
    scanner.registerFilters();
    //扫描注册
    scanner.scan(
        StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  }
相关推荐
学嵌入式的小杨同学2 小时前
【Linux 封神之路】信号编程全解析:从信号基础到 MP3 播放器实战(含核心 API 与避坑指南)
java·linux·c语言·开发语言·vscode·vim·ux
lang201509282 小时前
JSR-340 :高性能Web开发新标准
java·前端·servlet
Re.不晚2 小时前
Java入门17——异常
java·开发语言
缘空如是2 小时前
基础工具包之JSON 工厂类
java·json·json切换
追逐梦想的张小年3 小时前
JUC编程04
java·idea
好家伙VCC3 小时前
### WebRTC技术:实时通信的革新与实现####webRTC(Web Real-TimeComm
java·前端·python·webrtc
南极星10053 小时前
蓝桥杯JAVA--启蒙之路(十)class版本 模块
java·开发语言
消失的旧时光-19433 小时前
第十三课:权限系统如何设计?——RBAC 与 Spring Security 架构
java·架构·spring security·rbac
不能隔夜的咖喱4 小时前
牛客网刷题(2)
java·开发语言·算法