瀏覽代碼

修复feign多服务打包运行接口地址前缀问题;

woody 1 年之前
父節點
當前提交
b07d2d08ff

+ 7 - 1
framework-base/src/main/java/com/chelvc/framework/base/config/MultiserverMvcConfigurer.java

@@ -25,7 +25,13 @@ public class MultiserverMvcConfigurer implements WebMvcRegistrations {
         for (Resource resource : resources) {
             String prefix = ApplicationContextHolder.getResourcePrefix(resource);
             if (StringUtils.notEmpty(prefix)) {
-                prefixes.put(prefix, clazz -> ApplicationContextHolder.isResourceClass(resource, clazz));
+                prefixes.put(prefix, clazz -> {
+                    if (ApplicationContextHolder.isResourceClass(resource, clazz)) {
+                        ApplicationContextHolder.cachingResourcePrefix(prefix, clazz);
+                        return true;
+                    }
+                    return false;
+                });
             }
         }
         RequestMappingHandlerMapping mapping = new RequestMappingHandlerMapping();

+ 39 - 1
framework-base/src/main/java/com/chelvc/framework/base/context/ApplicationContextHolder.java

@@ -2,6 +2,7 @@ package com.chelvc.framework.base.context;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.reflect.Method;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -16,7 +17,6 @@ import java.util.function.Supplier;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
-import com.beust.jcommander.internal.Sets;
 import com.chelvc.framework.base.util.SpringUtils;
 import com.chelvc.framework.common.util.AssertUtils;
 import com.chelvc.framework.common.util.JacksonUtils;
@@ -24,6 +24,8 @@ import com.chelvc.framework.common.util.ObjectUtils;
 import com.chelvc.framework.common.util.StringUtils;
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
 import lombok.NonNull;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeansException;
@@ -78,6 +80,11 @@ public class ApplicationContextHolder implements ApplicationContextAware {
      */
     private static ApplicationContext APPLICATION_CONTEXT;
 
+    /**
+     * 资源前缀/接口地址集合映射表
+     */
+    private static Map<String, Set<String>> RESOURCE_PREFIX_MAPPING;
+
     @Override
     public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
         APPLICATION_CONTEXT = applicationContext;
@@ -618,6 +625,37 @@ public class ApplicationContextHolder implements ApplicationContextAware {
         }
     }
 
+    /**
+     * 判断资源地址是否存在前缀
+     *
+     * @param prefix 前缀地址
+     * @param uri    资源地址
+     * @return true/false
+     */
+    public static boolean isResourcePrefixed(@NonNull String prefix, @NonNull String uri) {
+        if (ObjectUtils.isEmpty(RESOURCE_PREFIX_MAPPING)) {
+            return false;
+        }
+        Set<String> uris = RESOURCE_PREFIX_MAPPING.get(prefix);
+        return ObjectUtils.notEmpty(uris) && uris.contains(uri);
+    }
+
+    /**
+     * 缓存资源地址前缀
+     *
+     * @param prefix 前缀地址
+     * @param clazz  资源对象
+     */
+    public synchronized static void cachingResourcePrefix(@NonNull String prefix, @NonNull Class<?> clazz) {
+        if (RESOURCE_PREFIX_MAPPING == null) {
+            RESOURCE_PREFIX_MAPPING = Maps.newHashMap();
+        }
+        Set<String> uris = RESOURCE_PREFIX_MAPPING.computeIfAbsent(prefix, p -> Sets.newHashSet());
+        for (Method method : clazz.getDeclaredMethods()) {
+            uris.addAll(SpringUtils.getApis(method));
+        }
+    }
+
     /**
      * 查找接口对象实例
      *

+ 53 - 141
framework-database/src/main/java/com/chelvc/framework/database/interceptor/DynamicDatasourceInterceptor.java

@@ -8,16 +8,10 @@ import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSour
 import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
 import com.chelvc.framework.common.util.StringUtils;
 import com.google.common.collect.Maps;
-import lombok.NonNull;
 import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.annotation.Around;
 import org.aspectj.lang.annotation.Aspect;
 import org.springframework.aop.framework.AopProxyUtils;
-import org.springframework.beans.BeansException;
-import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
-import org.springframework.beans.factory.support.BeanDefinitionRegistry;
-import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
-import org.springframework.beans.factory.support.RootBeanDefinition;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
 import org.springframework.core.Ordered;
 import org.springframework.core.annotation.Order;
@@ -29,159 +23,77 @@ import org.springframework.util.CollectionUtils;
  * @author Woody
  * @date 2024/1/30
  */
+@Aspect
+@Order(Ordered.HIGHEST_PRECEDENCE)
 @ConditionalOnClass(DynamicDataSourceContextHolder.class)
-public class DynamicDatasourceInterceptor implements BeanDefinitionRegistryPostProcessor {
-    /**
-     * 判断是否是兼容模式
-     *
-     * @return true/false
-     */
-    private boolean isCompatible() {
-        try {
-            // 尝试加载@DubboService注解
-            this.getClass().getClassLoader().loadClass("org.apache.dubbo.config.annotation.DubboService");
-        } catch (ClassNotFoundException e) {
-            return false;
-        }
-        return true;
-    }
-
-    @Override
-    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
-    }
+public class DynamicDatasourceInterceptor {
+    private final Map<String, String> datasourceContext;
 
-    @Override
-    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
-        if (this.isCompatible()) {
-            registry.registerBeanDefinition("compatibleDynamicDatasourceRouter",
-                    new RootBeanDefinition(CompatibleDynamicDatasourceRouter.class));
+    public DynamicDatasourceInterceptor(DynamicDataSourceProperties properties) {
+        // 初始化数据源标识集合
+        Set<String> keys = properties.getDatasource().keySet();
+        if (CollectionUtils.isEmpty(keys)) {
+            this.datasourceContext = Collections.emptyMap();
         } else {
-            registry.registerBeanDefinition("standardDynamicDatasourceRouter",
-                    new RootBeanDefinition(StandardDynamicDatasourceRouter.class));
+            this.datasourceContext = Maps.newHashMapWithExpectedSize(keys.size());
+            keys.forEach(value -> {
+                for (String key : StringUtils.splitActives(value)) {
+                    this.datasourceContext.put(key, value);
+                }
+            });
         }
     }
 
     /**
-     * 动态数据源路由器抽象实现
+     * 查找数据源标识
+     *
+     * @param point 方法拦截点
+     * @return 数据源标识
      */
-    public static abstract class AbstractDynamicDatasourceRouter {
-        private final Map<String, String> datasourceContext;
+    private String lookupDatasourceKey(ProceedingJoinPoint point) {
+        Object target = point.getTarget();
 
-        public AbstractDynamicDatasourceRouter(@NonNull DynamicDataSourceProperties properties) {
-            // 初始化数据源标识集合
-            Set<String> keys = properties.getDatasource().keySet();
-            if (CollectionUtils.isEmpty(keys)) {
-                this.datasourceContext = Collections.emptyMap();
-            } else {
-                this.datasourceContext = Maps.newHashMapWithExpectedSize(keys.size());
-                keys.forEach(value -> {
-                    for (String key : StringUtils.splitActives(value)) {
-                        this.datasourceContext.put(key, value);
-                    }
-                });
-            }
+        // 根据目标类对象查找数据源标识
+        String className = AopProxyUtils.ultimateTargetClass(target).getName();
+        Set<Map.Entry<String, String>> routes = this.datasourceContext.entrySet();
+        String key = routes.stream().filter(entry -> className.startsWith(entry.getKey())).findAny()
+                .map(Map.Entry::getValue).orElse(null);
+        if (StringUtils.notEmpty(key)) {
+            return key;
         }
 
-        /**
-         * 查找数据源标识
-         *
-         * @param point 方法拦截点
-         * @return 数据源标识
-         */
-        private String lookupDatasourceKey(ProceedingJoinPoint point) {
-            Object target = point.getTarget();
-
-            // 根据目标类对象查找数据源标识
-            String className = AopProxyUtils.ultimateTargetClass(target).getName();
-            Set<Map.Entry<String, String>> routes = this.datasourceContext.entrySet();
-            String key = routes.stream().filter(entry -> className.startsWith(entry.getKey())).findAny()
-                    .map(Map.Entry::getValue).orElse(null);
-            if (StringUtils.notEmpty(key)) {
-                return key;
-            }
-
-            // 根据目标接口对象查找数据源标识
-            Class<?>[] interfaces = AopProxyUtils.proxiedUserInterfaces(target);
-            if (interfaces.length > 0) {
-                for (Class<?> clazz : interfaces) {
-                    String interfaceName = clazz.getName();
-                    String route = routes.stream().filter(entry -> interfaceName.startsWith(entry.getKey())).findAny()
-                            .map(Map.Entry::getValue).orElse(null);
-                    if (StringUtils.notEmpty(route)) {
-                        return route;
-                    }
+        // 根据目标接口对象查找数据源标识
+        Class<?>[] interfaces = AopProxyUtils.proxiedUserInterfaces(target);
+        if (interfaces.length > 0) {
+            for (Class<?> clazz : interfaces) {
+                String interfaceName = clazz.getName();
+                String route = routes.stream().filter(entry -> interfaceName.startsWith(entry.getKey())).findAny()
+                        .map(Map.Entry::getValue).orElse(null);
+                if (StringUtils.notEmpty(route)) {
+                    return route;
                 }
             }
-            return null;
-        }
-
-        /**
-         * 数据源路由
-         *
-         * @param point 方法拦截点
-         * @return 方法调用结果
-         * @throws Throwable 方法调用异常
-         */
-        protected Object routing(ProceedingJoinPoint point) throws Throwable {
-            DynamicDataSourceContextHolder.push(this.lookupDatasourceKey(point));
-            try {
-                return point.proceed(point.getArgs());
-            } finally {
-                DynamicDataSourceContextHolder.poll();
-            }
-        }
-    }
-
-    /**
-     * 动态数据源拦截器标准实现
-     */
-    @Aspect
-    @Order(Ordered.HIGHEST_PRECEDENCE)
-    public static class StandardDynamicDatasourceRouter extends AbstractDynamicDatasourceRouter {
-        public StandardDynamicDatasourceRouter(DynamicDataSourceProperties properties) {
-            super(properties);
-        }
-
-        /**
-         * 数据源路由
-         *
-         * @param point 方法拦截点
-         * @return 方法调用结果
-         * @throws Throwable 方法调用异常
-         */
-        @Around("@within(org.springframework.stereotype.Service) " +
-                "|| @within(org.apache.ibatis.annotations.Mapper) " +
-                "|| this(com.baomidou.mybatisplus.core.mapper.BaseMapper) " +
-                "|| this(com.chelvc.framework.database.support.EnhanceMapper)")
-        public Object routing(ProceedingJoinPoint point) throws Throwable {
-            return super.routing(point);
         }
+        return null;
     }
 
     /**
-     * 动态数据源拦截器兼容实现
+     * 数据源路由
+     *
+     * @param point 方法拦截点
+     * @return 方法调用结果
+     * @throws Throwable 方法调用异常
      */
-    @Aspect
-    @Order(Ordered.HIGHEST_PRECEDENCE)
-    public static class CompatibleDynamicDatasourceRouter extends AbstractDynamicDatasourceRouter {
-        public CompatibleDynamicDatasourceRouter(DynamicDataSourceProperties properties) {
-            super(properties);
-        }
-
-        /**
-         * 数据源路由
-         *
-         * @param point 方法拦截点
-         * @return 方法调用结果
-         * @throws Throwable 方法调用异常
-         */
-        @Around("@within(org.springframework.stereotype.Service) " +
-                "|| @within(org.apache.ibatis.annotations.Mapper) " +
-                "|| @within(org.apache.dubbo.config.annotation.DubboService) " +
-                "|| this(com.baomidou.mybatisplus.core.mapper.BaseMapper) " +
-                "|| this(com.chelvc.framework.database.support.EnhanceMapper)")
-        public Object routing(ProceedingJoinPoint point) throws Throwable {
-            return super.routing(point);
+    @Around("@within(org.springframework.stereotype.Service) " +
+            "|| @within(org.apache.ibatis.annotations.Mapper) " +
+            "|| this(com.baomidou.mybatisplus.core.mapper.BaseMapper) " +
+            "|| this(com.chelvc.framework.database.support.EnhanceMapper)")
+    public Object routing(ProceedingJoinPoint point) throws Throwable {
+        DynamicDataSourceContextHolder.push(this.lookupDatasourceKey(point));
+        try {
+            return point.proceed(point.getArgs());
+        } finally {
+            DynamicDataSourceContextHolder.poll();
         }
     }
 }

+ 9 - 0
framework-feign/src/main/java/com/chelvc/framework/feign/interceptor/FeignHeaderInterceptor.java

@@ -1,7 +1,9 @@
 package com.chelvc.framework.feign.interceptor;
 
+import com.chelvc.framework.base.context.ApplicationContextHolder;
 import com.chelvc.framework.base.context.Session;
 import com.chelvc.framework.base.context.SessionContextHolder;
+import com.chelvc.framework.base.util.HttpUtils;
 import com.chelvc.framework.common.model.Platform;
 import com.chelvc.framework.common.model.Terminal;
 import com.chelvc.framework.common.util.ObjectUtils;
@@ -19,6 +21,13 @@ import org.springframework.stereotype.Component;
 public class FeignHeaderInterceptor implements RequestInterceptor {
     @Override
     public void apply(RequestTemplate template) {
+        // 打包运行环境自动添加接口地址前缀
+        String path = template.path();
+        String server = template.feignTarget().name();
+        if (ApplicationContextHolder.isResourcePrefixed(server, path)) {
+            template.uri(HttpUtils.uri(server, path));
+        }
+
         // 拷贝当前会话请求头
         Session session = SessionContextHolder.getSession(false);
         if (session != null) {

+ 5 - 8
framework-nacos/src/main/java/com/chelvc/framework/nacos/config/MultiserverNacosConfigurer.java

@@ -22,24 +22,21 @@ import org.springframework.core.io.Resource;
  */
 @RequiredArgsConstructor(onConstructor = @__(@Autowired))
 public class MultiserverNacosConfigurer implements ApplicationRunner {
+    private final NacosServiceRegistry registry;
+    private final NacosDiscoveryProperties properties;
     private final ApplicationContext applicationContext;
-    private final NacosServiceRegistry serviceRegistry;
-    private final NacosDiscoveryProperties discoveryProperties;
 
     @Override
     public void run(ApplicationArguments args) {
         String applicationName = ApplicationContextHolder.getApplicationName(this.applicationContext);
         for (Resource resource : ApplicationContextHolder.getApplicationResources()) {
-            // 获取应用服务名称
+            // 将打包的服务注册到nacos(排除当前服务)
             String service = ApplicationContextHolder.getApplicationName(resource);
             if (StringUtils.isEmpty(service) || service.equals(applicationName)) {
                 continue;
             }
-
-            // 注册指定服务到Nacos
-            this.discoveryProperties.setService(service);
-            NacosRegistration registration = new NacosRegistration(null, this.discoveryProperties, applicationContext);
-            this.serviceRegistry.register(registration);
+            this.properties.setService(service);
+            this.registry.register(new NacosRegistration(null, this.properties, this.applicationContext));
         }
     }
 }