欢迎您的访问
专注架构,Java,数据结构算法,Python技术分享

深入理解Spring IOC之扩展篇(二)、BeanFactoryPostProcessor和BeanPostProcessor

1. BeanFactoryPostProcessor

首先说BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor,先来看BeanFactoryPostProcessor:

/**
 * 1. Allows for custom modification of an application context's bean definitions,
 * adapting the bean property values of the context's underlying bean factory.
 *
 * 2. <p>Application contexts can auto-detect BeanFactoryPostProcessor beans in
 * their bean definitions and apply them before any other beans get created.
 *
 * 3. <p>Useful for custom config files targeted at system administrators that
 * override bean properties configured in the application context.
 *
 * 4. <p>See PropertyResourceConfigurer and its concrete implementations
 * for out-of-the-box solutions that address such configuration needs.
 *
 * 5. <p>A BeanFactoryPostProcessor may interact with and modify bean
 * definitions, but never bean instances. Doing so may cause premature bean
 * instantiation, violating the container and causing unintended side-effects.
 * If bean instance interaction is required, consider implementing
 * {@link BeanPostProcessor} instead.
 *
 * @author Juergen Hoeller
 * @since 06.07.2003
 * @see BeanPostProcessor
 * @see PropertyResourceConfigurer
 */
public interface BeanFactoryPostProcessor {

    /**
     * 6. Modify the application context's internal bean factory after its standard
     * initialization. All bean definitions will have been loaded, but no beans
     * will have been instantiated yet. This allows for overriding or adding
     * properties even to eager-initializing beans.
     * @param beanFactory the bean factory used by the application context
     * @throws org.springframework.beans.BeansException in case of errors
     */
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

这次你肯定会说,你TM逗我玩?没有中文注释看个蛋蛋?我不给注释的原因是本来源码中人家英文注释已经说的很清楚这玩意是干嘛的,你看不懂的话别怕,我给你一句一句翻译好了,顺便穿插上我自己的理解。

我们来看最重点的这块的注释,就是第6处的,这些英文翻译过来的意思是:在ApplicationContext里面的beanFactory对象进行标准的初始化之前修改它,此时所有的BeanDefinition应该已经被加载了,但是还没有bean被初始化。这个方法允许修改或者增加beanDefinition的属性,甚至是初始化bean。好了,6处的我已经翻译完了。我们经过对之前文章的学习,已经知道了在执行BeanFactoryPostProcessor之前就已经把所有的BeanDefinition加载进来了,对照着我对注释的翻译不难理解,我们可以去修改已经加载的BeanDefinition的信息。有的人肯定还想骚一下,在这里让某个bean提前初始化,但是请收起你的骚,为什么呢?我们来看看第5处的注释。

这段英文翻译过来的意思是:一个BeanFactoryPostProcessor可能会和其他的beanDefinition做交互或者是修改它,但是绝不是和bean去做这些事情。因为和bean做这些事情会触发bean过早的初始化,这是对容器的侵入并且会导致一些副作用,如果需要bean实例的交互,可以考虑实现BeanPostProcessor。好了,我翻译完了,你肯定还是不理解为啥不能在这里初始化。我们看看我下面这个demo:

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        TestBean testBean = beanFactory.getBean(TestBean.class);
    }
}

TestBean.java

@Component
@Data
public class TestBean {

    @Autowired
    private TestBean1 testBean1;
}

TestBean2.java

@Component
public class TestBean1 {
}

我们自己定义了一个BeanFactoryPostProcessor,然后里面进行了TestBean的实例化,接着启动项目,我们可以在控制台中看到:

93_1.png也就是说,TestBean进行了初始化,但是,它的成员属性却是null,这是因为进行属性注入的BeanPostProcessor还没有初始化,而且,直到最后,这个成员属性也一直是null,这个是在使用注解的情况下会有这样的事情发生,如果是使用的是xml,则不会有这样的事情,但是还是请不要这么做。

然后我们再说说BeanDefinitionRegistryPostProcessor,还是来看看代码哈:

/**
 * 1. Extension to the standard {@link BeanFactoryPostProcessor} SPI, allowing for
 * the registration of further bean definitions <i>before</i> regular
 * BeanFactoryPostProcessor detection kicks in. In particular,
 * BeanDefinitionRegistryPostProcessor may register further bean definitions
 * which in turn define BeanFactoryPostProcessor instances.
 *
 * @author Juergen Hoeller
 * @since 3.0.1
 * @see org.springframework.context.annotation.ConfigurationClassPostProcessor
 */
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {

    /**
     * 2. Modify the application context's internal bean definition registry after its
     * standard initialization. All regular bean definitions will have been loaded,
     * but no beans will have been instantiated yet. This allows for adding further
     * bean definitions before the next post-processing phase kicks in.
     * @param registry the bean definition registry used by the application context
     * @throws org.springframework.beans.BeansException in case of errors
     */
    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

}

我们来看第二处的翻译过来的意思:在ApplicationContext的标准初始化之后去修改它的BeanDefinition的注册。此时所有的普通的BeanDefinition都已经被加载但是还没有被初始化,这里可以在下个处理阶段之前添加更多的BeanDefinition。我们大家仔细看看这里的这个方法参数,然后再对比一下这个方法参数和BeanFactoryPostProcessor里面那个方法参数,不难发现,这两个方法的参数是不一样的,其中BeanDefinitionRegistryPostProcessor里面的方法参数是BeanDefinitonRegistry,但是BeanFactoryPostProcessor里面的却是ConfigurableListableBeanFactory,通过对比这两个参数,我们可以得出来这样的结论,BeanDefinitionRegistryPostProcessor是为了添加BeanDefinition,而BeanFactoryPostProcessor是为了修改BeanDefinition的属性,在作者看来,这两个接口都是基于框架层面的扩展,什么叫做基于框架的扩展呢,我认为,一般来说,当你需要使用到这两个接口的时候,说明你是要基于spring框架去扩展一些东西,这时候,你写出的类在业务代码中是不会使用的,而且你的代码的运行,只是在spring ioc容器初始化的这个过程中,此时业务类都还没有初始化。有的扩展点,比如你实现了InitializingBean这种接口,这时候你实现这个接口的代码很可能会在业务流程中使用,这种的扩展点我们可以理解它为基于应用层面的扩展,我们之前的自定义xml标签,也是属于基于框架层面的扩展。

我们最后来看个BeanDefinitionRegistryPostProcessor的案例吧,我们使用mybatis的时候,经常会使用Mapper注解标在一个接口上,然后会在另外一个类中通过Autowired注解去引用,这时候,其实接口根本就没有实现类,但是我们Autowired还是可以引用成功,其实就是因为mybatis-spring模块中的MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor这个接口,增加了BeanDefinition到beanFactory中,我们来看看源码:

@Override
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    if (this.processPropertyPlaceHolders) {
      processPropertyPlaceHolders();
    }

    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    scanner.setAddToConfig(this.addToConfig);
    scanner.setAnnotationClass(this.annotationClass);
    scanner.setMarkerInterface(this.markerInterface);
    scanner.setSqlSessionFactory(this.sqlSessionFactory);
    scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
    scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
    scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
    scanner.setResourceLoader(this.applicationContext);
    scanner.setBeanNameGenerator(this.nameGenerator);
    scanner.registerFilters();
    scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  }

这段代码是MapperScannerConfigurer中对BeanDefinitionRegistryPostProcessor的实现,最后这句代码,即为扫描basePackage路径下的Mapper,并解析成对应的BeanDefinition到spring容器中。

2. BeanPostProcessor

BeanPostProcessor这个是Spring提供给我们的另外一个扩展的接口,这个接口的作用是在bean已经创建之后去修改bean里面的信息,主要是修改属性。我们来看看它的代码:

public interface BeanPostProcessor {

    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

可以看到,里面也不过只有短短的两行代码而已。其中上面的方法postProcessBeforeInitialization是在bean创建出来并且初始化之前spring帮你调用的,后边的方法是在bean创建出来之后并且初始化了之后spring帮你调用的。很多人搞不懂这个初始化具体指的是啥,这个初始化指的是:bean在创建好并且被填充好了之后,但是还没做任何的初始化操作,换句话说这时候你的bean中的成员属性已经被注入了,但是可能你还需要这个成员属性去做的一些初始化操作,还没有做。

关于这个类的使用场景需要读者自己去研究,我这里仅仅提醒一点,同样的,在作者看来,这个类也是基于框架的扩展,所以也不要在这个类的实现里面做任何的业务逻辑操作,道理和上面的BeanFactoryPostProcessor相同,这时候,你的业务代码中的bean很可能是null,如果需要初始化的一些业务逻辑操作,请使用其他的初始化的手段。

赞(1) 打赏
版权归原创作者所有,任何形式转载请联系作者;码农code之路 » 深入理解Spring IOC之扩展篇(二)、BeanFactoryPostProcessor和BeanPostProcessor

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏