您的当前位置:首页正文

springboot+mybaitsplus多数据源原理(源码)

2024-11-26 来源:个人技术集锦

大概流程

有@DS注解都会跳进切面方法中。然后把@DS中配置的连接名称放入一个map中。这个map的k是线程id,v是连接名称。然后mybatis再getconnection的时候会通过spring的事务管理器SpringManagedTransaction获取。就会跳进那个map中获取刚才切面设置的连接名称然后获得相对应的连接。

源码分析

1.多数据源其实就是多个java.sql.Connection。所以只要在运行时候识别到正确的那个connection拿个就行了。

2.mybatisplus的连接都是在执行器org.apache.ibatis.executor.Executor中实现类中事务管理器的org.apache.ibatis.transaction.Transaction中,接口中有获取连接或者提交回滚的方法。

/*
 * Copyright 2010-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.mybatis.spring.transaction;

import static org.springframework.util.Assert.notNull;

import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;

import org.apache.ibatis.transaction.Transaction;
import org.mybatis.logging.Logger;
import org.mybatis.logging.LoggerFactory;
import org.springframework.jdbc.datasource.ConnectionHolder;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.transaction.support.TransactionSynchronizationManager;

/**
 * {@code SpringManagedTransaction} handles the lifecycle of a JDBC connection. It retrieves a connection from Spring's
 * transaction manager and returns it back to it when it is no longer needed.
 * <p>
 * If Spring's transaction handling is active it will no-op all commit/rollback/close calls assuming that the Spring
 * transaction manager will do the job.
 * <p>
 * If it is not it will behave like {@code JdbcTransaction}.
 *
 * @author Hunter Presnall
 * @author Eduardo Macarron
 */
public class SpringManagedTransaction implements Transaction {

  private static final Logger LOGGER = LoggerFactory.getLogger(SpringManagedTransaction.class);

  private final DataSource dataSource;

  private Connection connection;

  private boolean isConnectionTransactional;

  private boolean autoCommit;

  public SpringManagedTransaction(DataSource dataSource) {
    notNull(dataSource, "No DataSource specified");
    this.dataSource = dataSource;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public Connection getConnection() throws SQLException {
    if (this.connection == null) {
      openConnection();
    }
    return this.connection;
  }

  private void openConnection() throws SQLException {
    this.connection = DataSourceUtils.getConnection(this.dataSource);
    this.autoCommit = this.connection.getAutoCommit();
    this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);

    LOGGER.debug(() -> "JDBC Connection [" + this.connection + "] will"
        + (this.isConnectionTransactional ? " " : " not ") + "be managed by Spring");
  }




}

4.在获取连接时候org.springframework.jdbc.datasource.DataSourceUtils#fetchConnection就会进入com.baomidou.dynamic.datasource.DynamicRoutingDataSource#determineDataSource

5.而com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder
中有一个按线程的map。里面配置者连接的名字,所以@DS通过切面修改本线程的map。然后再拿连接名字时候就会拿到对应的连接。


public final class DynamicDataSourceContextHolder {
    private static final ThreadLocal<Deque<String>> LOOKUP_KEY_HOLDER = new NamedInheritableThreadLocal("dynamic-datasource") {
        protected Object initialValue() {
            return new ArrayDeque();
        }
    };

    private DynamicDataSourceContextHolder() {
    }

    public static String peek() {
        return (String)((Deque)LOOKUP_KEY_HOLDER.get()).peek();
    }

    public static void push(String ds) {
        ((Deque)LOOKUP_KEY_HOLDER.get()).push(StringUtils.isEmpty(ds) ? "" : ds);
    }

    public static void poll() {
        Deque<String> deque = (Deque)LOOKUP_KEY_HOLDER.get();
        deque.poll();
        if (deque.isEmpty()) {
            LOOKUP_KEY_HOLDER.remove();
        }

    }

    public static void clear() {
        LOOKUP_KEY_HOLDER.remove();
    }
}

6.来看看@DS中切面执行的情况,再有@DS注解会跳进com.baomidou.dynamic.datasource.aop.DynamicDataSourceAnnotationInterceptor#invoke中然后会往DynamicDataSourceContextHolder的线程map中ThreadLocal<Deque<String>> LOOKUP_KEY_HOLDER放入@DS的连接名称
这样当SpringManagedTransaction拿连接时候就会拿到@DS中的了。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.baomidou.dynamic.datasource.aop;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.dynamic.datasource.processor.DsProcessor;
import com.baomidou.dynamic.datasource.support.DataSourceClassResolver;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import java.lang.reflect.Method;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.core.annotation.AnnotationUtils;

public class DynamicDataSourceAnnotationInterceptor implements MethodInterceptor {
    private static final String DYNAMIC_PREFIX = "#";
    private static final DataSourceClassResolver RESOLVER = new DataSourceClassResolver();
    private DsProcessor dsProcessor;

    public DynamicDataSourceAnnotationInterceptor() {
    }

    public Object invoke(MethodInvocation invocation) throws Throwable {
        Object var2;
        try {
            DynamicDataSourceContextHolder.push(this.determineDatasource(invocation));
            var2 = invocation.proceed();
        } finally {
            DynamicDataSourceContextHolder.poll();
        }

        return var2;
    }

    private String determineDatasource(MethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();
        DS ds = method.isAnnotationPresent(DS.class) ? (DS)method.getAnnotation(DS.class) : (DS)AnnotationUtils.findAnnotation(RESOLVER.targetClass(invocation), DS.class);
        String key = ds.value();
        return !key.isEmpty() && key.startsWith("#") ? this.dsProcessor.determineDatasource(invocation, key) : key;
    }

    public void setDsProcessor(DsProcessor dsProcessor) {
        this.dsProcessor = dsProcessor;
    }
}

显示全文