mirror of
https://gitee.com/mirrors/Spring-Cloud-Alibaba.git
synced 2021-06-26 13:25:11 +08:00
Polish spring-cloud-incubator/spring-cloud-alibaba#348 : [Feature] Add @DubboTransported Annotation
This commit is contained in:
parent
e7b4a08f44
commit
0a4cd85255
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.alibaba.dubbo.annotation;
|
||||
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* {@link DubboTransported @DubboTransported} annotation indicates that the traditional Spring Cloud Service-to-Service call is transported
|
||||
* by Dubbo under the hood, there are two main scenarios:
|
||||
* <ol>
|
||||
* <li>{@link FeignClient @FeignClient} annotated classes:
|
||||
* <ul>
|
||||
* If {@link DubboTransported @DubboTransported} annotated classes, the invocation of all methods of
|
||||
* {@link FeignClient @FeignClient} annotated classes.
|
||||
* </ul>
|
||||
* <ul>
|
||||
* If {@link DubboTransported @DubboTransported} annotated methods of {@link FeignClient @FeignClient} annotated classes.
|
||||
* </ul>
|
||||
* </li>
|
||||
* <li>{@link LoadBalanced @LoadBalanced} {@link RestTemplate} annotated field, method and parameters</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
*
|
||||
* @see FeignClient
|
||||
* @see LoadBalanced
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(value = {ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
|
||||
@Documented
|
||||
public @interface DubboTransported {
|
||||
|
||||
/**
|
||||
* The protocol of Dubbo transport whose value could be used the placeholder "dubbo.transport.protocol"
|
||||
*
|
||||
* @return the default protocol is "dubbo"
|
||||
*/
|
||||
String protocol() default "${dubbo.transport.protocol:dubbo}";
|
||||
|
||||
/**
|
||||
* The cluster of Dubbo transport whose value could be used the placeholder "dubbo.transport.cluster"
|
||||
*
|
||||
* @return the default protocol is "failover"
|
||||
*/
|
||||
String cluster() default "${dubbo.transport.cluster:failover}";
|
||||
}
|
@ -23,13 +23,14 @@ import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.resolver.FeignMetadataResolver;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.resolver.DubboServiceBeanMetadataResolver;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.resolver.MetadataResolver;
|
||||
import org.springframework.cloud.alibaba.dubbo.openfeign.DubboFeignClientsConfiguration;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.alibaba.dubbo.openfeign.TargeterBeanPostProcessor;
|
||||
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
|
||||
/**
|
||||
@ -39,7 +40,6 @@ import org.springframework.context.annotation.Configuration;
|
||||
*/
|
||||
@ConditionalOnClass(value = Feign.class)
|
||||
@AutoConfigureAfter(FeignAutoConfiguration.class)
|
||||
@EnableFeignClients(defaultConfiguration = DubboFeignClientsConfiguration.class)
|
||||
@Configuration
|
||||
public class DubboOpenFeignAutoConfiguration {
|
||||
|
||||
@ -49,6 +49,13 @@ public class DubboOpenFeignAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public MetadataResolver metadataJsonResolver(ObjectProvider<Contract> contract) {
|
||||
return new FeignMetadataResolver(currentApplicationName, contract);
|
||||
return new DubboServiceBeanMetadataResolver(currentApplicationName, contract);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TargeterBeanPostProcessor targeterBeanPostProcessor(Environment environment,
|
||||
DubboServiceMetadataRepository dubboServiceMetadataRepository) {
|
||||
return new TargeterBeanPostProcessor(environment, dubboServiceMetadataRepository);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.alibaba.dubbo.metadata;
|
||||
|
||||
import org.springframework.cloud.alibaba.dubbo.annotation.DubboTransported;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* {@link MethodMetadata} annotated {@link DubboTransported @DubboTransported}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class DubboTransportedMethodMetadata extends MethodMetadata {
|
||||
|
||||
private String protocol;
|
||||
|
||||
private String cluster;
|
||||
|
||||
public DubboTransportedMethodMetadata(Method method) {
|
||||
super(method);
|
||||
}
|
||||
|
||||
public String getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
public void setProtocol(String protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public String getCluster() {
|
||||
return cluster;
|
||||
}
|
||||
|
||||
public void setCluster(String cluster) {
|
||||
this.cluster = cluster;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof DubboTransportedMethodMetadata)) return false;
|
||||
if (!super.equals(o)) return false;
|
||||
|
||||
DubboTransportedMethodMetadata that = (DubboTransportedMethodMetadata) o;
|
||||
|
||||
if (protocol != null ? !protocol.equals(that.protocol) : that.protocol != null) return false;
|
||||
return cluster != null ? cluster.equals(that.cluster) : that.cluster == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = super.hashCode();
|
||||
result = 31 * result + (protocol != null ? protocol.hashCode() : 0);
|
||||
result = 31 * result + (cluster != null ? cluster.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -18,8 +18,8 @@ package org.springframework.cloud.alibaba.dubbo.metadata.repository;
|
||||
|
||||
import com.alibaba.dubbo.config.spring.ReferenceBean;
|
||||
import com.alibaba.dubbo.rpc.service.GenericService;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
|
||||
@ -46,24 +46,18 @@ public class DubboServiceMetadataRepository {
|
||||
|
||||
/**
|
||||
* Key is application name
|
||||
* Value is Map<RequestMetadata, GenericService>
|
||||
* Value is Map<RequestMetadata, ReferenceBean<GenericService>>
|
||||
*/
|
||||
private Map<String, Map<RequestMetadata, GenericService>> genericServicesRepository = new HashMap<>();
|
||||
private Map<String, Map<RequestMetadata, ReferenceBean<GenericService>>> referenceBeansRepository = new HashMap<>();
|
||||
|
||||
private Map<String, Map<RequestMetadata, MethodMetadata>> methodMetadataRepository = new HashMap<>();
|
||||
|
||||
@Autowired
|
||||
private MetadataConfigService metadataConfigService;
|
||||
|
||||
@Value("${dubbo.target.protocol:dubbo}")
|
||||
private String targetProtocol;
|
||||
|
||||
@Value("${dubbo.target.cluster:failover}")
|
||||
private String targetCluster;
|
||||
|
||||
public void updateMetadata(String serviceName) {
|
||||
|
||||
Map<RequestMetadata, GenericService> genericServicesMap = genericServicesRepository.computeIfAbsent(serviceName, k -> new HashMap<>());
|
||||
Map<RequestMetadata, ReferenceBean<GenericService>> genericServicesMap = referenceBeansRepository.computeIfAbsent(serviceName, k -> new HashMap<>());
|
||||
|
||||
Map<RequestMetadata, MethodMetadata> methodMetadataMap = methodMetadataRepository.computeIfAbsent(serviceName, k -> new HashMap<>());
|
||||
|
||||
@ -75,14 +69,14 @@ public class DubboServiceMetadataRepository {
|
||||
|
||||
serviceRestMetadata.getMeta().forEach(restMethodMetadata -> {
|
||||
RequestMetadata requestMetadata = restMethodMetadata.getRequest();
|
||||
genericServicesMap.put(requestMetadata, referenceBean.get());
|
||||
genericServicesMap.put(requestMetadata, referenceBean);
|
||||
methodMetadataMap.put(requestMetadata, restMethodMetadata.getMethod());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public GenericService getGenericService(String serviceName, RequestMetadata requestMetadata) {
|
||||
return getGenericServicesMap(serviceName).get(requestMetadata);
|
||||
public ReferenceBean<GenericService> getReferenceBean(String serviceName, RequestMetadata requestMetadata) {
|
||||
return getReferenceBeansMap(serviceName).get(requestMetadata);
|
||||
}
|
||||
|
||||
public MethodMetadata getMethodMetadata(String serviceName, RequestMetadata requestMetadata) {
|
||||
@ -101,18 +95,15 @@ public class DubboServiceMetadataRepository {
|
||||
referenceBean.setInterface(interfaceName);
|
||||
referenceBean.setVersion(version);
|
||||
referenceBean.setGroup(group);
|
||||
referenceBean.setProtocol(targetProtocol);
|
||||
referenceBean.setCluster(targetCluster);
|
||||
|
||||
return referenceBean;
|
||||
}
|
||||
|
||||
private Map<RequestMetadata, GenericService> getGenericServicesMap(String serviceName) {
|
||||
return genericServicesRepository.getOrDefault(serviceName, Collections.emptyMap());
|
||||
private Map<RequestMetadata, ReferenceBean<GenericService>> getReferenceBeansMap(String serviceName) {
|
||||
return referenceBeansRepository.getOrDefault(serviceName, Collections.emptyMap());
|
||||
}
|
||||
|
||||
private Map<RequestMetadata, MethodMetadata> getMethodMetadataMap(String serviceName) {
|
||||
return methodMetadataRepository.getOrDefault(serviceName, Collections.emptyMap());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ package org.springframework.cloud.alibaba.dubbo.metadata.resolver;
|
||||
|
||||
import com.alibaba.dubbo.common.URL;
|
||||
import com.alibaba.dubbo.config.spring.ServiceBean;
|
||||
|
||||
import feign.Contract;
|
||||
import feign.Feign;
|
||||
import feign.MethodMetadata;
|
||||
@ -44,11 +45,12 @@ import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* The metadata resolver for {@link Feign}
|
||||
* The metadata resolver for {@link Feign} for {@link ServiceBean Dubbo Service Bean} in the provider side.
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class FeignMetadataResolver implements BeanClassLoaderAware, SmartInitializingSingleton, MetadataResolver {
|
||||
public class DubboServiceBeanMetadataResolver implements BeanClassLoaderAware, SmartInitializingSingleton,
|
||||
MetadataResolver {
|
||||
|
||||
private static final String[] CONTRACT_CLASS_NAMES = {
|
||||
"feign.jaxrs2.JAXRS2Contract",
|
||||
@ -66,7 +68,7 @@ public class FeignMetadataResolver implements BeanClassLoaderAware, SmartInitial
|
||||
*/
|
||||
private Collection<Contract> contracts;
|
||||
|
||||
public FeignMetadataResolver(String currentApplicationName, ObjectProvider<Contract> contract) {
|
||||
public DubboServiceBeanMetadataResolver(String currentApplicationName, ObjectProvider<Contract> contract) {
|
||||
this.currentApplicationName = currentApplicationName;
|
||||
this.contract = contract;
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.alibaba.dubbo.metadata.resolver;
|
||||
|
||||
import feign.Contract;
|
||||
import org.springframework.cloud.alibaba.dubbo.annotation.DubboTransported;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.DubboTransportedMethodMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.env.PropertyResolver;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static feign.Feign.configKey;
|
||||
|
||||
/**
|
||||
* {@link MethodMetadata} Resolver for the {@link DubboTransported} annotated classes or methods in client side.
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
* @see DubboTransportedMethodMetadata
|
||||
*/
|
||||
public class DubboTransportedMethodMetadataResolver {
|
||||
|
||||
private static final Class<DubboTransported> DUBBO_TRANSPORTED_CLASS = DubboTransported.class;
|
||||
|
||||
private final PropertyResolver propertyResolver;
|
||||
|
||||
private final Contract contract;
|
||||
|
||||
public DubboTransportedMethodMetadataResolver(PropertyResolver propertyResolver, Contract contract) {
|
||||
this.propertyResolver = propertyResolver;
|
||||
this.contract = contract;
|
||||
}
|
||||
|
||||
public Map<DubboTransportedMethodMetadata, RequestMetadata> resolve(Class<?> targetType) {
|
||||
Set<DubboTransportedMethodMetadata> dubboTransportedMethodMetadataSet =
|
||||
resolveDubboTransportedMethodMetadataSet(targetType);
|
||||
Map<String, RequestMetadata> requestMetadataMap = resolveRequestMetadataMap(targetType);
|
||||
return dubboTransportedMethodMetadataSet
|
||||
.stream()
|
||||
.collect(Collectors.toMap(methodMetadata -> methodMetadata, methodMetadata ->
|
||||
requestMetadataMap.get(configKey(targetType, methodMetadata.getMethod()))
|
||||
));
|
||||
}
|
||||
|
||||
protected Set<DubboTransportedMethodMetadata> resolveDubboTransportedMethodMetadataSet(Class<?> targetType) {
|
||||
// The public methods of target interface
|
||||
Method[] methods = targetType.getMethods();
|
||||
|
||||
Set<DubboTransportedMethodMetadata> methodMetadataSet = new LinkedHashSet<>();
|
||||
|
||||
for (Method method : methods) {
|
||||
DubboTransported dubboTransported = resolveDubboTransported(method);
|
||||
if (dubboTransported != null) {
|
||||
DubboTransportedMethodMetadata methodMetadata = createDubboTransportedMethodMetadata(method, dubboTransported);
|
||||
methodMetadataSet.add(methodMetadata);
|
||||
}
|
||||
}
|
||||
return methodMetadataSet;
|
||||
}
|
||||
|
||||
|
||||
private Map<String, RequestMetadata> resolveRequestMetadataMap(Class<?> targetType) {
|
||||
return contract.parseAndValidatateMetadata(targetType)
|
||||
.stream().collect(Collectors.toMap(feign.MethodMetadata::configKey, this::requestMetadata));
|
||||
}
|
||||
|
||||
private RequestMetadata requestMetadata(feign.MethodMetadata methodMetadata) {
|
||||
return new RequestMetadata(methodMetadata.template());
|
||||
}
|
||||
|
||||
private DubboTransportedMethodMetadata createDubboTransportedMethodMetadata(Method method,
|
||||
DubboTransported dubboTransported) {
|
||||
DubboTransportedMethodMetadata methodMetadata = new DubboTransportedMethodMetadata(method);
|
||||
String protocol = propertyResolver.resolvePlaceholders(dubboTransported.protocol());
|
||||
String cluster = propertyResolver.resolvePlaceholders(dubboTransported.cluster());
|
||||
methodMetadata.setProtocol(protocol);
|
||||
methodMetadata.setCluster(cluster);
|
||||
return methodMetadata;
|
||||
}
|
||||
|
||||
private DubboTransported resolveDubboTransported(Method method) {
|
||||
DubboTransported dubboTransported = AnnotationUtils.findAnnotation(method, DUBBO_TRANSPORTED_CLASS);
|
||||
if (dubboTransported == null) { // Attempt to find @DubboTransported in the declaring class
|
||||
Class<?> declaringClass = method.getDeclaringClass();
|
||||
dubboTransported = AnnotationUtils.findAnnotation(declaringClass, DUBBO_TRANSPORTED_CLASS);
|
||||
}
|
||||
return dubboTransported;
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.alibaba.dubbo.openfeign;
|
||||
|
||||
import feign.Contract;
|
||||
import feign.Feign;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboOpenFeignAutoConfiguration;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.cloud.openfeign.FeignClientsConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Dubbo {@link Configuration} for {@link FeignClient FeignClients}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
* @see DubboOpenFeignAutoConfiguration
|
||||
* @see org.springframework.cloud.openfeign.FeignContext#setConfigurations(List)
|
||||
* @see FeignClientsConfiguration
|
||||
*/
|
||||
@Configuration
|
||||
public class DubboFeignClientsConfiguration {
|
||||
|
||||
@Autowired
|
||||
private Contract contract;
|
||||
|
||||
@Autowired
|
||||
private DubboServiceMetadataRepository dubboServiceRepository;
|
||||
|
||||
@Bean
|
||||
public BeanPostProcessor beanPostProcessor() {
|
||||
return new BeanPostProcessor() {
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof Feign.Builder) {
|
||||
Feign.Builder builder = (Feign.Builder) bean;
|
||||
builder.invocationHandlerFactory(new DubboInvocationHandlerFactory(contract, dubboServiceRepository));
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -16,7 +16,9 @@
|
||||
*/
|
||||
package org.springframework.cloud.alibaba.dubbo.openfeign;
|
||||
|
||||
import com.alibaba.dubbo.config.spring.ReferenceBean;
|
||||
import com.alibaba.dubbo.rpc.service.GenericService;
|
||||
|
||||
import feign.Contract;
|
||||
import feign.InvocationHandlerFactory;
|
||||
import feign.MethodMetadata;
|
||||
@ -68,10 +70,10 @@ public class DubboInvocationHandlerFactory implements InvocationHandlerFactory {
|
||||
Map<Method, org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata> methodMetadataMap = new HashMap<>();
|
||||
|
||||
methodRequestMetadataMap.forEach((method, requestMetadata) -> {
|
||||
GenericService genericService = dubboServiceRepository.getGenericService(serviceName, requestMetadata);
|
||||
ReferenceBean<GenericService> referenceBean = dubboServiceRepository.getReferenceBean(serviceName, requestMetadata);
|
||||
org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata methodMetadata =
|
||||
dubboServiceRepository.getMethodMetadata(serviceName, requestMetadata);
|
||||
genericServicesMap.put(method, genericService);
|
||||
genericServicesMap.put(method, referenceBean.get());
|
||||
methodMetadataMap.put(method, methodMetadata);
|
||||
});
|
||||
|
||||
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.alibaba.dubbo.openfeign;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import static java.lang.reflect.Proxy.newProxyInstance;
|
||||
import static org.springframework.util.ClassUtils.getUserClass;
|
||||
import static org.springframework.util.ClassUtils.resolveClassName;
|
||||
|
||||
/**
|
||||
* org.springframework.cloud.openfeign.Targeter {@link BeanPostProcessor}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class TargeterBeanPostProcessor implements BeanPostProcessor, BeanClassLoaderAware {
|
||||
|
||||
private static final String TARGETER_CLASS_NAME = "org.springframework.cloud.openfeign.Targeter";
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
private final DubboServiceMetadataRepository dubboServiceMetadataRepository;
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
public TargeterBeanPostProcessor(Environment environment,
|
||||
DubboServiceMetadataRepository dubboServiceMetadataRepository) {
|
||||
this.environment = environment;
|
||||
this.dubboServiceMetadataRepository = dubboServiceMetadataRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
|
||||
Class<?> beanClass = getUserClass(bean.getClass());
|
||||
Class<?> targetClass = resolveClassName(TARGETER_CLASS_NAME, classLoader);
|
||||
if (targetClass.isAssignableFrom(beanClass)) {
|
||||
return newProxyInstance(classLoader, new Class[]{targetClass},
|
||||
new TargeterInvocationHandler(bean, environment, dubboServiceMetadataRepository));
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
}
|
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.alibaba.dubbo.openfeign;
|
||||
|
||||
|
||||
import com.alibaba.dubbo.config.spring.ReferenceBean;
|
||||
import com.alibaba.dubbo.rpc.service.GenericService;
|
||||
|
||||
import feign.Contract;
|
||||
import feign.Target;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.DubboTransportedMethodMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.resolver.DubboTransportedMethodMetadataResolver;
|
||||
import org.springframework.cloud.openfeign.FeignContext;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.lang.reflect.Proxy.newProxyInstance;
|
||||
|
||||
/**
|
||||
* org.springframework.cloud.openfeign.Targeter {@link InvocationHandler}
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
class TargeterInvocationHandler implements InvocationHandler {
|
||||
|
||||
private final Object bean;
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
private final DubboServiceMetadataRepository dubboServiceMetadataRepository;
|
||||
|
||||
TargeterInvocationHandler(Object bean, Environment environment,
|
||||
DubboServiceMetadataRepository dubboServiceMetadataRepository) {
|
||||
this.bean = bean;
|
||||
this.environment = environment;
|
||||
this.dubboServiceMetadataRepository = dubboServiceMetadataRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
/**
|
||||
* args[0]: FeignClientFactoryBean factory
|
||||
* args[1]: Feign.Builder feign
|
||||
* args[2]: FeignContext context
|
||||
* args[3]: Target.HardCodedTarget<T> target
|
||||
*/
|
||||
FeignContext feignContext = cast(args[2]);
|
||||
Target.HardCodedTarget<?> target = cast(args[3]);
|
||||
|
||||
// Execute Targeter#target method first
|
||||
method.setAccessible(true);
|
||||
// Get the default proxy object
|
||||
Object defaultProxy = method.invoke(bean, args);
|
||||
// Create Dubbo Proxy if required
|
||||
return createDubboProxyIfRequired(feignContext, target, defaultProxy);
|
||||
}
|
||||
|
||||
private Object createDubboProxyIfRequired(FeignContext feignContext, Target target, Object defaultProxy) {
|
||||
|
||||
DubboInvocationHandler dubboInvocationHandler = createDubboInvocationHandler(feignContext, target, defaultProxy);
|
||||
|
||||
if (dubboInvocationHandler == null) {
|
||||
return defaultProxy;
|
||||
}
|
||||
|
||||
return newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, dubboInvocationHandler);
|
||||
}
|
||||
|
||||
private DubboInvocationHandler createDubboInvocationHandler(FeignContext feignContext, Target target,
|
||||
Object defaultFeignClientProxy) {
|
||||
|
||||
// Service name equals @FeignClient.name()
|
||||
String serviceName = target.name();
|
||||
Class<?> targetType = target.type();
|
||||
|
||||
// Get Contract Bean from FeignContext
|
||||
Contract contract = feignContext.getInstance(serviceName, Contract.class);
|
||||
|
||||
DubboTransportedMethodMetadataResolver resolver =
|
||||
new DubboTransportedMethodMetadataResolver(environment, contract);
|
||||
|
||||
Map<DubboTransportedMethodMetadata, RequestMetadata> methodRequestMetadataMap = resolver.resolve(targetType);
|
||||
|
||||
if (methodRequestMetadataMap.isEmpty()) { // @DubboTransported method was not found
|
||||
return null;
|
||||
}
|
||||
|
||||
// Update Metadata
|
||||
dubboServiceMetadataRepository.updateMetadata(serviceName);
|
||||
|
||||
Map<Method, org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata> methodMetadataMap = new HashMap<>();
|
||||
|
||||
Map<Method, GenericService> genericServicesMap = new HashMap<>();
|
||||
|
||||
methodRequestMetadataMap.forEach((dubboTransportedMethodMetadata, requestMetadata) -> {
|
||||
ReferenceBean<GenericService> referenceBean = dubboServiceMetadataRepository.getReferenceBean(serviceName, requestMetadata);
|
||||
org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata methodMetadata =
|
||||
dubboServiceMetadataRepository.getMethodMetadata(serviceName, requestMetadata);
|
||||
referenceBean.setProtocol(dubboTransportedMethodMetadata.getProtocol());
|
||||
referenceBean.setCluster(dubboTransportedMethodMetadata.getCluster());
|
||||
genericServicesMap.put(dubboTransportedMethodMetadata.getMethod(), referenceBean.get());
|
||||
methodMetadataMap.put(dubboTransportedMethodMetadata.getMethod(), methodMetadata);
|
||||
});
|
||||
|
||||
InvocationHandler defaultFeignClientInvocationHandler = Proxy.getInvocationHandler(defaultFeignClientProxy);
|
||||
|
||||
DubboInvocationHandler dubboInvocationHandler = new DubboInvocationHandler(genericServicesMap, methodMetadataMap,
|
||||
defaultFeignClientInvocationHandler);
|
||||
|
||||
return dubboInvocationHandler;
|
||||
}
|
||||
|
||||
private static <T> T cast(Object object) {
|
||||
return (T) object;
|
||||
}
|
||||
}
|
@ -17,10 +17,12 @@
|
||||
package org.springframework.cloud.alibaba.dubbo.bootstrap;
|
||||
|
||||
import com.alibaba.dubbo.config.annotation.Reference;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.alibaba.dubbo.annotation.DubboTransported;
|
||||
import org.springframework.cloud.alibaba.dubbo.service.EchoService;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
@ -47,16 +49,39 @@ public class DubboSpringCloudBootstrap {
|
||||
@Lazy
|
||||
private FeignEchoService feignEchoService;
|
||||
|
||||
@GetMapping(value = "/call/echo")
|
||||
public String echo(@RequestParam("message") String message) {
|
||||
@Autowired
|
||||
@Lazy
|
||||
private DubboFeignEchoService dubboFeignEchoService;
|
||||
|
||||
@GetMapping(value = "/dubbo/call/echo")
|
||||
public String dubboEcho(@RequestParam("message") String message) {
|
||||
return echoService.echo(message);
|
||||
}
|
||||
|
||||
@GetMapping(value = "/feign/call/echo")
|
||||
public String feignEcho(@RequestParam("message") String message) {
|
||||
return feignEchoService.echo(message);
|
||||
}
|
||||
|
||||
@GetMapping(value = "/feign-dubbo/call/echo")
|
||||
public String feignDubboEcho(@RequestParam("message") String message) {
|
||||
return dubboFeignEchoService.echo(message);
|
||||
}
|
||||
|
||||
@FeignClient("spring-cloud-alibaba-dubbo")
|
||||
public interface FeignEchoService {
|
||||
|
||||
@GetMapping(value = "/echo")
|
||||
String echo(@RequestParam("message") String message);
|
||||
|
||||
}
|
||||
|
||||
@FeignClient("spring-cloud-alibaba-dubbo")
|
||||
public interface DubboFeignEchoService {
|
||||
|
||||
@GetMapping(value = "/echo")
|
||||
@DubboTransported
|
||||
String echo(@RequestParam("message") String message);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ -66,6 +91,8 @@ public class DubboSpringCloudBootstrap {
|
||||
System.out.println(echoService.echo("mercyblitz"));
|
||||
// Spring Cloud Open Feign REST Call
|
||||
System.out.println(feignEchoService.echo("mercyblitz"));
|
||||
// Spring Cloud Open Feign REST Call (Dubbo Transported)
|
||||
System.out.println(dubboFeignEchoService.echo("mercyblitz"));
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.alibaba.dubbo.metadata.resolver;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.alibaba.dubbo.annotation.DubboTransported;
|
||||
import org.springframework.cloud.alibaba.dubbo.metadata.DubboTransportedMethodMetadata;
|
||||
import org.springframework.cloud.openfeign.support.SpringMvcContract;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* {@link DubboTransportedMethodMetadataResolver} Test
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public class DubboTransportedMethodMetadataResolverTest {
|
||||
|
||||
private DubboTransportedMethodMetadataResolver resolver;
|
||||
|
||||
private MockEnvironment environment;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
environment = new MockEnvironment();
|
||||
resolver = new DubboTransportedMethodMetadataResolver(environment, new SpringMvcContract());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolve() {
|
||||
Set<DubboTransportedMethodMetadata> metadataSet = resolver.resolveDubboTransportedMethodMetadataSet(TestDefaultService.class);
|
||||
Assert.assertEquals(1, metadataSet.size());
|
||||
}
|
||||
|
||||
|
||||
@DubboTransported
|
||||
interface TestDefaultService {
|
||||
|
||||
String test(String message);
|
||||
|
||||
}
|
||||
}
|
@ -12,5 +12,9 @@ dubbo:
|
||||
registry:
|
||||
address: spring-cloud://nacos
|
||||
|
||||
feign:
|
||||
hystrix:
|
||||
enabled: true
|
||||
|
||||
server:
|
||||
port: 8080
|
Loading…
x
Reference in New Issue
Block a user