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


相关推荐

  • cocoapods使用_软线连接

    cocoapods使用_软线连接背景

    2022年9月27日
    3
  • 2020年云服务器采购季-福利专区

    2020年云服务器采购季

    2022年3月14日
    43
  • scratch编程小游戏黑白棋

    scratch编程小游戏黑白棋你有没有玩过一种游戏,就是按3X3排列的方块,方块中有黑有白,当你按下一个后周围的黑块会变成白块,白块变黑块,当全部的方块都变成白色是就赢了,今天我们就来编写这个游戏!首先来看看效果:知道怎么玩了吗?现在就来看看是怎么编写的吧!首先画出所有的角色:方块要画出一黑一白:方块首先要移动到合适的位置,然根据探测器来切换黑白:方块1:方块2:方块3:方块4:方块5:方块6:方块7:方块8:方块9:现在是最最最最重要的探测器程序了,探测器决定了布局的结果,保证不会

    2022年6月15日
    55
  • pycharm引入anaconda_pycharm怎么配置anaconda环境

    pycharm引入anaconda_pycharm怎么配置anaconda环境第一步:找到Settings,双击第二步:找到Project,选择第一个Interpreter,然后点击右上角选择添加第三步:选择第二个CondaEnvironment,再勾选Existingenvironment第四步:在interpreter中找到anaconda安装的路径第五步:找到env中py_36(这个可能不一样,因为这是你创建的环境命名),然后找到p…

    2022年8月26日
    7
  • C#泛型入门泛型类、泛型集合、泛型方法、泛型约束、泛型委托

    C#泛型入门泛型类、泛型集合、泛型方法、泛型约束、泛型委托泛型集合和ArrayList的装箱拆箱、常见的泛型类型、泛型类和泛型方法、泛型约束、泛型委托泛型很难理解?不然在接触的一个新的概念的时候,总会感觉难以理解,当你掌握并能熟练地使用的时候,发现这个概念其实简单的,我相信大多数码农都会有这种似曾相识的感觉。可能大多数人刚学习泛型的时候觉得很难理解,当然我也是这样的,所以便写下这篇文章加深一下对泛型的印象。

    2022年7月11日
    17
  • 用JavaScript制作页面特效

    用JavaScript制作页面特效1.Window对象名称history:有关客户访问过的URL的信息location:有关当前URL的信息screen:有关客户端的屏幕和显示性能的信息常用方法prompt():弹出输入框

    2022年7月2日
    27

发表回复

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

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