一,SpringCloud 服务调用
Spring Cloud提供了两种方式进行服务调用,分别为RestTemplate方式和声明式Feign方式,两种代用方式在 SpringCloud:注册中心——Eureka中已经进行了演示;本篇文章参考Feign方式,整合RestTemplate实现自定义的声明式Feign调用
二,步骤解析
1,搭建简易声明式Feign客户端调动环境,并初步调通
2,重写@EnableFeignClients -> @EnableRestFeign,引用注册类为下面自定义注册类
3,重写@FeignClient -> @RestClient
4,重写注册类 -> RestFeignRegisters,通过JDK动态代理获取被@RestClient注解接口的代理对象,并通过BeanDeifinition注册到Spring Bean容器
5,重写JDK动态代理需要的InvocationHandler方法 -> MyInvocationHandler,拼接访问URL后,通过RestTemplate进行调用
三,调用流程
1,启动服务时:
@EnableRestFeign -> RestFeignRegisters :注册被@RestClient注解的接口代理对象到SpringBean容器中
2,服务调用时:
* 通过调用的接口路径@RequestMapping及参数@RequestParam拼接服务访问路径 http://{serviceName}/{uri}?{param}
* 拼接成功后,通过RestTemplate直接进行服务调用
四,服务端代码
* 接口 -> com-gupao-springcloud-selffeign-server-api
package com.gupao.self.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* @author pj_zhang
* @create 2019-01-24 0:38
**/
public interface ISelfFeignController {
@RequestMapping("/getMessage")
String getMessage(@RequestParam("message") String message);
}
* 实现类 -> com-guapo-springcloud-selffeign-server
package com.gupao.self.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @author pj_zhang
* @create 2019-01-24 0:30
**/
@RestController
public class SelfFeignController implements ISelfFeignController {
@Override
@RequestMapping("/getMessage")
public String getMessage(@RequestParam("message") String message) {
return "SelfFeignController.getMessage : " + message;
}
}
五,代码实现
1,搭建简易声明式Feign客户端调动环境,并初步调通
* 项目架构,具体方式参考博文SpringCloud:注册中心——Eureka
* 客户端自定义Feign结构
2,重写@EnableFeignClients -> @EnableRestFeign
package com.gupao.self.feign.annotation;
import com.gupao.self.feign.register.RestFeignRegisters;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
/**
* @author pj_zhang
* @create 2019-01-23 23:01
**/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({RestFeignRegisters.class})
public @interface EnableRestFeign {
/**
* 指定RestClient端口
* @return
*/
Class>[] clients() default {};
}
3,重写@FeignClient -> @RestClient
package com.gupao.self.feign.annotation;
import java.lang.annotation.*;
/**
* @author pj_zhang
* @create 2019-01-23 23:08
**/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RestClient {
String name() default "";
}
4,重写注册类 -> RestFeignRegisters,通过JDK动态代理获取被@RestClient注解接口的代理对象,并通过BeanDeifinition注册到Spring Bean容器
package com.gupao.self.feign.register;
import com.gupao.self.feign.annotation.EnableRestFeign;
import com.gupao.self.feign.annotation.RestClient;
import com.gupao.self.feign.handler.MyInvocationHandler;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.SingletonBeanRegistry;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.AnnotationMetadata;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.stream.Stream;
/**
* @author pj_zhang
* @create 2019-01-23 23:01
**/
public class RestFeignRegisters implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata,
BeanDefinitionRegistry registry) {
// 获取@EnableRestFeign模块引用的需要注册的Class类
Map attributes =
annotationMetadata.getAnnotationAttributes(EnableRestFeign.class.getName());
Class>[] classes = (Class>[]) attributes.get("clients");
// 筛选所有被Feign客户端注解的类, @RestFeign
Stream.of(classes)
// 过滤接口
.filter(Class::isInterface)
// 仅选择标注了@RestClient注解的接口
.filter(interfaceClass ->
AnnotationUtils.findAnnotation(interfaceClass, RestClient.class) != null)
// 过滤requestMapping方法
.forEach(restClientClass -> {
// 通过@RestClient源数据获取应用名称
RestClient restClient =
AnnotationUtils.findAnnotation(restClientClass, RestClient.class);
String serverName = restClient.name();
// @RestClient注解类进行动态代理
Object proxy = Proxy.newProxyInstance(registry.getClass().getClassLoader(),
new Class[]{restClientClass},
new MyInvocationHandler(serverName, beanFactory));
// 将@RestClient接口代理实现proxy注册为Bean
String beanName = "RestClient." + serverName;
// 通过SingletonBeanRegistry注册bean
// if (registry instanceof SingletonBeanRegistry) {
// SingletonBeanRegistry singletonBeanRegistry = (SingletonBeanRegistry) registry;
// singletonBeanRegistry.registerSingleton(serverName, proxy);
// }
// 通过BeanDefinition注册Bean
BeanDefinitionBuilder beanDefinitionBuilder =
BeanDefinitionBuilder.genericBeanDefinition(RestClientClassFactoryBean.class);
beanDefinitionBuilder.addConstructorArgValue(restClientClass);
beanDefinitionBuilder.addConstructorArgValue(proxy);
BeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition();
registry.registerBeanDefinition(beanName, beanDefinition);
});
}
/**
* 自定义FactoryBean类,
* 通过BeanDefinition方式注册类到Spring Bean容器中
*/
private static class RestClientClassFactoryBean implements FactoryBean {
private final Class> restClient;
private final Object proxy;
private RestClientClassFactoryBean(Class> restClient, Object proxy) {
this.restClient = restClient;
this.proxy = proxy;
}
@Override
public Object getObject() throws Exception {
return proxy;
}
@Override
public Class> getObjectType() {
return restClient;
}
}
/**
* 通过集成BeanFactoryWare获取Spring Bean容器
* 在通过RestTemplate进行方法调用时, 获取RestTemplate
* @param beanFactory
* @throws BeansException
*/
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
5,重写JDK动态代理需要的InvocationHandler方法 -> MyInvocationHandler,拼接访问URL后,通过RestTemplate进行调用
package com.gupao.self.feign.handler;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.client.RestTemplate;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @author pj_zhang
* @create 2019-01-23 23:26
**/
public class MyInvocationHandler implements InvocationHandler {
// ServiceName
private final String serviceName;
private final BeanFactory beanFactory;
public MyInvocationHandler(String serviceName, BeanFactory beanFactory) {
this.serviceName = serviceName;
this.beanFactory = beanFactory;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 通过RestTemplate进行调用, 进行拼接访问路径, 访问路径参数如下
// http://servierName/uri?param
// 此处需要获取三个参数, 分别为
// serviceName 从register获取
// url 从方法注解获取
RequestMapping requestMapping = AnnotationUtils.getAnnotation(method, RequestMapping.class);
String[] uri = requestMapping.value();
// param
// 获取方法参数数量
int count = method.getParameterCount();
// 获取方法参数类型集合
Class>[] parameterTypes = method.getParameterTypes();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < count; i ++) {
// 获取方法第i个参数注解
Annotation[] annotations = method.getParameterAnnotations()[i];
// 遍历注解, 获取参数名称
String paramName = null;
for (Annotation annotation : annotations) {
if (annotation instanceof RequestParam) {
paramName = ((RequestParam) annotation).value();
break;
}
}
String paramValue = String.valueOf(args[i]);
// 拼接参数
sb.append("&").append(paramName).append("=").append(paramValue);
}
// 拼接完整URL
// http://servierName/uri?param
StringBuilder acturalUrl = new StringBuilder();
acturalUrl.append("http://").append(serviceName)
.append("/").append(uri[0])
.append("?").append(sb.toString());
// 从BeanFactory容器中获取RestTemplate, 进行执行
RestTemplate restTemplate = beanFactory.getBean("restTemplate", RestTemplate.class);
return restTemplate.getForObject(acturalUrl.toString(), String.class);
}
}
6,feign接口 -> 注意@FeignClient注解替换为@RestFeign
package com.gupao.self.feign;
import com.gupao.self.controller.ISelfFeignController;
import com.gupao.self.feign.annotation.RestClient;
import org.springframework.cloud.openfeign.FeignClient;
/**
* @author pj_zhang
* @create 2019-01-24 0:42
**/
@RestClient(name = "server-feign")
public interface ISelfFeign extends ISelfFeignController {
}
7,启动类 -> 注意@EnableFeignClients替换为@EnableRestFeign
package com.gupao;
import com.gupao.self.feign.ISelfFeign;
import com.gupao.self.feign.annotation.EnableRestFeign;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
/**
* @author pj_zhang
* @create 2019-01-24 0:34
**/
@SpringBootApplication
//@EnableFeignClients
@EnableRestFeign(clients = ISelfFeign.class)
@EnableEurekaClient
public class SelfFeignClientApp {
public static void main(String[] args) {
SpringApplication.run(SelfFeignClientApp.class, args);
}
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
8,调用结果