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

@ -82,31 +82,42 @@
<artifactId>netty-all</artifactId>
</dependency>
<!-- REST -->
<!--<dependency>-->
<!--<groupId>org.jboss.resteasy</groupId>-->
<!--<artifactId>resteasy-jaxrs</artifactId>-->
<!--</dependency>-->
<!-- REST support dependencies -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
</dependency>
<!--<dependency>-->
<!--<groupId>org.jboss.resteasy</groupId>-->
<!--<artifactId>resteasy-client</artifactId>-->
<!--</dependency>-->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
</dependency>
<!--<dependency>-->
<!--<groupId>javax.validation</groupId>-->
<!--<artifactId>validation-api</artifactId>-->
<!--</dependency>-->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-netty4</artifactId>
</dependency>
<!--<dependency>-->
<!--<groupId>org.hibernate.validator</groupId>-->
<!--<artifactId>hibernate-validator</artifactId>-->
<!--</dependency>-->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<!--<dependency>-->
<!--<groupId>org.mortbay.jetty</groupId>-->
<!--<artifactId>jetty</artifactId>-->
<!--</dependency>-->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson-provider</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<!-- Feign for JAX-RS 2-->
<dependency>

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

View File

@ -16,14 +16,23 @@
*/
package org.springframework.cloud.alibaba.dubbo.bootstrap;
import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.spring.ReferenceBean;
import com.alibaba.dubbo.config.spring.ServiceBean;
import com.alibaba.dubbo.config.spring.context.event.ServiceBeanExportedEvent;
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.service.EchoService;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.event.EventListener;
import java.util.List;
/**
* Dubbo Spring Cloud Bootstrap
@ -35,6 +44,9 @@ public class DubboSpringCloudBootstrap {
@Reference(version = "1.0.0")
private EchoService echoService;
@Reference(version = "1.0.0")
private EchoService echoServiceForRest;
@Bean
public ApplicationRunner applicationRunner() {
return arguments -> {
@ -42,6 +54,28 @@ public class DubboSpringCloudBootstrap {
};
}
@Autowired
private ApplicationConfig applicationConfig;
@Autowired
private List<RegistryConfig> registries;
@EventListener(ServiceBeanExportedEvent.class)
public void onServiceBeanExportedEvent(ServiceBeanExportedEvent event) {
ServiceBean serviceBean = event.getServiceBean();
ReferenceBean referenceBean = new ReferenceBean();
referenceBean.setApplication(applicationConfig);
referenceBean.setRegistries(registries);
referenceBean.setInterface(serviceBean.getInterfaceClass());
referenceBean.setInterface(serviceBean.getInterface());
referenceBean.setVersion(serviceBean.getVersion());
referenceBean.setGroup(serviceBean.getGroup());
Object object = referenceBean.get();
System.out.println(object);
}
public static void main(String[] args) {
new SpringApplicationBuilder(DubboSpringCloudBootstrap.class)
.run(args);

View File

@ -17,23 +17,48 @@
package org.springframework.cloud.alibaba.dubbo.service;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.dubbo.rpc.RpcContext;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
/**
* Default {@link EchoService}
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@Service(version = "1.0.0")
@Service(version = "1.0.0", protocol = {"dubbo", "rest"})
@RestController
@Path("/")
public class DefaultEchoService implements EchoService {
@Override
@GetMapping("/echo")
public String echo(@RequestParam String message) {
return "[echo] : " + message;
@GetMapping(value = "/echo",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@Path("/echo")
@GET
@Consumes("application/json")
@Produces("application/json;charset=UTF-8")
public String echo(@RequestParam @QueryParam("message") String message) {
return RpcContext.getContext().getUrl() + " [echo] : " + message;
}
@Override
@PostMapping("/plus")
@Path("/plus")
@POST
public String plus(@RequestParam @QueryParam("a") int a, @RequestParam @QueryParam("b") int b) {
return null;
}
}

View File

@ -24,4 +24,7 @@ package org.springframework.cloud.alibaba.dubbo.service;
public interface EchoService {
String echo(String message);
String plus(int a, int b);
}

View File

@ -1,9 +1,13 @@
dubbo:
scan:
base-packages: org.springframework.cloud.alibaba.dubbo.service
protocol:
port: 12345
protocols:
dubbo:
name: dubbo
port: 12345
rest:
name: rest
port: 9090
server: netty
registry:
address: spring-cloud://dummy