diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboOpenFeignAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboOpenFeignAutoConfiguration.java index 7b0c4c97..877d2ccd 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboOpenFeignAutoConfiguration.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboOpenFeignAutoConfiguration.java @@ -58,7 +58,7 @@ public class DubboOpenFeignAutoConfiguration { private ObjectProvider feignContextObjectProvider; @EventListener(ApplicationReadyEvent.class) - public void onContextRefreshed(ApplicationReadyEvent event) { + public void onApplicationReady(ApplicationReadyEvent event) { ConfigurableApplicationContext applicationContext = event.getApplicationContext(); // Resolve the subscribed service names for @FeignClient Set feignClientServiceNames = resolveFeignClientServiceNames(applicationContext); diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboRestAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboRestAutoConfiguration.java index 775f706b..c325e96e 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboRestAutoConfiguration.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboRestAutoConfiguration.java @@ -17,10 +17,11 @@ package org.springframework.cloud.alibaba.dubbo.autoconfigure; import com.fasterxml.jackson.databind.ObjectMapper; +import feign.Contract; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfigureOrder; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.cloud.alibaba.dubbo.rest.feign.FeignRestMetadataResolver; import org.springframework.cloud.alibaba.dubbo.rest.feign.RestMetadataConfigService; -import org.springframework.cloud.alibaba.dubbo.rest.feign.RestMetadataResolver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; @@ -35,14 +36,9 @@ import org.springframework.core.Ordered; public class DubboRestAutoConfiguration { @Bean - @ConditionalOnMissingBean - public ObjectMapper objectMapper() { - return new ObjectMapper(); - } - - @Bean - public RestMetadataResolver metadataJsonResolver(ObjectMapper objectMapper) { - return new RestMetadataResolver(objectMapper); + public FeignRestMetadataResolver metadataJsonResolver( + ObjectProvider objectMapper, ObjectProvider contract) { + return new FeignRestMetadataResolver(objectMapper, contract); } @Bean diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboRestDiscoveryAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboRestDiscoveryAutoConfiguration.java index 25ceaf0d..0dcfdec1 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboRestDiscoveryAutoConfiguration.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboRestDiscoveryAutoConfiguration.java @@ -31,7 +31,7 @@ import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.alibaba.dubbo.rest.feign.RestMetadataConfigService; -import org.springframework.cloud.alibaba.dubbo.rest.feign.RestMetadataResolver; +import org.springframework.cloud.alibaba.dubbo.rest.feign.FeignRestMetadataResolver; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -63,7 +63,7 @@ public class DubboRestDiscoveryAutoConfiguration { private DiscoveryClient discoveryClient; @Autowired - private RestMetadataResolver restMetadataResolver; + private FeignRestMetadataResolver feignRestMetadataResolver; @Autowired(required = false) private ObjectMapper objectMapper = new ObjectMapper(); @@ -206,7 +206,7 @@ public class DubboRestDiscoveryAutoConfiguration { // Map> restMetadata = objectMapper.readValue(restMetadataJson, Map.class); // // restMetadata.forEach((dubboServiceName, restJsons) -> { -// restJsons.stream().map(restMetadataResolver::resolveRequest).forEach(request -> { +// restJsons.stream().map(feignRestMetadataResolver::resolveRequest).forEach(request -> { // referenceBeanCache.put(request.toString(), buildReferenceBean(dubboServiceName)); // }); // }); @@ -231,7 +231,7 @@ public class DubboRestDiscoveryAutoConfiguration { // Map> restMetadata = objectMapper.readValue(restMetadataJson, Map.class); // // restMetadata.forEach((dubboServiceName, restJsons) -> { -// restJsons.stream().map(restMetadataResolver::resolveRequest).forEach(request -> { +// restJsons.stream().map(feignRestMetadataResolver::resolveRequest).forEach(request -> { // referenceBeanCache.put(request.toString(), buildReferenceBean(dubboServiceName)); // }); // }); diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboRestMetadataRegistrationAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboRestMetadataRegistrationAutoConfiguration.java index 2b901e6f..1179cb0f 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboRestMetadataRegistrationAutoConfiguration.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboRestMetadataRegistrationAutoConfiguration.java @@ -23,8 +23,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.alibaba.dubbo.rest.feign.FeignRestMetadataResolver; import org.springframework.cloud.alibaba.dubbo.rest.feign.RestMetadataConfigService; -import org.springframework.cloud.alibaba.dubbo.rest.feign.RestMetadataResolver; import org.springframework.cloud.alibaba.dubbo.rest.metadata.ServiceRestMetadata; import org.springframework.cloud.client.discovery.event.InstancePreRegisteredEvent; import org.springframework.cloud.client.serviceregistry.Registration; @@ -57,7 +57,7 @@ public class DubboRestMetadataRegistrationAutoConfiguration { private ObjectMapper objectMapper; @Autowired - private RestMetadataResolver restMetadataResolver; + private FeignRestMetadataResolver feignRestMetadataResolver; @Autowired private RestMetadataConfigService metadataConfigService; @@ -66,7 +66,7 @@ public class DubboRestMetadataRegistrationAutoConfiguration { @EventListener(ServiceBeanExportedEvent.class) public void recordRestMetadata(ServiceBeanExportedEvent event) throws JsonProcessingException { ServiceBean serviceBean = event.getServiceBean(); - serviceRestMetadata.addAll(restMetadataResolver.resolve(serviceBean)); + serviceRestMetadata.addAll(feignRestMetadataResolver.resolveServiceRestMetadata(serviceBean)); } /** diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/DubboFeignClientsConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/DubboFeignClientsConfiguration.java index 6fa4a978..cbd61416 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/DubboFeignClientsConfiguration.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/DubboFeignClientsConfiguration.java @@ -16,7 +16,10 @@ */ package org.springframework.cloud.alibaba.dubbo.openfeign; -import feign.Feign; +import feign.*; +import feign.codec.Decoder; +import feign.codec.Encoder; +import feign.codec.ErrorDecoder; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboOpenFeignAutoConfiguration; @@ -45,12 +48,132 @@ public class DubboFeignClientsConfiguration { public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof Feign.Builder) { Feign.Builder builder = (Feign.Builder) bean; - Feign feign = builder.build(); + BuilderWrapper wrapper = new BuilderWrapper(builder); + return wrapper; } return bean; } }; } + private static class BuilderWrapper extends Feign.Builder { + + private final Feign.Builder delegate; + + private BuilderWrapper(Feign.Builder delegate) { + this.delegate = delegate; + } + + @Override + public Feign.Builder logLevel(Logger.Level logLevel) { + return delegate.logLevel(logLevel); + } + + @Override + public Feign.Builder contract(Contract contract) { + delegate.contract(contract); + return this; + } + + @Override + public Feign.Builder client(Client client) { + delegate.client(client); + return this; + } + + @Override + public Feign.Builder retryer(Retryer retryer) { + delegate.retryer(retryer); + return this; + } + + @Override + public Feign.Builder logger(Logger logger) { + delegate.logger(logger); + return this; + } + + @Override + public Feign.Builder encoder(Encoder encoder) { + delegate.encoder(encoder); + return this; + } + + @Override + public Feign.Builder decoder(Decoder decoder) { + delegate.decoder(decoder); + return this; + } + + @Override + public Feign.Builder queryMapEncoder(QueryMapEncoder queryMapEncoder) { + delegate.queryMapEncoder(queryMapEncoder); + return this; + } + + @Override + public Feign.Builder mapAndDecode(ResponseMapper mapper, Decoder decoder) { + delegate.mapAndDecode(mapper, decoder); + return this; + } + + @Override + public Feign.Builder decode404() { + delegate.decode404(); + return this; + } + + @Override + public Feign.Builder errorDecoder(ErrorDecoder errorDecoder) { + delegate.errorDecoder(errorDecoder); + return this; + } + + @Override + public Feign.Builder options(Request.Options options) { + delegate.options(options); + return this; + } + + @Override + public Feign.Builder requestInterceptor(RequestInterceptor requestInterceptor) { + delegate.requestInterceptor(requestInterceptor); + return this; + } + + @Override + public Feign.Builder requestInterceptors(Iterable requestInterceptors) { + delegate.requestInterceptors(requestInterceptors); + return this; + } + + @Override + public Feign.Builder invocationHandlerFactory(InvocationHandlerFactory invocationHandlerFactory) { + delegate.invocationHandlerFactory(invocationHandlerFactory); + return this; + } + + @Override + public Feign.Builder doNotCloseAfterDecode() { + delegate.doNotCloseAfterDecode(); + return this; + } + + @Override + public T target(Class apiType, String url) { + return delegate.target(apiType, url); + } + + @Override + public T target(Target target) { + return delegate.target(target); + } + + @Override + public Feign build() { + return delegate.build(); + } + } + } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/rest/feign/RestMetadataResolver.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/rest/feign/FeignRestMetadataResolver.java similarity index 67% rename from spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/rest/feign/RestMetadataResolver.java rename to spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/rest/feign/FeignRestMetadataResolver.java index 994d0a98..e2588c50 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/rest/feign/RestMetadataResolver.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/rest/feign/FeignRestMetadataResolver.java @@ -26,10 +26,12 @@ import feign.Request; import feign.RequestTemplate; import feign.jaxrs2.JAXRS2Contract; import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistry; import org.springframework.cloud.alibaba.dubbo.rest.metadata.MethodRestMetadata; import org.springframework.cloud.alibaba.dubbo.rest.metadata.ServiceRestMetadata; @@ -44,18 +46,29 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** - * The JSON resolver for {@link MethodMetadata} + * The REST metadata resolver for Feign + * + * @author Mercy */ -public class RestMetadataResolver implements BeanClassLoaderAware, SmartInitializingSingleton { +public class FeignRestMetadataResolver implements BeanClassLoaderAware, SmartInitializingSingleton { private static final String METHOD_PROPERTY_NAME = "method"; private static final String URL_PROPERTY_NAME = "url"; private static final String HEADERS_PROPERTY_NAME = "headers"; + private static final String[] CONTRACT_CLASS_NAMES = { + "feign.jaxrs2.JAXRS2Contract", + "org.springframework.cloud.openfeign.support.SpringMvcContract", + }; + private final ObjectMapper objectMapper; + private final ObjectProvider contract; + /** * Feign Contracts */ @@ -63,37 +76,44 @@ public class RestMetadataResolver implements BeanClassLoaderAware, SmartInitiali private ClassLoader classLoader; - @Autowired - private ObjectProvider contractObjectProvider; + @Value("${spring.application.name}") + private String currentApplicationName; - public RestMetadataResolver(ObjectMapper objectMapper) { - this.objectMapper = objectMapper; + public FeignRestMetadataResolver(ObjectProvider objectMapper, ObjectProvider contract) { + this.objectMapper = objectMapper.getIfAvailable(); + this.contract = contract; } @Override public void afterSingletonsInstantiated() { - Collection contracts = new LinkedList<>(); + LinkedList contracts = new LinkedList<>(); - // Add injected Contract , for example SpringMvcContract Bean under Spring Cloud Open Feign - Contract contract = contractObjectProvider.getIfAvailable(); - if (contract != null) { - contracts.add(contract); - } else { - if (ClassUtils.isPresent("org.springframework.cloud.openfeign.support.SpringMvcContract", classLoader)) { - contracts.add(new SpringMvcContract()); - } - } + // Add injected Contract if available, for example SpringMvcContract Bean under Spring Cloud Open Feign + contract.ifAvailable(contracts::add); - // Add JAXRS2Contract if it's present in Class Path - if (ClassUtils.isPresent("javax.ws.rs.Path", classLoader)) { - contracts.add(new JAXRS2Contract()); - } + Stream.of(CONTRACT_CLASS_NAMES) + .filter(this::isClassPresent) // filter the existed classes + .map(this::loadContractClass) // load Contract Class + .map(this::createContract) // create instance by the specified class + .forEach(contracts::add); // add the Contract instance into contracts this.contracts = Collections.unmodifiableCollection(contracts); } - public Set resolve(ServiceBean serviceBean) { + private Contract createContract(Class contractClassName) { + return (Contract) BeanUtils.instantiateClass(contractClassName); + } + + private Class loadContractClass(String contractClassName) { + return ClassUtils.resolveClassName(contractClassName, classLoader); + } + + private boolean isClassPresent(String className) { + return ClassUtils.isPresent(className, classLoader); + } + + public Set resolveServiceRestMetadata(ServiceBean serviceBean) { Object bean = serviceBean.getRef(); @@ -103,14 +123,7 @@ public class RestMetadataResolver implements BeanClassLoaderAware, SmartInitiali Set serviceRestMetadata = new LinkedHashSet<>(); - Set methodRestMetadata = new LinkedHashSet<>(); - - contracts.stream() - .map(contract -> contract.parseAndValidatateMetadata(bean.getClass())) - .flatMap(v -> v.stream()) - .forEach(methodMetadata -> { - methodRestMetadata.add(resolveMethodRestMetadata(methodMetadata, beanType, interfaceClass)); - }); + Set methodRestMetadata = resolveMethodRestMetadata(beanType, interfaceClass); List urls = serviceBean.getExportedUrls(); @@ -126,6 +139,18 @@ public class RestMetadataResolver implements BeanClassLoaderAware, SmartInitiali return serviceRestMetadata; } + public Set resolveMethodRestMetadata(Class targetClass) { + return resolveMethodRestMetadata(targetClass, targetClass); + } + + protected Set resolveMethodRestMetadata(Class targetClass, Class revisedClass) { + return contracts.stream() + .map(contract -> contract.parseAndValidatateMetadata(targetClass)) + .flatMap(v -> v.stream()) + .map(methodMetadata -> resolveMethodRestMetadata(methodMetadata, targetClass, revisedClass)) + .collect(Collectors.toSet()); + } + private String toJson(Object object) { String jsonContent = null; try { @@ -137,6 +162,9 @@ public class RestMetadataResolver implements BeanClassLoaderAware, SmartInitiali } private String regenerateConfigKey(String configKey, Class beanType, Class interfaceClass) { + if (beanType.equals(interfaceClass)) { + return configKey; + } return StringUtils.replace(configKey, beanType.getSimpleName(), interfaceClass.getSimpleName()); } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/rest/feign/RestMetadataConfigService.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/rest/feign/RestMetadataConfigService.java index d66a6947..cc14191f 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/rest/feign/RestMetadataConfigService.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/rest/feign/RestMetadataConfigService.java @@ -27,6 +27,8 @@ import static com.alibaba.nacos.api.common.Constants.DEFAULT_GROUP; /** * Rest Metadata Config Service + * + * @author Mercy */ public class RestMetadataConfigService { @@ -45,7 +47,7 @@ public class RestMetadataConfigService { * TODO JavaDoc */ private static String getServiceRestMetadataDataId(String serviceName) { - return serviceName + "-rest-metadata.json"; + return "metadata:rest:" + serviceName + ".json"; } public void publishServiceRestMetadata(String serviceName, String restMetadataJSON) diff --git a/spring-cloud-alibaba-dubbo/src/test/resources/application.yaml b/spring-cloud-alibaba-dubbo/src/test/resources/application.yaml index 2dc92d59..afdfbfa7 100644 --- a/spring-cloud-alibaba-dubbo/src/test/resources/application.yaml +++ b/spring-cloud-alibaba-dubbo/src/test/resources/application.yaml @@ -10,4 +10,7 @@ dubbo: port: 9090 server: netty registry: - address: spring-cloud://nacos \ No newline at end of file + address: spring-cloud://nacos + +server: + port: 8080 \ No newline at end of file