1
0
mirror of https://gitee.com/mirrors/Spring-Cloud-Alibaba.git synced 2021-06-26 13:25:11 +08:00
mercyblitz
2019-01-11 20:15:19 +08:00
parent d40555eff4
commit 53b677f3b7
10 changed files with 402 additions and 33 deletions

View File

@@ -16,7 +16,18 @@
*/
package org.springframework.cloud.alibaba.dubbo.autoconfigure;
import com.fasterxml.jackson.databind.ObjectMapper;
import feign.Contract;
import feign.jaxrs2.JAXRS2Contract;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.alibaba.dubbo.rest.feign.RestMetadataResolver;
import org.springframework.cloud.openfeign.support.SpringMvcContract;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.ws.rs.Path;
/**
* Spring Boot Auto-Configuration class for Dubbo REST
@@ -26,5 +37,32 @@ import org.springframework.context.annotation.Configuration;
@Configuration
public class DubboRestAutoConfiguration {
/**
* A Feign Contract bean for JAX-RS if available
*/
@ConditionalOnClass(Path.class)
@Bean
public Contract jaxrs2Contract() {
return new JAXRS2Contract();
}
}
@Bean
@ConditionalOnMissingBean
public ObjectMapper objectMapper() {
return new ObjectMapper();
}
/**
* A Feign Contract bean for Spring MVC if available
*/
@ConditionalOnClass(RequestMapping.class)
@Bean
public Contract springMvcContract() {
return new SpringMvcContract();
}
@Bean
public RestMetadataResolver metadataJsonResolver(ObjectMapper objectMapper) {
return new RestMetadataResolver(objectMapper);
}
}

View File

@@ -0,0 +1,72 @@
/*
* 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.autoconfigure;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.context.named.NamedContextFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import java.util.Map;
/**
* The Auto-Configuration class for Dubbo REST Discovery
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@Configuration
@AutoConfigureAfter(value = {
DubboRestAutoConfiguration.class,
DubboRestMetadataRegistrationAutoConfiguration.class})
public class DubboRestDiscoveryAutoConfiguration {
@Autowired
private DiscoveryClient discoveryClient;
// 1. Get all service names from Spring beans that was annotated by @FeignClient
// 2. Get all service instances by echo specified service name
// 3. Get Rest metadata from service instance
// 4. Resolve REST metadata from the @FeignClient instance
@Bean
public SmartInitializingSingleton onBeansInitialized(ListableBeanFactory beanFactory) {
return () -> {
Map<String, Object> feignClientBeans = beanFactory.getBeansWithAnnotation(FeignClient.class);
feignClientBeans.forEach((beanName, bean) -> {
if (bean instanceof NamedContextFactory.Specification) {
NamedContextFactory.Specification specification = (NamedContextFactory.Specification) bean;
String serviceName = specification.getName();
}
});
};
}
@EventListener(ContextRefreshedEvent.class)
public void onContextRefreshed(ContextRefreshedEvent event) {
}
}

View File

@@ -0,0 +1,108 @@
/*
* 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.autoconfigure;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.config.spring.ServiceBean;
import com.alibaba.dubbo.config.spring.context.event.ServiceBeanExportedEvent;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import feign.Contract;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.cloud.alibaba.dubbo.rest.feign.RestMetadataResolver;
import org.springframework.cloud.client.discovery.event.InstancePreRegisteredEvent;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistry.getServiceName;
/**
* The Auto-Configuration class for Dubbo REST metadata registration,
* REST metadata that is a part of {@link Registration#getMetadata() Spring Cloud service instances' metadata}
* will be registered Spring Cloud registry.
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@Configuration
@AutoConfigureAfter(value = {
DubboRestAutoConfiguration.class, DubboServiceRegistrationAutoConfiguration.class})
public class DubboRestMetadataRegistrationAutoConfiguration {
/**
* A Map to store REST metadata temporary, its' key is the special service name for a Dubbo service,
* the value is a JSON content of JAX-RS or Spring MVC REST metadata from the annotated methods.
*/
private final Map<String, Set<String>> restMetadata = new LinkedHashMap<>();
/**
* Autowire Feign Contract Beans
*/
@Autowired(required = false)
private Collection<Contract> contracts = Collections.emptyList();
@Autowired
private ObjectMapper objectMapper;
@Autowired
private RestMetadataResolver restMetadataResolver;
@EventListener(ServiceBeanExportedEvent.class)
public void recordRestMetadata(ServiceBeanExportedEvent event) {
ServiceBean serviceBean = event.getServiceBean();
List<URL> urls = serviceBean.getExportedUrls();
Object bean = serviceBean.getRef();
Set<String> metadata = contracts.stream()
.map(contract -> contract.parseAndValidatateMetadata(bean.getClass()))
.flatMap(v -> v.stream())
.map(restMetadataResolver::resolve)
.collect(Collectors.toSet());
urls.forEach(url -> {
String serviceName = getServiceName(url);
restMetadata.put(serviceName, metadata);
});
}
/**
* Pre-handle Spring Cloud application service registered:
* <p>
* Put <code>restMetadata</code> with the JSON format into
* {@link Registration#getMetadata() service instances' metadata}
* <p>
*
* @param event {@link InstancePreRegisteredEvent} instance
*/
@EventListener(InstancePreRegisteredEvent.class)
public void registerRestMetadata(InstancePreRegisteredEvent event) throws JsonProcessingException {
Registration registration = event.getRegistration();
Map<String, String> serviceInstanceMetadata = registration.getMetadata();
String restMetadataJson = objectMapper.writeValueAsString(restMetadata);
serviceInstanceMetadata.put("restMetadata", restMetadataJson);
}
}

View File

@@ -160,12 +160,12 @@ public class SpringCloudRegistry extends FailbackRegistry {
return serviceInstance;
}
private String getServiceName(URL url) {
public static String getServiceName(URL url) {
String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
return getServiceName(url, category);
}
private String getServiceName(URL url, String category) {
private static String getServiceName(URL url, String category) {
StringBuilder serviceNameBuilder = new StringBuilder(category);
appendIfPresent(serviceNameBuilder, url, Constants.INTERFACE_KEY);
appendIfPresent(serviceNameBuilder, url, Constants.VERSION_KEY);
@@ -173,7 +173,7 @@ public class SpringCloudRegistry extends FailbackRegistry {
return serviceNameBuilder.toString();
}
private void appendIfPresent(StringBuilder target, URL url, String parameterName) {
private static void appendIfPresent(StringBuilder target, URL url, String parameterName) {
String parameterValue = url.getParameter(parameterName);
if (StringUtils.hasText(parameterValue)) {
target.append(SERVICE_NAME_SEPARATOR).append(parameterValue);

View File

@@ -0,0 +1,74 @@
/*
* 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.rest.feign;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import feign.MethodMetadata;
import feign.Request;
import feign.RequestTemplate;
import java.io.IOException;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* The JSON resolver for {@link MethodMetadata}
*/
public class RestMetadataResolver {
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 final ObjectMapper objectMapper;
public RestMetadataResolver(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
public String resolve(MethodMetadata methodMetadata) {
String jsonContent = null;
Map<String, Object> metadata = new LinkedHashMap<>();
RequestTemplate requestTemplate = methodMetadata.template();
Request request = requestTemplate.request();
metadata.put(METHOD_PROPERTY_NAME, request.method());
metadata.put(URL_PROPERTY_NAME, request.url());
metadata.put(HEADERS_PROPERTY_NAME, request.headers());
try {
jsonContent = objectMapper.writeValueAsString(metadata);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException(e);
}
return jsonContent;
}
public Request resolveRequest(String json) {
Request request = null;
try {
Map<String, Object> data = objectMapper.readValue(json, Map.class);
String method = (String) data.get(METHOD_PROPERTY_NAME);
String url = (String) data.get(URL_PROPERTY_NAME);
Map<String, Collection<String>> headers = (Map) data.get(HEADERS_PROPERTY_NAME);
request = Request.create(method, url, headers, null, null);
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
return request;
}
}