一、问题背景
在自定义的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
二、探索之路
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中
三、解决方案
于是改造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,成功注入