001/*
002 *  Copyright (c) 2022-2023, 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
019
020import com.mybatisflex.annotation.UseDataSource;
021import com.mybatisflex.core.datasource.DataSourceKey;
022import com.mybatisflex.core.util.StringUtil;
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 * 
038 * @since 2023-06-25
039 */
040public class DataSourceInterceptor implements MethodInterceptor {
041
042    /**
043     * 缓存方法对应的数据源。
044     */
045    private final Map<Object, String> dsCache = new ConcurrentHashMap<>();
046
047    @Override
048    public Object invoke(MethodInvocation invocation) throws Throwable {
049        String dsKey = DataSourceKey.getByManual();
050        if (StringUtil.isNotBlank(dsKey)) {
051            return invocation.proceed();
052        }
053
054        dsKey = findDataSourceKey(invocation.getMethod(), invocation.getThis().getClass());
055        if (StringUtil.isBlank(dsKey)) {
056            return invocation.proceed();
057        }
058
059        //方法嵌套时,挂起的 key
060        String suspendKey = DataSourceKey.getByAnnotation();
061
062        try {
063            DataSourceKey.useWithAnnotation(dsKey);
064            return invocation.proceed();
065        } finally {
066            //恢复挂起的 key
067            if (suspendKey != null) {
068                DataSourceKey.useWithAnnotation(suspendKey);
069            } else {
070                DataSourceKey.clear();
071            }
072        }
073    }
074
075    private String findDataSourceKey(Method method, Class<?> targetClass) {
076        Object cacheKey = new MethodClassKey(method, targetClass);
077        String dsKey = this.dsCache.get(cacheKey);
078        if (dsKey == null) {
079            dsKey = determineDataSourceKey(method, targetClass);
080            this.dsCache.put(cacheKey, dsKey);
081        }
082        return dsKey;
083    }
084
085
086    private String determineDataSourceKey(Method method, Class<?> targetClass) {
087
088        // 方法上定义有 UseDataSource 注解
089        UseDataSource annotation = method.getAnnotation(UseDataSource.class);
090        if (annotation != null) {
091            return annotation.value();
092        }
093
094        // 类上定义有 UseDataSource 注解
095        annotation = targetClass.getAnnotation(UseDataSource.class);
096        if (annotation != null) {
097            return annotation.value();
098        }
099
100        // 接口上定义有 UseDataSource 注解
101        Class<?>[] interfaces = targetClass.getInterfaces();
102        for (Class<?> anInterface : interfaces) {
103            annotation = anInterface.getAnnotation(UseDataSource.class);
104            if (annotation != null) {
105                return annotation.value();
106            }
107        }
108
109        return "";
110    }
111
112}