解决Hmily与Feign冲突报错 NullPointerException的问题

 更新时间:2021年11月16日 14:56:06   作者:Been_You  
这篇文章主要介绍了解决Hmily与Feign冲突报错 NullPointerException的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

Hmily与Feign冲突报错 NullPointerException

在项目中使用了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;
    }
}

java.lang.NullPointerException出现的几种原因及解决

出现的原因

1、字符串变量未初始化

2、接口类型的对象没有用具体的类初始化,比如:

Map map // 会报错
Map map = new Map(); //则不会报错了

3、当一个对象的值为空时,你没有判断为空的情况。

4、字符串与文字的比较,文字可以是一个字符串或Enum的元素,如下会出现异常

String str = null;
if(str.equals(“Test”)){undefined
//这里的代码将不会被触发,因为会抛出java.lang.NullPointerException异常。
}

5、优先使用String.valueOf()方法代替toString()

当程序代码需要对象的字符串表示形式时,请避免使用该对象的toString方法。如果你的对象的引用等于null,NullPointerException则会抛出,使用静态String.valueOf方法,该方法不会抛出任何异常并打印"null"

6、class被声明了类型, 默认 class = null; 这样在调用class中方法的时候系统只能给你个空指针异常, 给其实例化就好了:class = new Class();

7、返回null,方法的返回值不要定义成为一般的类型,而是用数组。这样如果想要返回null的时候就能避免许多不必要的NullPointerException

我加粗的两个是比较常见的,容易忽略的错误,

大部分都是字符串比较的时候由于str=null,那么使用str.equals(“Test”)就会抛出异常

null不能和字符串进行比较

解决的办法有两种:

  • 就是在比较之前判断字符串是否为空
  • 当传入的参数str是空值的时候,程序就会异常,正确的是应该把字符串放在前面
"Test".equals(str)

推荐使用第二种。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java简单实现农夫过河问题示例

    Java简单实现农夫过河问题示例

    这篇文章主要介绍了Java简单实现农夫过河问题,简单描述了农夫过河问题的概念、原理并结合简单实例形式分析了java解决农夫过河问题的相关操作技巧,需要的朋友可以参考下
    2017-12-12
  • SpringBoot使用Guava实现日志脱敏的示例代码

    SpringBoot使用Guava实现日志脱敏的示例代码

    本文主要介绍了SpringBoot使用Guava实现日志脱敏的示例代码,使用Guava中的Strings、Maps和CharMatcher类来进行日志脱敏,保护敏感数据的安全,感兴趣的可以了解一下
    2024-01-01
  • idea开启mybatis控制台SQL日志打印的代码示例

    idea开启mybatis控制台SQL日志打印的代码示例

    本文主要介绍了idea开启mybatis控制台SQL日志打印的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-12-12
  • Java hutool List集合对象拷贝示例代码

    Java hutool List集合对象拷贝示例代码

    这篇文章主要介绍了Java hutool List集合对象拷贝的相关资料,文章还分享了在实现过程中遇到的一些问题,并强调了阅读源码和正确配置CopyOptions的重要性,需要的朋友可以参考下
    2024-12-12
  • Java下载Excel模板文件的简单实现方法

    Java下载Excel模板文件的简单实现方法

    这篇文章主要给大家介绍了关于Java下载Excel模板文件的简单实现方法,日常工作中可能经常会涉及到用java开发报表,需求比较多的就是表格类的报表导出,单元格合并,图表的展现,需要的朋友可以参考下
    2023-07-07
  • MybatisPlus 自定义.vm模板的生成

    MybatisPlus 自定义.vm模板的生成

    为更加快捷方便的开发代码,使用MybatisPlus的代码自动生成功能,将一些繁琐的操作自动生成,本文主要介绍了MybatisPlus 自定义.vm模板的生成,感兴趣的可以了解一下
    2024-03-03
  • java、js中实现无限层级的树形结构方法(类似递归)

    java、js中实现无限层级的树形结构方法(类似递归)

    下面小编就为大家带来一篇java、js中实现无限层级的树形结构方法(类似递归)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-11-11
  • Java关键字synchronized基本使用详解

    Java关键字synchronized基本使用详解

    这篇文章主要给大家介绍了关于Java关键字synchronized基本使用的相关资料,synchronized可以用来同步静态和非静态方法,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-01-01
  • Spring基础之AOP的概念介绍

    Spring基础之AOP的概念介绍

    AOP是Spring的关键特性之一,虽然Spring的IOC特性并不依赖于AOP,本文重点介绍AOP编程中的一些术语,这些术语不仅仅局限于Spring,它适用于所有的AOP编程,感兴趣的朋友一起看看吧
    2022-06-06
  • Java责任链设计模式实例分析

    Java责任链设计模式实例分析

    这篇文章主要介绍了Java责任链设计模式,结合实例形式详细分析了Java责任链设计模式的原理与相关操作技巧,需要的朋友可以参考下
    2019-07-07

最新评论