技术背景
在开发过程中 ,常常会遇到RPC服务/远程服务调用,如服务A(feign-producer) 暴露出一个接口 DemoService ,
那么对于服务B(feign-consumer)来说 ,如何能远程调用服务A 方法?
解决方案
1 手写HttpUtil *** , B调用A (很原始/拉胯)
2 Feign工具(本文推荐)
前期技术准备(Jdk动态代理)(良辰建议先温习一遍)如下
问题: 假设我有一个学生操作类,如何动态增加审查日志
public interface StudentService {
public void add();
}
//TODO 如何增加审查日志 遵循开闭原则 对扩展开放 修改关闭
public class StudentServiceImpl implements StudentService {
@Override
public void add() {
System.out.println("正在添加学生...................");
}
}
复制代码
解决: 1.1 创建动态代理类,实现核心接口 InvocationHandler
public class DynamicStudentService implements InvocationHandler {
/**
* 要代理的真实对象
*/
private Object student;
public DynamicStudentService(Object student) {
this.student = student;
}
/**
* 动态代理上的所有方法应用
*
* @param proxy 代理类实例
* @param method 被调用的方法对象
* @param args 参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始操作");
Object returnValue = method.invoke(student, args);
System.out.println("结束操作");
return returnValue;
}
}
复制代码
1.2 创建代理对象/验证
public class TestStudent {
public static void main(String[] args) {
//配置系统属性为true,代理类生成时将自动写入磁盘
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//代理的真实对象
StudentService studentService = new StudentServiceImpl();
InvocationHandler invocationHandler = new DynamicStudentService(studentService);
ClassLoader classLoader = studentService.getClass().getClassLoader();
Class[] interfaces = studentService.getClass().getInterfaces();
/**
* 生成代理对象
* 核心参数
* 1 定义代理类的类加载器
* 2 代理类要实现的接口列表
* 3 将方法调用分派到具有指定调用处理程序的代理实例的调用处理程序
* 由指定的类加载器定义的代理类
* 并实现指定的接口
*
*/
StudentService service = (StudentService) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
service.add();
}
}
复制代码
Feign 源码解析
1.1 feign-consumer 程序启动时
Spring容器创建动态代理对象new ReflectiveFeign.FeignInvocationHandler
这一步相当于 Spring和Feign 相关远程调用服务的Service Bean 并将这些Bean进行了动态代理 / 并且初始化目标调用地址/见图
复制代码
1.2 当消费者服务访问远程DemoService时,Spring判断此Service Bean 被ReflectiveFeign.FeignInvocationHandler动态代理,并进入代理方法
复制代码
1.3 追踪代理类方法Invoke内部 取出远程服务启动时加载保存的相关信息
复制代码
1.4 继续追踪方法 看到feign拼装目标服务器请求地址/参数 并进行接口请求 见图
复制代码
到这步就请求被执行完毕了 后续操作****