了解OpenFeign实现原理,实现过程中学习了两个Spring扩展接口
-
定义接口,添加自定义注解,注解的属性值代表请求地址
-
接口内定义方法,方法上加@RequestMapping注解属性值是请求路径
-
自定义注解,注解属性值可以设置为请求地址
-
给添加了自定义注解的接口创建代理对象,创建时机在bean实例化之前
-
设置代理对象的invoke方法,发送http请求
具体实现:
实现过程为,首先创建自定义注解,这个注解的作用是加了这个注解的接口会被创建一个代理对象,当代理对象的方法被调用时,会发送一个HTTP请求,并返回请求的结果,相当于模拟@FeignClient注解的作用
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomProxy {
String service();
String address();
}
|
然后创建InvocationHandler对象,当调用代理对象的方法时,这个对象的 invoke 方法会被调用,invoke 方法内部逻辑是利用反射获取代理方法所在类的class对象,判断是否包含目标注解,包含目标注解则获取注解的属性值,进行url拼接,然后利用RestTemplate发送http请求。
public class CustomProxyHandler implements InvocationHandler {
RestTemplate restTemplate = new RestTemplate();
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Class<?> declaringClass = method.getDeclaringClass();
if (declaringClass.isAnnotationPresent(CustomProxy.class)) {
CustomProxy annotation = declaringClass.getAnnotation(CustomProxy.class);
String service = annotation.service();
String address = annotation.address();
String url = address + service;
RequestMapping annotation1 = method.getAnnotation(RequestMapping.class);
url = url + annotation1.value()[0];
if (args != null && args.length != 0) {
System.out.println("代理对象的方法被调用了");
return restTemplate.getForObject(url, method.getReturnType(), args);
} else {
System.out.println("代理对象的方法被调用了");
return restTemplate.getForObject(url, method.getReturnType());
}
} else {
return null;
}
}
}
|
然后定义创建代理对象的工厂方法,工厂方法接收一个class对象,利用Proxy.newProxyInstance传入类加载器,代理类 Class 对象和InvocationHandler对象,通过调用静态工厂方法即可创建目标接口的代理对象
public class ProxyFactory {
public static <T> T createProxy(Class<T> targetInterface) {
return (T) Proxy.newProxyInstance(
targetInterface.getClassLoader(),
new Class[]{targetInterface},
new CustomProxyHandler()
);
}
}
|
创建时机在bean实例化之前,因此需要实现Spring 框架提供的一个扩展接口,InstantiationAwareBeanPostProcessor,重写其中的方法,可以在 Spring 容器实例化和初始化 bean 的各个阶段进行自定义处理,从而灵活地对 bean 进行定制化的操作,然后postProcessBeforeInstantiation在实例化前的方法中创建目标接口的代理对象。
@Slf4j
@Component("openInstantiationAwareBeanPostProcessor")
public class OpenInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor{
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if(!beanClass.isAnnotationPresent(CustomProxy.class)){
return null;
}
log.info("正在为:{}生成代理对象,被代理的类为:{}",beanName,beanClass.getName());
Object object = ProxyFactory.createProxy(beanClass);
return object;
}
|
启动类
@SpringBootApplication
@Import({MyService.class})
public class LaunchApplication {
public static void main(String[] args) {
SpringApplication.run(LaunchApplication.class, args);
}
}
|
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人