SpringCloud:服务调用——自定义Feign方式实现 - Go语言中文社区

SpringCloud:服务调用——自定义Feign方式实现


一,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<String, Object> 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,调用结果

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/u011976388/article/details/86603531
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2021-05-30 22:01:19
  • 阅读 ( 1215 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢