Solve the conflict between Hmily and Feign and report the error of NullPointerException
- 2021-12-12 04:37:58
- OfStack
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.