From 53a482fe74090d87bbba03f1b070e702dba26945 Mon Sep 17 00:00:00 2001 From: lbw Date: Mon, 24 Jul 2023 17:22:28 +0800 Subject: [PATCH] =?UTF-8?q?:recycle:=20Refactoring=20code.=20=E9=87=8D?= =?UTF-8?q?=E6=9E=84feign=20client=20registrar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feign/PigxFeignAutoConfiguration.java | 3 + .../openfeign/PigxFeignClientsRegistrar.java | 272 ++++++++++++++++++ ...ot.autoconfigure.AutoConfiguration.imports | 9 + 3 files changed, 284 insertions(+) create mode 100644 pigx-common/pigx-common-feign/src/main/java/org/springframework/cloud/openfeign/PigxFeignClientsRegistrar.java create mode 100644 pigx-upms/pigx-upms-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports diff --git a/pigx-common/pigx-common-feign/src/main/java/com/pig4cloud/pigx/common/feign/PigxFeignAutoConfiguration.java b/pigx-common/pigx-common-feign/src/main/java/com/pig4cloud/pigx/common/feign/PigxFeignAutoConfiguration.java index 6a4ca5641..db0f54436 100644 --- a/pigx-common/pigx-common-feign/src/main/java/com/pig4cloud/pigx/common/feign/PigxFeignAutoConfiguration.java +++ b/pigx-common/pigx-common-feign/src/main/java/com/pig4cloud/pigx/common/feign/PigxFeignAutoConfiguration.java @@ -24,9 +24,11 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.cloud.openfeign.PigxFeignClientsRegistrar; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; /** * @author lengleng @@ -36,6 +38,7 @@ import org.springframework.context.annotation.Configuration; */ @Configuration @ConditionalOnClass(Feign.class) +@Import(PigxFeignClientsRegistrar.class) @AutoConfigureAfter(EnableFeignClients.class) public class PigxFeignAutoConfiguration { diff --git a/pigx-common/pigx-common-feign/src/main/java/org/springframework/cloud/openfeign/PigxFeignClientsRegistrar.java b/pigx-common/pigx-common-feign/src/main/java/org/springframework/cloud/openfeign/PigxFeignClientsRegistrar.java new file mode 100644 index 000000000..eea83315b --- /dev/null +++ b/pigx-common/pigx-common-feign/src/main/java/org/springframework/cloud/openfeign/PigxFeignClientsRegistrar.java @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2018-2025, lengleng All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * Neither the name of the pig4cloud.com developer nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * Author: lengleng (wangiegie@gmail.com) + */ + +package org.springframework.cloud.openfeign; + +import com.pig4cloud.pigx.common.feign.PigxFeignAutoConfiguration; +import lombok.Getter; +import org.springframework.beans.factory.BeanClassLoaderAware; +import org.springframework.beans.factory.config.BeanDefinitionHolder; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.boot.context.annotation.ImportCandidates; +import org.springframework.context.EnvironmentAware; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.env.Environment; +import org.springframework.core.io.support.SpringFactoriesLoader; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.lang.Nullable; +import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; + +import java.util.List; +import java.util.Map; + +/** + * @author L.cm + * @date 2020/2/8 + *

+ * feign 自动配置功能 from mica + */ +public class PigxFeignClientsRegistrar + implements ImportBeanDefinitionRegistrar, BeanClassLoaderAware, EnvironmentAware { + + private final static String BASE_URL = "http://127.0.0.1:${server.port}${server.servlet.context-path}"; + + @Getter + private ClassLoader beanClassLoader; + + @Getter + private Environment environment; + + @Override + public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { + registerFeignClients(registry); + } + + @Override + public void setBeanClassLoader(ClassLoader classLoader) { + this.beanClassLoader = classLoader; + } + + private void registerFeignClients(BeanDefinitionRegistry registry) { + List feignClients = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), + getBeanClassLoader()); + + // 支持 springboot 2.7 + 最新版本的配置方式 + ImportCandidates.load(FeignClient.class, getBeanClassLoader()).forEach(feignClients::add); + + // 如果 spring.factories 里为空 + if (feignClients.isEmpty()) { + return; + } + for (String className : feignClients) { + try { + Class clazz = beanClassLoader.loadClass(className); + AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(clazz, + FeignClient.class); + if (attributes == null) { + continue; + } + // 如果已经存在该 bean,支持原生的 Feign + if (registry.containsBeanDefinition(className)) { + continue; + } + + registerClientConfiguration(registry, getClientName(attributes), className, attributes.get("configuration")); + + validate(attributes); + BeanDefinitionBuilder definition = BeanDefinitionBuilder + .genericBeanDefinition(FeignClientFactoryBean.class); + definition.addPropertyValue("url", getUrl(attributes)); + definition.addPropertyValue("path", getPath(attributes)); + String name = getName(attributes); + definition.addPropertyValue("name", name); + + // 兼容最新版本的 spring-cloud-openfeign,尚未发布 + StringBuilder aliasBuilder = new StringBuilder(18); + if (attributes.containsKey("contextId")) { + String contextId = getContextId(attributes); + aliasBuilder.append(contextId); + definition.addPropertyValue("contextId", contextId); + } + else { + aliasBuilder.append(name); + } + + definition.addPropertyValue("type", className); + definition.addPropertyValue("dismiss404", + Boolean.parseBoolean(String.valueOf(attributes.get("dismiss404")))); + Object fallbackFactory = attributes.get("fallbackFactory"); + if (fallbackFactory != null) { + definition.addPropertyValue("fallbackFactory", fallbackFactory instanceof Class ? fallbackFactory + : ClassUtils.resolveClassName(fallbackFactory.toString(), null)); + } + definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory")); + definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); + + AbstractBeanDefinition beanDefinition = definition.getBeanDefinition(); + + // alias + String alias = aliasBuilder.append("FeignClient").toString(); + + // has a default, won't be null + boolean primary = (Boolean) attributes.get("primary"); + + beanDefinition.setPrimary(primary); + + String qualifier = getQualifier(attributes); + if (StringUtils.hasText(qualifier)) { + alias = qualifier; + } + + BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, + new String[] { alias }); + BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry); + + } + catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } + } + + /** + * Return the class used by {@link SpringFactoriesLoader} to load configuration + * candidates. + * @return the factory class + */ + private Class getSpringFactoriesLoaderFactoryClass() { + return PigxFeignAutoConfiguration.class; + } + + private void validate(Map attributes) { + AnnotationAttributes annotation = AnnotationAttributes.fromMap(attributes); + // This blows up if an aliased property is overspecified + FeignClientsRegistrar.validateFallback(annotation.getClass("fallback")); + FeignClientsRegistrar.validateFallbackFactory(annotation.getClass("fallbackFactory")); + } + + private String getName(Map attributes) { + String name = (String) attributes.get("serviceId"); + if (!StringUtils.hasText(name)) { + name = (String) attributes.get("name"); + } + if (!StringUtils.hasText(name)) { + name = (String) attributes.get("value"); + } + name = resolve(name); + return FeignClientsRegistrar.getName(name); + } + + private String getContextId(Map attributes) { + String contextId = (String) attributes.get("contextId"); + if (!StringUtils.hasText(contextId)) { + return getName(attributes); + } + + contextId = resolve(contextId); + return FeignClientsRegistrar.getName(contextId); + } + + private String resolve(String value) { + if (StringUtils.hasText(value)) { + return this.environment.resolvePlaceholders(value); + } + return value; + } + + private String getUrl(Map attributes) { + // 如果是单体项目自动注入 & url 为空 + String url = (String) attributes.get("url"); + boolean present = ClassUtils.isPresent("com.alibaba.cloud.nacos.discovery.NacosDiscoveryClient", + this.getClass().getClassLoader()); + if (!StringUtils.hasText(url) && !present) { + url = resolve(BASE_URL); + } + + return FeignClientsRegistrar.getUrl(url); + } + + private String getPath(Map attributes) { + String path = resolve((String) attributes.get("path")); + return FeignClientsRegistrar.getPath(path); + } + + @Nullable + private String getQualifier(@Nullable Map client) { + if (client == null) { + return null; + } + String qualifier = (String) client.get("qualifier"); + if (StringUtils.hasText(qualifier)) { + return qualifier; + } + return null; + } + + @Nullable + private String getClientName(@Nullable Map client) { + if (client == null) { + return null; + } + String value = (String) client.get("contextId"); + if (!StringUtils.hasText(value)) { + value = (String) client.get("value"); + } + if (!StringUtils.hasText(value)) { + value = (String) client.get("name"); + } + if (!StringUtils.hasText(value)) { + value = (String) client.get("serviceId"); + } + if (StringUtils.hasText(value)) { + return value; + } + + throw new IllegalStateException( + "Either 'name' or 'value' must be provided in @" + FeignClient.class.getSimpleName()); + } + + private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class); + builder.addConstructorArgValue(name); + builder.addConstructorArgValue(configuration); + registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(), + builder.getBeanDefinition()); + } + + private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object className, + Object configuration) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class); + builder.addConstructorArgValue(name); + builder.addConstructorArgValue(className); + builder.addConstructorArgValue(configuration); + registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(), + builder.getBeanDefinition()); + } + + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } + +} diff --git a/pigx-upms/pigx-upms-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/pigx-upms/pigx-upms-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 000000000..460350b55 --- /dev/null +++ b/pigx-upms/pigx-upms-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,9 @@ +com.pig4cloud.pigx.admin.api.feign.RemoteDataScopeService +com.pig4cloud.pigx.admin.api.feign.RemoteClientDetailsService +com.pig4cloud.pigx.admin.api.feign.RemoteLogService +com.pig4cloud.pigx.admin.api.feign.RemoteParamService +com.pig4cloud.pigx.admin.api.feign.RemoteTenantService +com.pig4cloud.pigx.admin.api.feign.RemoteTokenService +com.pig4cloud.pigx.admin.api.feign.RemoteUserService +com.pig4cloud.pigx.admin.api.feign.RemoteDictService +com.pig4cloud.pigx.admin.api.feign.RemoteAuditLogService