解决Hmily与Feign冲突报错 NullPointerException

解决Hmily与Feign冲突报错 NullPointerException在项目中使用了Hmily保证分布式事务的一致性,由于Hmily会注册一个HmilyFeignInterceptor,并且feign会将其添加到SynchronousMethodHandler中的requestInterceptors,当feign客户端执行HmilyFeignInterceptor中apply方法publicvoidapply(finalRequestTemplaterequestTemplate){Transmiter.getInstance

大家好,又见面了,我是你们的朋友全栈君。

在项目中使用了Hmily保证分布式事务的一致性,由于Hmily会注册一个 HmilyFeignInterceptor ,并且feign会将其添加到 SynchronousMethodHandler 中的 requestInterceptors ,当feign客户端执行 HmilyFeignInterceptor 中apply方法

public void apply(final RequestTemplate requestTemplate) { 
   
        Transmiter.getInstance().transmit((x$0, xva$1) -> { 
   
            requestTemplate.header(x$0, new String[]{ 
   xva$1});
        }, HmilyTransactionContextLocal.getInstance().get());
    }

由于获取到的 HmilyTransactionContext 为 null ,所以抛出 NullPointerException 异常。

解决方法:

定义一个后置处理器,将没有被 @Hmily 注解的方法,移除 HmilyFeignInterceptor 。

package com.jz.shop.cart.service;

import com.jz.shop.commons.utils.text.StringUtils;
import feign.InvocationHandlerFactory;
import feign.ReflectiveFeign;
import feign.RequestInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.hmily.annotation.Hmily;
import org.dromara.hmily.springcloud.feign.HmilyFeignInterceptor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RestController;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

/** * @author:JZ * @date:2020/6/1 */
@Slf4j
@Component
public class ShopFeignPostProcessor implements BeanPostProcessor { 
   

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 
   
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 
   
        // 对所有含有 @FeignClient 的bean进行处理
        if (StringUtils.isNotNull(AnnotationUtils.findAnnotation(bean.getClass(), FeignClient.class))) { 
   
            // 排除含有 @Controller 和 @RestController 注解的bean
            if (StringUtils.isNotNull(AnnotationUtils.findAnnotation(bean.getClass(), Controller.class)) ||
                    StringUtils.isNotNull(AnnotationUtils.findAnnotation(bean.getClass(), RestController.class))) { 
   
                return bean;
            }
            try { 
   
                // 获取代理类中的 FeignInvocationHandler
                Field h = bean.getClass().getSuperclass().getDeclaredField("h");
                boolean hAccessible = h.isAccessible();
                h.setAccessible(true);
                Object feignInvocationHandler = h.get(bean);
                /** * 获取 FeignInvocationHandler 中 dispatch 字段的 Map<Method, MethodHandler> dispatch 属性。 * dispatch中包含feign代理的方法 和 SynchronousMethodHandler */
                Field dispatchField = feignInvocationHandler.getClass().getDeclaredField("dispatch");
                boolean dispatchAccessible = dispatchField.isAccessible();
                dispatchField.setAccessible(true);
                Map<Method, InvocationHandlerFactory.MethodHandler> dispatch =
                        (Map<Method, InvocationHandlerFactory.MethodHandler>) dispatchField.get(feignInvocationHandler);

                /** * SynchronousMethodHandler 中的 List<RequestInterceptor> requestInterceptors 字段 * 加载了Hmily对feign的拦截器 HmilyFeignInterceptor */
                for (Map.Entry<Method, InvocationHandlerFactory.MethodHandler> entry : dispatch.entrySet()) { 
   
                    /** * 没有添加 @Hmily 注解的方法不需要被 Hmily 拦截处理, * 否则会因为加载的 HmilyTransactionContext 为 null 导致 NullPointerException */
                    if (StringUtils.isNull(AnnotationUtils.findAnnotation(entry.getKey(), Hmily.class))) { 
   
                        Field riField = entry.getValue().getClass().getDeclaredField("requestInterceptors");
                        boolean riAccessible = riField.isAccessible();
                        riField.setAccessible(true);
                        List<RequestInterceptor> requestInterceptors = (List<RequestInterceptor>) riField.get(entry.getValue());
                        for (RequestInterceptor interceptor : requestInterceptors) { 
   
                            if (interceptor instanceof HmilyFeignInterceptor) { 
   
                                requestInterceptors.remove(interceptor);
                                break;
                            }
                        }
                        riField.setAccessible(riAccessible);
                        log.info("{}.{} 方法移除 HmilyFeignInterceptor", beanName, entry.getKey().getName());
                    }
                }
                dispatchField.setAccessible(dispatchAccessible);
                h.setAccessible(hAccessible);
            } catch (Exception e) { 
   
                log.warn("{} exception", beanName);
                e.printStackTrace();
            }
        }
        return bean;
    }

}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/144064.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 单链表排序java_快速排序链表

    单链表排序java_快速排序链表难易程度:★★重要性:★★★链表的排序相对数组的排序更为复杂些,也是考察求职者是否真正理解了排序算法(而不是“死记硬背”) 链表的插入排序 publicclassLinkedInsertSort{staticclassListNode{intval;ListNodenext;Lis…

    2022年10月10日
  • 宽带开启ipv6的方法(包含光猫改桥接和光猫改桥接后访问其管理界面以及路由器拨号)

    宽带开启ipv6的方法(包含光猫改桥接和光猫改桥接后访问其管理界面以及路由器拨号)移动宽带开启ipv6以及光猫改桥接后访问光猫管理界面的方法

    2022年10月8日
  • 1442. 形成两个异或相等数组的三元组数目[通俗易懂]

    1442. 形成两个异或相等数组的三元组数目[通俗易懂]给你一个整数数组 arr 。现需要从数组中取三个下标 i、j 和 k ,其中 (0 <= i < j <= k < arr.length) 。a 和 b 定义如下:a = arr[i] ^ arr[i + 1] ^ … ^ arr[j – 1]b = arr[j] ^ arr[j + 1] ^ … ^ arr[k]注意:^ 表示 按位异或 操作。请返回能够令 a == b 成立的三元组 (i, j , k) 的数目。示例 1:输入:arr = [2,3,1,6,7]

    2022年8月9日
  • wing是什么_acwing算法基础

    wing是什么_acwing算法基础原题链接设有 N×N 的方格图,我们在其中的某些方格中填入正整数,而其它的方格中则放入数字0。如下图所示:某人从图中的左上角 A 出发,可以向下行走,也可以向右行走,直到到达右下角的 B 点。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。此人从 A 点到 B 点共走了两次,试找出两条这样的路径,使得取得的数字和为最大。输入格式第一行为一个整数N,表示 N×N 的方格图。接下来的每行有三个整数,第一个为行号数,第二个为列号数,第三个为在该行、该列上所放的数。行和列编号从

    2022年8月8日
  • 图片压缩最优方案

    图片压缩最优方案

    2022年3月1日
  • 关于get请求的长度限制到底是多少?—-一个误区,一个教训

    关于get请求的长度限制到底是多少?—-一个误区,一个教训截至今日之前,我一直因为从某处看到get、post区别中写的:get有长度限制,1024B。很抱歉在未经过个人的检验后,直接奉为正确的定义(也提醒我个人:以后概念理论,还是需要好好验证或求证,要能在繁杂的网络知识中,认真求真,以防以讹传讹!!!)。今日,看到前同事大牛多年前的csdn知识总结,发现原来一直信奉的1024Get请求长度,是错误的。下面把从权威官网的解释复制过来,以做…

    2022年8月24日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号