由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注解了.

好了,到这里就结束

BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor都是Spring框架中的回调接口,用于在Spring容器初始化过程中对Bean定义或Bean工厂执行自定义操作。它们的主要相同点和区别如下: **相同点:** 1. **生命周期**: 都是在Spring应用上下文初始化前(即`preRefresh()`阶段)和初始化后(即`postProcessBeanDefinitions()`或`postProcessBeanFactory()`方法)执行。 2. **目的**: 都可以用来修改或扩展Spring容器的配置,例如添加额外的配置信息或进行定制。 **区别:** 1. **作用范围**: - `BeanDefinitionRegistryPostProcessor`: 更专注于处理Bean定义层面,它可以直接操作`BeanDefinitionRegistry`,比如注册新的Bean定义、修改已有的定义等。此接口适合于需要基于XML或注解自动装配之前的操作。 - `BeanFactoryPostProcessor`: 侧重于Bean实例化后的处理,即在所有Bean创建完成之后执行。它只能访问`BeanFactory`,无法直接改变Bean定义,更适合对已有Bean进行后期操作,如AOP切面的织入。 2. **可用资源**: - `BeanDefinitionRegistryPostProcessor`可以在Bean定义阶段访问更多的元数据,如Bean的依赖关系等。 - `BeanFactoryPostProcessor`则在Bean实例化后才能访问这些信息,因为这时Bean的实际状态已经形成。 3. **延迟加载**: - `BeanDefinitionRegistryPostProcessor`如果在处理Bean定义期间有依赖于其他bean的情况,会引发懒加载策略。 - `BeanFactoryPostProcessor`同样面临这个问题,但它更关注的是单个bean实例的处理,所以可能会有额外的限制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值