@Autowired注入null空指针和解决

一、问题背景

在自定义的component中,通常需要注入一些其他的bean来完成业务操作,比如需要注入service,但可能会出现@Autowired注入失败的情况。

业务背景

客户下了一个送货的订单,经过平台的派单系统 之后,司机接收到了订单,开始进行送货。但是在送货的过程中,可能会出现一些意外情况无法完成订单,因此司机需要对该订单上报异常,上报异常之后,平台根据上报的异常做出不同的处理。

POJO

这个实体类是平台受理异常订单时,做出的受理方式的请求参数,后台会根据,后台会根据不同的处理方式做出不同的操作。

@Data
public class ErrorOrderInfo implements Serializable {
    private static final long serialVersionUID = 2614758007534512965L;
    /** 记录异常订单信息的记录号 */
    private String recordNo;
    /** 上报异常的订单号 */
    private String orderNo;
    /** 异常类型 */
    private String type;
    /** 平台受理方式 */
    private String result;
}
ErrorOrderResultEnum

异常订单的处理方式枚举类

@Getter
@SuppressWarnings("all")
public enum  ErrorOrderResultEnum {
    CANCEL_ORDER("1","取消订单"),
    CHANGE_DRIVER("2","更换司机");
    // 更多类型。。。
    private final String result;
    private final String desc;
    private  ErrorOrderResultEnum(String result, String desc) {
        this.result = result;
        this.desc = desc;
    }
}
service
@Service
@Transactional(rollbackFor = Exception.class)
public class OrderErrorServiceImpl implements OrderErrorService{
    /**
     * 异常订单处理
     */
    @Override
    public void handleErrorOrder(ErrorOrderInfo info) {
        // 省略一些业务逻辑处理。。。
        
        // 开始处理平台对上报异常之后不同处理方式的功能实现
        if (ErrorOrderResultEnum.CANCEL_ORDER.getResult().equals(info.getResult())) {
            // 平台对异常订单做出"取消订单"操作
        } else if (ErrorOrderResultEnum.CHANGE_DRIVER.getResult().equals(info.getResult())) {
            // 平台对异常订单做出"更换司机"操作
        }
        // 更多处理方式
    }
}
代码实现方式改造

后来采用了策略模式改造代码,具体如下
1.1 增加一个处理异常订单的抽象处理器

public abstract class ErrorOrderHandler {
    /**
     * 根据不同的处理结果做出不同的处理
     * @param info 平台处理结果信息
     */
    public abstract Object errorOrderHandler(ErrorOrderInfo info);
}

1.2 每种处理方式使用单独的bean来处理
@ErrorOrderResultType标记这个bean用来处理那个操作类型的

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface ErrorOrderResultType {
    ErrorOrderResultEnum value();
}

CancelOrderHandler取消订单的handler

/**
 * 处理取消订单的handler
 */
@Component
@ErrorOrderResultType(ErrorOrderResultEnum.CANCEL_ORDER) // 标记为取消订单的处理器
public class CancelOrderHandler extends ErrorOrderHandler{
    /**  注入订单service */
    @Autowired
    private OrderService orderService;
    
    /** 继承抽象处理器 ErrorOrderHandler 实现具体的操作逻辑*/
    @Override
    public Object errorOrderHandler(ErrorOrderInfo info) {
        // 省去一些业务逻辑
        
        // cancelOrder方法是取消订单的具体实现
        return orderService.cancelOrder(info);
    }
}

ChangeDriverHandler更换司机的handler

/**
 * 处理更换司机的handler
 */
@Component
@ErrorOrderResultType(ErrorOrderResultEnum.CHANGE_DRIVER) // 标记为更换司机的处理器
public class ChangeDriverHandler extends ErrorOrderHandler{
    /**  注入司机service */
    @Autowired
    private DriverService driverService;
    
    /** 继承抽象处理器 ErrorOrderHandler 实现具体的操作逻辑*/
    @Override
    public Object errorOrderHandler(ErrorOrderInfo info) {
        // 省去一些业务逻辑
        
        // changeDriver方法是更换司机的具体实现
        return driverService.changeDriver(info);
    }
}

1.3 初始化CancelOrderHandler和ChangeDriverHandler
将其初始化到HandlerContext中

/**
 * 异常订单处理器上下文对象
 */
public class HandlerContext {
    
    private final Map<ErrorOrderResultEnum, ErrorOrderHandler> handlerMap;
    
    public HandlerContext(Map<ErrorOrderResultEnum, ErrorOrderHandler> handlerMap) {
        this.handlerMap = handlerMap;
    }
    
    /**
     * 获得实例
     */
    public ErrorOrderHandler getInstance(ErrorOrderResultEnum type) {
        return handlerMap.get(type);
    }
}

1.4 对HandlerContext初始化

/**
 * 初始化数据
 */
@Component
public class HandlerProcessor implements BeanFactoryPostProcessor {
    /** 保存初始化后的handler */
    private final Map<ErrorOrderResultEnum, ErrorOrderHandler> handlerMap = new ConcurrentHashMap(ErrorOrderResultEnum.values().length);
    /**
     * 将HandlerContext单例注册到bean工厂,构造注入handlerMap
     * @param beanFactory bean工厂
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        for (ErrorOrderResultEnum temp : ErrorOrderResultEnum.values()) {
            ErrorOrderHandler handler = this.getBeansWithAnnotation(beanFactory,
                    ErrorOrderHandler.class,ErrorOrderResultType.class,temp);
            if (handler != null) {
                handlerMap.put(temp, handler);
            }
        }
        // 将HandlerContext以单例模式注册到beanFactory中
        HandlerContext context = new HandlerContext(handlerMap);
        beanFactory.registerSingleton(HandlerContext.class.getName(),context);
    }
    
    /**
     * 获取带有@ErrorOrderResultType注解的bean
     * @param beanFactory bean工厂
     * @param parentClass 带有@ErrorOrderResultType的bean的父类
     * @param anno @ErrorOrderResultType注解
     * @param type 枚举类型
     */
    private <T> T getBeansWithAnnotation(ConfigurableListableBeanFactory beanFactory,
                                                     Class<T> parentClass,
                                                     Class<? extends ErrorOrderResultType> anno,
                                                     ErrorOrderResultEnum type) {
        Collection<T> tCollection = beanFactory.getBeansOfType(parentClass).values();
        for (T t : tCollection) {
            // 是否有@ErrorOrderResultType注解
            ErrorOrderResultType annotation = t.getClass().getAnnotation(anno);
            if (annotation != null) {
                // 枚举bean上的枚举类型是否对应
                ErrorOrderResultEnum e = annotation.value();
                // 返回枚举类型匹配的bean
                if (e != null && type.getResult().equals(e.getResult())) {
                    return t;
                }
            }
        }
        return null;
    }
}

1.5 改造OrderErrorService

@Service
@Transactional(rollbackFor = Exception.class)
public class OrderErrorServiceImpl implements OrderErrorService{
    @Autowired
    private HandlerContext context;
    /**
     * 异常订单处理
     */
    @Override
    public void handleErrorOrder(ErrorOrderInfo info) {
        // 省略一些业务逻辑处理。。。
        
        // 开始处理平台对上报异常之后不同处理方式的功能实现
        // if (ErrorOrderResultEnum.CANCEL_ORDER.getResult().equals(info.getResult())) {
        //     // 平台对异常订单做出"取消订单"操作
        // } else if (ErrorOrderResultEnum.CHANGE_DRIVER.getResult().equals(info.getResult())) {
        //     // 平台对异常订单做出"更换司机"操作
        // }
        // 更多处理方式
    
        
        // 通过info获取到对应的枚举类(此处省略具体的实现),通过枚举类获取对应的处理器
        ErrorOrderHandler instance = context.getInstance(type);
        instance.errorOrderHandler(info);
    }
}

改造后的结果

在CancleOrderHandelr中@Autowire注入的OrderService变成了null
@Autowired注入null空指针和解决

二、探索之路

2.1 @autowire注入null
2.2 尝试了set方法注入 依旧是null
2.3 构造注入,还是null
2.4 于是又通过@postConstruct初始化,还是null

@Autowired
    private Orderservice orderservice;
    
    private static CancelOrderHandler cancelOrderHandler;
    @PostConstruct
    public void init() {
        cancelOrderHandler = this;
        cancelOrderHandler.orderservice = this.orderservice;
    }

2.5 通过spring上下文工具类获取,还是null
SpringUtils

@Component
public final class SpringUtils implements BeanFactoryPostProcessor {
    /**
     * Spring应用上下文环境
     */
    private static ConfigurableListableBeanFactory beanFactory;

    @Override
    public void postProcessBeanFactory(@NotNull ConfigurableListableBeanFactory beanFactory) throws BeansException {
        SpringUtils.beanFactory = beanFactory;
    }

    /**
     * 获取对象
     *
     * @param name
     * @return Object 一个以所给名字注册的bean的实例
     * @throws BeansException
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) throws BeansException {
        return (T) beanFactory.getBean(name);
    }

    /**
     * 获取类型为requiredType的对象
     *
     * @param clz 类型
     * @return T
     * @throws BeansException
     */
    public static <T> T getBean(Class<T> clz) throws BeansException {
        return beanFactory.getBean(clz);
    }
}
private final OrderService orderService = SpringUtils.getBean(OrderService.class);

2.6于是开始怀疑应该是初始化CancleOrderHandler的时候出了问题
通过debug发现,确实是bean生命周期的问题,初始化的时候handlerMap中的存储的bean并没有进行属性注入,就被取出来放到了handlerMap中
@Autowired注入null空指针和解决

三、解决方案

于是改造HandlerProcessor,同时实现BeanPostProcessor, BeanFactoryPostProcessor两个接口,并重写以下两个方法

/**
 * 初始化数据
 */
@Component
public class HandlerProcessor implements BeanPostProcessor, BeanFactoryPostProcessor {
    private final Map<ErrorOrderResultEnum, ErrorOrderHandler> handlerMap = new ConcurrentHashMap(ErrorOrderResultEnum.values().length);
    /**
     * 将HandlerContext单例注册到bean工厂,构造注入handlerMap
     * @param beanFactory bean工厂
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 将HandlerContext以单例模式注册到beanFactory中
        HandlerContext context = new HandlerContext(handlerMap);
        beanFactory.registerSingleton(HandlerContext.class.getName(),context);
    }
    /**
     * 初始化handlerMap,每当初始完一个bean调用一次该方法
     * @param bean bean对象
     * @param beanName bean名字
     * @return 不能返回null,因为后置处理器从ioc取出后不会主动还回去,否则后续不能从ioc容器获得该bean,所以需要return回去而不是return null
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    // 该方法是等bean完全初始化完之后再取出来放到handlerMap中,以供自己后期使用
    // 此处省略具体的实现
	}
}    

再次测试,发现OrderService不再为 null,成功注入
@Autowired注入null空指针和解决

上一篇:java开发常用注解整理


下一篇:IDEA在使用@Autowired注解标红的问题