解决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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Centos7关闭selinux命令「建议收藏」

    关闭selinux步骤0x01用vi修改selinux的配置文件vi/etc/selinux/config0x02修改#SELINUX=enforcing为SELINUX=disabled这里就不修改了,注释掉好了,再直接复制修改为SELINUX=disabledselinux的工作模式enforcing强制模式permissive宽容模式disabled关闭什么是selinux?selinux是Linux的一种安全子系统 Linux中的权限管..

    2022年4月18日
    230
  • C# 登陆验证码工具类VerifyCode

    C# 登陆验证码工具类VerifyCodeusingSystem;usingSystem.Collections.Generic;usingSystem.Drawing;usingSystem.Drawing.Imaging;usingSystem.IO;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;…

    2022年7月15日
    17
  • OTL音频功率放大器

    OTL音频功率放大器音频功率放大器是音响系统中不可缺少的重要部分,其主要任务是将微弱的音频信号进行功率放大到足以推动外接负载,如扬声器、音响等。OTL功率放大器,它具有非线性失真小,频率响应宽,电路性能指标较高等优点,也是目前OTL电路在各种高保真放大器应用电路中较为广泛采用的电路之一。本人的一次课设制作出来的实物图如下:实现功能有:实现对音量、音调的控制图1…

    2022年5月3日
    46
  • ORBSLAM2学习(一):ORB算法原理[通俗易懂]

    ORBSLAM2学习(一):ORB算法原理[通俗易懂]前言目前学习ORBSLAM2中,ORBSLAM2中使用ORB算子进行特征点的提取与描述,ORB算法原理主要来自于文章《ORBanefficientalternativetoSIFTorSURF》。这里先就该文章做自己的学习过程记录,之后结合文章内容分析ORBSLAM2中的代码实现(放到下一篇博客中)。本文把文章《ORBanefficientalternative……

    2025年7月26日
    3
  • 眼下最好的JSP分页技术

    眼下最好的JSP分页技术

    2021年11月13日
    42
  • Amazon Elastic Compute Cloud(服务计费方式)

    Amazon Elastic Compute Cloud(服务计费方式)

    2021年5月7日
    132

发表回复

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

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