Solve the conflict between Hmily and Feign and report the error of NullPointerException

  • 2021-12-12 04:37:58
  • OfStack

Directory Hmily and Feign Conflict Report Error NullPointerException Solution java. lang. NullPointerException

Conflict between Hmily and Feign reported error NullPointerException

In the project, Hmily is used to ensure the uniformity of distributed transactions. Because Hmily will register an HmilyFeignInterceptor, and feign will add it to requestInterceptors in SynchronousMethodHandler, when feign client executes apply method in HmilyFeignInterceptor,


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

Because the HmilyTransactionContext obtained is null, an NullPointerException exception is thrown.

Solution

Define a post processor to remove HmilyFeignInterceptor from methods not annotated by @ Hmily.


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 {
        //  For all those containing  @FeignClient  Adj. bean Process 
        if (StringUtils.isNotNull(AnnotationUtils.findAnnotation(bean.getClass(), FeignClient.class))) {
            //  Exclude containing  @Controller  And  @RestController  Annotated bean
            if (StringUtils.isNotNull(AnnotationUtils.findAnnotation(bean.getClass(), Controller.class)) ||
                    StringUtils.isNotNull(AnnotationUtils.findAnnotation(bean.getClass(), RestController.class))) {
                return bean;
            }
            try {
                //  Object in the proxy class  FeignInvocationHandler
                Field h = bean.getClass().getSuperclass().getDeclaredField("h");
                boolean hAccessible = h.isAccessible();
                h.setAccessible(true);
                Object feignInvocationHandler = h.get(bean);
                /**
                 *  Get  FeignInvocationHandler  Medium  dispatch  Field  Map<Method, MethodHandler> dispatch  Property. 
                 * dispatch Include in feign Method of proxy   And  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  In  List<RequestInterceptor> requestInterceptors  Field 
                 *  Loaded Hmily Right feign Interceptor of  HmilyFeignInterceptor
                 */
                for (Map.Entry<Method, InvocationHandlerFactory.MethodHandler> entry : dispatch.entrySet()) {
                    /**
                     *  Not added  @Hmily  The annotated method does not need to be  Hmily  Interception processing, 
                     *  Otherwise, it will be caused by the loaded  HmilyTransactionContext  For  null  Cause  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("{}.{}  Method to remove  HmilyFeignInterceptor", beanName, entry.getKey().getName());
                    }
                }
                dispatchField.setAccessible(dispatchAccessible);
                h.setAccessible(hAccessible);
            } catch (Exception e) {
                log.warn("{} exception", beanName);
                e.printStackTrace();
            }
        }
        return bean;
    }
}

Causes and Solutions of java. lang. NullPointerException

Causes of occurrence

1. The string variable is not initialized

2. Objects of interface type are not initialized with specific classes, such as:


Map map //  Will report an error 
Map map = new Map(); // You won't report an error 

3. When the value of an object is empty, you don't judge it as empty.

4. Comparison between string and literal, literal can be 1 string or Enum element, and the following exception will appear


String str = null;
if ( str.equals (" Test ")) {undefined
// The code here will not be triggered because it will throw java.lang.NullPointerException Abnormal. 
}

5. Preferential use of String. valueOf () method instead of toString ()

Avoid using the toString method of an object when the program code requires a string representation of the object. If your object's reference is equal to null, NullPointerException is thrown, using the static String. valueOf method, which does not throw any exceptions and prints "null."

6. class is declared as a type, and the default is class = null; In this way, when calling the method in class, the system can only give you a null pointer exception, so it is good to instantiate it: class = new Class ();

7. Return null. The return value of the method should not be defined as a 1-like type, but an array. In this way, if you want to return to null, you can avoid many unnecessary NullPointerException

My two bold ones are common and easily overlooked mistakes.

When most of them are string comparisons, because str = null, using str. equals ("Test") throws an exception

null cannot be compared to string

There are two solutions:

Is to determine whether the string is empty before comparison When the parameter str passed in is null, the program will be excepted. It is correct to put the string before it

"Test".equals(str)

The second type is recommended.


Related articles: