解决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)
上一篇 2022年5月20日 上午10:20
下一篇 2022年5月20日 上午10:20


相关推荐

  • xamarin android listview的用法

    xamarin android listview的用法listview也许是用的非常频繁的一个控件之一,下面我写一个xamarin的listview栗子,大家尝一尝xamarinandroid开发的乐趣。原谅我的大小写吧.listview绑定自定义的BaseAdapter先来看一下最终实现的效果图:News.cs和NewAdapter.csnamespaceDrawerLayout.Adapter{public…

    2022年7月22日
    15
  • 前端人员该怎么面试 经典Angular面试题有哪些[通俗易懂]

    前端人员该怎么面试 经典Angular面试题有哪些[通俗易懂]前端人员该怎么面试?经典Angular面试题有哪些?AngularJS是一个JavaScript框架,是一个以JavaScript编写的库。它可通过1、解释Angular2应用程序的生命周期hooks是什么?Angular2组件/指令具有生命周期事件,是由@angular/core管理的。@angular/core会创建组件,渲染它,创建并呈现它的后代。当@angular/core的数据绑定…

    2022年10月17日
    4
  • 万字详解,Hadoop大数据技术简介及 伪分布式集群搭建快速入门教程

    万字详解,Hadoop大数据技术简介及 伪分布式集群搭建快速入门教程在大学时学习Hadoop大数据技术的时候,安装配置Hadoop框架,发现找的一些资料介绍得不够详细,比如一些路径的变化没有说清楚,这对于初学者来说是不够友好的,所以在这里做个详细总结介绍一下Hadoop框架,以及Ubuntu版本的Hadoop伪分布式的安装配置。

    2022年6月14日
    46
  • Java集合类详解

    Java集合类详解Collection├List│├LinkedList│├ArrayList│└Vector│ └Stack└SetMap├Hashtable├HashMap└WeakHashMapCollection接口  Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Element

    2022年4月27日
    44
  • netstat -an 结果分析

    netstat -an 结果分析netstat an 及其结果分析 netstat 结果前言 这几天由于病毒的日益流行 许多朋友开始对防毒和防黑重视起来 装了不少的病毒或网络防火墙 诚然 通过防火墙我们可以得到许多有关我们计算机的信息 不过 windows 自带的 netstat 更加小巧玲珑 可以让你不费吹灰之力就可以对本机的开放端口和连接信息一览无余 针对 netstat 命令的用

    2026年3月26日
    1
  • lrzsz linux安装包,linux 离线安装lrzsz「建议收藏」

    lrzsz linux安装包,linux 离线安装lrzsz「建议收藏」安装gcc环境yuminstall–downloadonly–downloaddir=/usr/local/gccgccyuminstall–downloadonly–downloaddir=/usr/local/gcc++gcc-c++cd/usr/local/gcccd/usr/local/gcc++1.下载lrzsz-0.12.20.tar.gz2.上传压缩包到服…

    2022年6月23日
    90

发表回复

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

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