1
0
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:
mercyblitz 2019-02-13 15:21:14 +08:00
parent e7b4a08f44
commit 0a4cd85255
13 changed files with 578 additions and 95 deletions

View File

@ -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}";
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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());
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}
};
}
}

View File

@ -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);
});

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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"));
};
}

View File

@ -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);
}
}

View File

@ -12,5 +12,9 @@ dubbo:
registry:
address: spring-cloud://nacos
feign:
hystrix:
enabled: true
server:
port: 8080