001/*
002 *  Copyright (c) 2022-2024, Mybatis-Flex (fuhai999@gmail.com).
003 *  <p>
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *  <p>
008 *  http://www.apache.org/licenses/LICENSE-2.0
009 *  <p>
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016
017package com.mybatisflex.spring.datasource;
018
019import com.mybatisflex.annotation.UseDataSource;
020import com.mybatisflex.core.datasource.DataSourceKey;
021import com.mybatisflex.core.util.StringUtil;
022import com.mybatisflex.processor.util.StrUtil;
023import org.aopalliance.intercept.MethodInterceptor;
024import org.aopalliance.intercept.MethodInvocation;
025import org.springframework.core.MethodClassKey;
026
027import java.lang.reflect.Method;
028import java.util.Map;
029import java.util.concurrent.ConcurrentHashMap;
030
031/**
032 * 多数据源切换拦截器。
033 *
034 * @author 王帅
035 * @author barql
036 * @author michael
037 * @since 2023-06-25
038 */
039public class DataSourceInterceptor implements MethodInterceptor {
040
041    /**
042     * 缓存方法对应的数据源。
043     */
044    private final Map<Object, String> dsCache = new ConcurrentHashMap<>();
045
046    @Override
047    public Object invoke(MethodInvocation invocation) throws Throwable {
048        String dsKey = getDataSourceKey(invocation.getThis(), invocation.getMethod(), invocation.getArguments());
049        if (StringUtil.noText(dsKey)) {
050            return invocation.proceed();
051        }
052        try {
053            DataSourceKey.use(dsKey);
054            return invocation.proceed();
055        } finally {
056            DataSourceKey.clear();
057        }
058    }
059
060    private String getDataSourceKey(Object target, Method method, Object[] arguments) {
061        Object cacheKey = new MethodClassKey(method, target.getClass());
062        String dsKey = this.dsCache.get(cacheKey);
063        if (dsKey == null) {
064            dsKey = determineDataSourceKey(method, target.getClass());
065            // 对数据源取值进行动态取值处理
066            if (!StrUtil.isBlank(dsKey)) {
067                dsKey = DataSourceKey.processDataSourceKey(dsKey, target, method, arguments);
068            }
069            this.dsCache.put(cacheKey, dsKey);
070        }
071        return dsKey;
072    }
073
074    private String determineDataSourceKey(Method method, Class<?> targetClass) {
075        // 方法上定义有 UseDataSource 注解
076        UseDataSource annotation = method.getAnnotation(UseDataSource.class);
077        if (annotation != null) {
078            return annotation.value();
079        }
080        // 类上定义有 UseDataSource 注解
081        annotation = targetClass.getAnnotation(UseDataSource.class);
082        if (annotation != null) {
083            return annotation.value();
084        }
085        // 接口上定义有 UseDataSource 注解
086        Class<?>[] interfaces = targetClass.getInterfaces();
087        for (Class<?> anInterface : interfaces) {
088            annotation = anInterface.getAnnotation(UseDataSource.class);
089            if (annotation != null) {
090                return annotation.value();
091            }
092        }
093        // 哪里都没有 UseDataSource 注解
094        return "";
095    }
096
097}