All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.zodiac.autoconfigure.datasource.DynamicDataSourceObjectAopSwitcherAutoConfiguration Maven / Gradle / Ivy

There is a newer version: 1.6.8
Show newest version
package org.zodiac.autoconfigure.datasource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.zodiac.autoconfigure.datasource.condition.ConditionalOnDataSourceEnabled;
import org.zodiac.commons.util.Classes;
import org.zodiac.commons.util.Colls;
import org.zodiac.commons.util.lang.Strings;
import org.zodiac.core.aop.MethodInterceptorContext;
import org.zodiac.core.aop.MethodInterceptorHolder;

import reactor.core.publisher.Flux;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;

/**
 * 通过AOP方式进行对注解方式切换数据源提供支持。
 *
 */
@SpringBootConfiguration
@ConditionalOnDataSourceEnabled
@ConditionalOnClass(value = {org.aopalliance.intercept.MethodInterceptor.class, org.zodiac.datasource.DynamicDataSourceObjectHolder.class,
    org.zodiac.script.engine.util.ScriptExpressions.class, reactor.core.CorePublisher.class, org.reactivestreams.Publisher.class})
public class DynamicDataSourceObjectAopSwitcherAutoConfiguration {

    private DataSourceConfigProperties dataSourceConfigProperties;

    public DynamicDataSourceObjectAopSwitcherAutoConfiguration(DataSourceConfigProperties dataSourceConfigProperties) {
        this.dataSourceConfigProperties = dataSourceConfigProperties;
    }

    @Bean
    @ConditionalOnMissingBean
    //@ConfigurationProperties(prefix = "hsweb.datasource")
    protected org.zodiac.datasource.strategy.ExpressionDataSourceSwitchStrategyMatcher expressionDataSourceSwitchStrategyMatcher() {
        return new org.zodiac.datasource.strategy.ExpressionDataSourceSwitchStrategyMatcher().setSwitcher(dataSourceConfigProperties.getDynamicStrategies());
    }

    @Bean
    @ConditionalOnMissingBean
    protected org.zodiac.datasource.strategy.AnnotationDataSourceSwitchStrategyMatcher annotationDataSourceSwitchStrategyMatcher() {
        return new org.zodiac.datasource.strategy.AnnotationDataSourceSwitchStrategyMatcher();
    }

    @Bean
    @ConditionalOnMissingBean
    protected org.zodiac.datasource.strategy.TableSwitchStrategyMatcher alwaysNoMatchStrategyMatcher() {
        return new org.zodiac.datasource.strategy.TableSwitchStrategyMatcher() {
            @Override
            public boolean match(Class target, Method method) {
                return false;
            }

            @Override
            public Strategy getStrategy(MethodInterceptorContext context) {
                return null;
            }
        };
    }

    @Bean
    @ConditionalOnMissingBean
    protected SwitcherMethodMatcherPointcutAdvisor switcherMethodMatcherPointcutAdvisor(
        List matchers, List tableSwitcher) {
        return new SwitcherMethodMatcherPointcutAdvisor(matchers, tableSwitcher);
    }

    public static class SwitcherMethodMatcherPointcutAdvisor extends StaticMethodMatcherPointcutAdvisor {

        private static final long serialVersionUID = -7957131071706966882L;

        private static final Logger logger = LoggerFactory.getLogger(SwitcherMethodMatcherPointcutAdvisor.class);

        private List matchers;

        private List tableSwitcher;

        private Map cache =
            Colls.concurrentMap();
        private Map tableCache =
            Colls.concurrentMap();

        public SwitcherMethodMatcherPointcutAdvisor(List matchers,
            List tableSwitcher) {
            this.matchers = matchers;
            this.tableSwitcher = tableSwitcher;
            setAdvice((org.aopalliance.intercept.MethodInterceptor)methodInvocation -> {
                org.zodiac.datasource.strategy.AnnotationDataSourceSwitchStrategyMatcher.CacheKey key =
                    new org.zodiac.datasource.strategy.AnnotationDataSourceSwitchStrategyMatcher.CacheKey(
                        Classes.getUserClass(methodInvocation.getThis()), methodInvocation.getMethod());
                org.zodiac.datasource.strategy.CachedTableSwitchStrategyMatcher.CacheKey tableKey = new org.zodiac.datasource.strategy.CachedTableSwitchStrategyMatcher.CacheKey(
                    Classes.getUserClass(methodInvocation.getThis()), methodInvocation.getMethod());

                org.zodiac.datasource.strategy.DataSourceSwitchStrategyMatcher matcher = cache.get(key);
                org.zodiac.datasource.strategy.TableSwitchStrategyMatcher tableMatcher = tableCache.get(tableKey);

                Consumer before = context -> {
                };
                AtomicBoolean dataSourceChanged = new AtomicBoolean(false);
                AtomicBoolean databaseChanged = new AtomicBoolean(false);

                if (matcher != null) {
                    before = before.andThen(context -> {
                        org.zodiac.datasource.strategy.AnnotationDataSourceSwitchStrategyMatcher.Strategy strategy = matcher.getStrategy(context);
                        if (strategy == null) {
                            dataSourceChanged.set(false);
                            logger.warn("strategy matcher found:{}, but strategy is null!", matcher);
                        } else {
                            logger.debug("switch datasource. use strategy:{}", strategy);
                            if (strategy.isUseDefaultDataSource()) {
                                org.zodiac.datasource.DynamicDataSourceObjectHolder.switcher().datasource()
                                    .useDefault();
                            } else {
                                try {
                                    String id = strategy.getDataSourceId();
                                    if (Strings.hasText(id)) {
                                        if (id.contains("${")) {
                                            id = org.zodiac.script.engine.util.ScriptExpressions.analytical(id, context.getNamedArguments(), "spel");
                                        }
                                        if (!org.zodiac.datasource.DynamicDataSourceObjectHolder.existing(id)) {
                                            if (strategy.isFallbackDefault()) {
                                                org.zodiac.datasource.DynamicDataSourceObjectHolder.switcher()
                                                    .datasource().useDefault();
                                            } else {
                                                throw new org.zodiac.datasource.exception.DataSourceNotFoundException(
                                                    "数据源[" + id + "]不存在");
                                            }
                                        } else {
                                            org.zodiac.datasource.DynamicDataSourceObjectHolder.switcher().datasource()
                                                .use(id);
                                        }
                                        dataSourceChanged.set(true);
                                    }
                                } catch (RuntimeException e) {
                                    dataSourceChanged.set(false);
                                    throw e;
                                } catch (Exception e) {
                                    dataSourceChanged.set(false);
                                    throw new RuntimeException(e.getMessage(), e);
                                }
                            }
                            if (Strings.hasText(strategy.getDatabase())) {
                                databaseChanged.set(true);
                                org.zodiac.datasource.DynamicDataSourceObjectHolder.switcher().datasource()
                                    .use(strategy.getDatabase());
                            }
                        }
                    });
                }
                if (tableMatcher != null) {
                    before = before.andThen(context -> {
                        org.zodiac.datasource.strategy.TableSwitchStrategyMatcher.Strategy strategy = tableMatcher.getStrategy(context);
                        if (null != strategy) {
                            logger.debug("switch table. use strategy:{}", strategy);
                            // strategy.getMapping().forEach(DataSourceHolder.switcher()::use);
                        } else {
                            logger.warn("table strategy matcher found:{}, but strategy is null!", matcher);
                        }
                    });
                }

                Class returnType = methodInvocation.getMethod().getReturnType();

                if (returnType.isAssignableFrom(Flux.class)) {
                    // TODO: 2019-10-08
                }
                MethodInterceptorHolder holder = MethodInterceptorHolder.create(methodInvocation);
                before.accept(holder.createParamContext());
                try {
                    return methodInvocation.proceed();
                } finally {
                    if (dataSourceChanged.get()) {
                        org.zodiac.datasource.DynamicDataSourceObjectHolder.switcher().datasource().useLast();
                    }
                    if (databaseChanged.get()) {
                        org.zodiac.datasource.DynamicDataSourceObjectHolder.switcher().datasource().useLast();
                    }
                    // DataSourceHolder.tableSwitcher().reset();
                }
            });
        }

        @Override
        public boolean matches(Method method, Class aClass) {
            Class targetClass = Classes.getUserClass(aClass);

            org.zodiac.datasource.strategy.AnnotationDataSourceSwitchStrategyMatcher.CacheKey key =
                new org.zodiac.datasource.strategy.AnnotationDataSourceSwitchStrategyMatcher.CacheKey(targetClass, method);
            matchers.stream().filter(matcher -> matcher.match(targetClass, method)).findFirst()
                .ifPresent((matcher) -> cache.put(key, matcher));

            boolean datasourceMatched = cache.containsKey(key);
            boolean tableMatched = false;
            if (null != tableSwitcher) {
                org.zodiac.datasource.strategy.CachedTableSwitchStrategyMatcher.CacheKey tableCacheKey =
                    new org.zodiac.datasource.strategy.CachedTableSwitchStrategyMatcher.CacheKey(targetClass, method);
                tableSwitcher.stream().filter(matcher -> matcher.match(targetClass, method)).findFirst()
                    .ifPresent((matcher) -> tableCache.put(tableCacheKey, matcher));
                tableMatched = tableCache.containsKey(tableCacheKey);
            }

            return datasourceMatched || tableMatched;
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy