由spring BeanFactoryPostProcessor 引发的一个问题

本文深入探讨了在Spring框架中如何正确初始化和注册Listener,解决由懒加载引起的事件处理器未注册问题。通过实现BeanFactoryPostProcessor接口和使用@Lazy(false)及@PostConstruct注解,确保了依赖注入的正确性和事件处理器的有效注册。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天在工作中碰到这样一个, 其实之前也遇到过, 但是并没有具体的去了解详情, 今天由于疏忽再次碰到了这个问题, 所以这里和大家分享一下。

起初是这样的:

Listener<P> 接口, 负责处理事件,

ListenerRegistry 负责dispatcher 事件处理接口,也就是Listener, Listener 在实力化时通过 spring的InitializingBean 回调接口向ListenerRegistry注册自己.

下面是代码:

Listener接口:

public interface Listener<P> extends InitializingBean {

    /**
     * 是否支持此事件
     * @param event 事件类型
     * @return boolean
     */
    boolean isSupport(Event event);

    /**
     * 处理事件
     * @param param 参数
     */
    LogisticsResult<Boolean> handle(P param);
}
ListenerRegistry:
@Service
@Slf4j
public class ListenerRegistry implements BeanFactoryPostProcessor {

    private List<Listener> listeners = new ArrayList<>();

    private ConfigurableListableBeanFactory beanFactory;

    public void register(Listener listener) {
        if(listener != null) {
            listeners.add(listener);
        }
    }

    public <P> LogisticsResult<Boolean> call(Event event,P param) {
        try {

            
            for (Listener listener : listeners) {
                if(listener.isSupport(event)) {
                    return listener.handle(param);
                }
            }

            return LogisticsResult.success(Boolean.TRUE);
        } catch (Exception e) {
           
            return LogisticsResult.fail("回调处理异常,"+e.getMessage());
        }
    }
}

 

AbstractListener: 所有事件处理器继承这个抽象类

public abstract class AbstractListener<P> implements Listener<P> {

    @Autowired
    protected ListenerRegistry listenerRegistry;

    @Override
    public void afterPropertiesSet() throws Exception {
        listenerRegistry.register(this);
    }
}

现在有这样一个类,接收事件, 然后调用ListenerRegistry:

@Service
@Slf4j
public class ReceiverServiceImpl implements ReceiverService {

    @Autowired
    private ListenerRegistry listenerRegistry;

    @Override
    public LogisticsResult<Boolean> pushQuotationInfo(InquireBidDTO inquireBidDTO){
        return listenerRegistry.call(Event.Inquire_Callback,inquireBidDTO);
    }

    
}

整个应用的配置,spring bean 默认都是懒加载的, 

如上面所写, 应用可以正常启动, 但是当ReceiverService 接收到事件并传给 ListenerRegistry时, 事件并不会得到处理.

经过debug, 发现 ListenerRegistry 中并没有事件处理器注册, 这个问题不难理解,因为懒加载的问题,

还有个前提, 就是应用中没有其他类引用事件处理器,也就是Listener, 这样,Listener 实现类并被没有被spring 创建, 自然而然

InitializingBean接口也不被调用, 就不会像ListenerRegistry注册.

然后经过改造ListenerRegistry如下:

@Service
@Slf4j
public class ListenerRegistry implements BeanFactoryPostProcessor {

    private List<Listener> listeners = new ArrayList<>();

    private ConfigurableListableBeanFactory beanFactory;

    public void register(Listener listener) {
        if(listener != null) {
            listeners.add(listener);
        }
    }

    public <P> LogisticsResult<Boolean> call(Event event,P param) {
        try {

            
            for (Listener listener : listeners) {
                if(listener.isSupport(event)) {
                    return listener.handle(param);
                }
            }

            return LogisticsResult.success(Boolean.TRUE);
        } catch (Exception e) {
            
            return LogisticsResult.fail("回调处理异常,"+e.getMessage());
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
           
          beanFactory.getBeansOfType(Listener.class);
    }
}

上述改造实现了

BeanFactoryPostProcessor, 通过此postProcessBeanFactory(), 调用beanFactory.getBeansOfType(Listener.class),以触发Listener实现类的实例化

但是如上所写,依然存在问题, 就是 Listener 实现类中的依赖(通过@Autowire)的属性无法注入, 导致 导致调用ListenerRegistry.register接口时,抛空指针异常.

经过断点调试, 发现在BeanFactoryPostProcessor 执行时, 

InstantiationAwareBeanPostProcessor(实现了BeanPostProcessor接口)还未被实例化

这个接口有什么用呢? 

其实@Autowire 注解处理是由 

AutowiredAnnotationBeanPostProcessor来完成, 它继承了InstantiationAwareBeanPostProcessor,最终的处理是由InstantiationAwareBeanPostProcessor.postProcessPropertyValues这个方法来完成, AutowiredAnnotationBeanPostProcessor重写了这个方法,其代码如下:
@Override
	public PropertyValues postProcessPropertyValues(
			PropertyValues pvs, 
            PropertyDescriptor[] pds, 
            Object bean, String beanName) throws BeansException {

		InjectionMetadata metadata = findAutowiringMetadata(beanName,             
                   bean.getClass(), pvs);
		try {
			metadata.inject(bean, beanName, pvs);
		}
		catch (BeanCreationException ex) {
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
		}
		return pvs;
	}

具体的代码有兴趣者可以去看看.

找到了原因,就好处理了, 最终的代码如下,其实之前有这样写过,但是没有很详细的了解为什么要非要这么写:

改造后的ListenerRegistry:

@Service
@Slf4j
public class ListenerRegistry implements BeanFactoryPostProcessor {

    private List<Listener> listeners = new ArrayList<>();

    private ConfigurableListableBeanFactory beanFactory;

    public void init() {
        beanFactory.getBeansOfType(Listener.class);
    }

    public void register(Listener listener) {
        if(listener != null) {
            listeners.add(listener);
        }
    }

    public <P> LogisticsResult<Boolean> call(Event event,P param) {
        try {

            log.info("[[ListenerRegistry.call]>>>>回调处理,事件:{},参数:{}",event.getName(), JsonUtils.toJson(param));
            for (Listener listener : listeners) {
                if(listener.isSupport(event)) {
                    return listener.handle(param);
                }
            }

            return LogisticsResult.success(Boolean.TRUE);
        } catch (Exception e) {
            log.error("[ListenerRegistry.call]>>>>调用异常,事件:{},参数:{},ERROR:{}",event.getName(), JsonUtils.toJson(param),e.getMessage(),e);
            return LogisticsResult.fail("回调处理异常,"+e.getMessage());
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

        this.beanFactory = beanFactory;
    }
}

改造后postProcessBeanFactory不做具体事宜, 新增init方法去触发Listener实现类的实例化,

还需要添加一个类,来调用ListenerRegistry的init方法,代码如下:

@Service
@Lazy(false)
public class ListenerRegistryHelper {

    @Autowired
    private ListenerRegistry listenerRegistry;

    @PostConstruct
    public void init() {
        listenerRegistry.init();
    }
}

前提是要关闭此类的延迟初始化,为什么这样就可以了呢, 对于spring 来说,非懒加载的单例实例化时那些BeanPostProcessor接口已实例化, 就能处理Autowire注解了.

好了,到这里就结束

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值