自定义BeanPostProcessor踩坑记

背景

BeanPostProcessor是Spring扩展机制中的其中一种,它在Bean生命周期中的位置如下:

自定义BeanPostProcessor踩坑记

一般我们可以用它来对bean实例化前后做一些处理,如收集并处理自定义注解从而为成员变量赋上值等。

问题

现在有三个类,AnnotationProcessor用于处理自定义注解,Anno为自定义注解,MyProcessor依赖AnnotationProcessor处理结果。

@Component
public class AnnotationProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Field[] fields = bean.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (field.getAnnotation(Anno.class) != null) {
                //对字段做处理
            }
        }
        return bean;
    }
}复制代码
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Anno {

}复制代码
@Component
public class MyProcessor implements BeanPostProcessor {

    @Anno
    private String str;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        //str此时为null

        return null;
    }
}复制代码

程序运行起来之后,会发现MyProcessor#postProcessAfterInitialization()拿到的str是空的,为什么会这样呢?怎么样才能让str是已经经过AnnotationProcessor处理的?

问题分析

我们可以通过查看源码来看Spring是如何处理BeanPostProcessor。由于MyProcessor 本身也是一个bean,那么我们可以到Spring实例化 MyProcessor 的地方,看看当时对应的BeanPostProcessor集合当中有哪些。

代码到达AbstractAutowireCapableBeanFactory#doCreateBean()位置,BeanPostProcessor的处理是在这里完成:

自定义BeanPostProcessor踩坑记

自定义BeanPostProcessor踩坑记

自定义BeanPostProcessor踩坑记

可以看到,在实例化MyProcessor的时候,在当时已经注册到容器的BeanPostProcessor集合中,只有Spring内置的一些BeanPostProcessor,此时并没有任何自定义的BeanPostProcessor

我们再看BeanPostProcessor是如何注册的,代码来到PostProcessorRegistrationDelegate#registerBeanPostProcessors()(部分代码省略)

public static void registerBeanPostProcessors(
			ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {

  	// 1、注册实现了PriorityOrdered的BeanPostProcessor
	sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
	registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

  	// 2、注册实现了Ordered接口的BeanPostProcessor
	sortPostProcessors(orderedPostProcessors, beanFactory);
	registerBeanPostProcessors(beanFactory, orderedPostProcessors);

        //3、注册一般的BeanPostProcessor(即示例中的AnnotationProcessor、MyProcessor) 	List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanPostProcessor>();
	for (String ppName : nonOrderedPostProcessorNames) {
            //这里会触发AnnotationBeanPostProcessor的实例化,但此时registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors)未执行,AnnotationBeanPostProcessor未注册到BeanFactory中,所以在示例中拿不到AnnotationBeanPostProcessor的处理结果	    BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
	    nonOrderedPostProcessors.add(pp);
            if (pp instanceof MergedBeanDefinitionPostProcessor) {
		internalPostProcessors.add(pp);
            }
	}
	registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

  	//4、注册Spring内部用的BeanPostProcessor
	sortPostProcessors(internalPostProcessors, beanFactory);
	registerBeanPostProcessors(beanFactory, internalPostProcessors);
}复制代码

到这里,我们可以看到我们自定义的BeanPostProcess是在第三步被处理,此时会先实例化BeanPostProcess,再注册到容器中,因此我们示例中的MyProcessor是不会被AnnotationProcessor处理的。

那么要如何才能被处理呢?可以看到第三步之前第一步和第二步会先注册实现了PriorityOrdered或Ordered接口的BeanPostProcessor,那么只要我们让AnnotationProcessor实现了这两个接口之一,也能实现我们的目的。

结论

由此可以得出,如果希望中MyProcessor用到的属性是被AnnotationProcessor处理过的,就需要让AnnotationProcessor实现PriorityOrdered或Ordered接口。

最后

Spring对处理BeanPostProcessor的依赖关系有什么优化点?

个人理解:后一个处理的BeanPostProcessor可以依赖于前一个BeanPostProcessor的处理结果。对于带有优先级的BeanPostProcessor,通过优先级可以实现;对于无优先级的BeanPostProcessor来说,就取决于声明的顺序。

emm…这么玩BeanPostProcessor合不合理?

可能Spring设计BeanPostProcessor的初衷就不让BeanPostProcessor依赖于其它BeanPostProcessor的结果,每个BeanPostProcessor处理的逻辑应该都是独立的。

原文 

https://juejin.im/post/5d4f0c00518825262a6bcdc0

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » 自定义BeanPostProcessor踩坑记

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址