Spring 的扩展点

1,566 阅读7分钟

作者:零星。(微信公众号ID:Fchen),欢迎分享,转载请保留出处。

  说起 Spring 的扩展点,大家都会想到 BeanPostProcessorBeanFactoryPostProcessor 这两个处理器,并给他们起名对象的后置处理器和工厂的后置处理器。

  对于这两个后置处理器相关的分析,其实在前面的文章中都已经或多或少的有提及了。但是,没有办法,这两后置处理器确实在 Spring 中有着非常重要的地位。你或许会说,我平常在开发的时候好像没有自己去写类似于这样的扩展点,去实现里面的方法,来插手 Bean 工厂初始化、影响 Bean的生命周期,等等。但是在 Spring 的内部,我们所用到的注解,基本上都是通过这两个扩展点来完成的。

  不知道你是否还记得 @Configuration 这个注解的处理类 ConfigurationClassPostProcessor。可以这么说,纵观整个 Spring 对于 BeanFactoryPostProcessor 实现的类中,这个是最最最重要的。应为有了这个类,我们自定义的被 @Configuration 注解的类才得以被处理。我们定义的对象,才有机会被加入到 IoC 容器中。

  不知道你是否还记得 @Autowired 注解的处理类 AutowiredAnnotationBeanPostProcessor。在这个类中,对被注入的对象做了依赖注入,完成了对象之间依赖关系的建立。当然还有其他的注解的处理,基本上都是 BeanPostProcessor 的子类。

  今天这篇文章我试着对比一下这两个扩展点。

1.相同点

1.1设计思想

  首先 BeanFactoryPostProcessorBeanPostProcessor 都是以接口的形式定义了方法,供子类实现,来完成自己想要做的情。

  BeanPostProcessorSpring 框架提供的一个类的扩展点,称之为 bean的后置处理器。通过实现 BeanPostProcessor 接口,可以干预 bean 的初始化过程,从而减轻了 BeanFactory 负担。

public interface BeanPostProcessor {

	/**
	 * 在bean的初始化之前执行
	 */
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	/**
	 * 在bean的初始化之后执行
	 */
	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}

   BeanFactoryPostProcessor 用来插手 BeanFactory的初始化。影响容器的形成。以 ConfigurationClassPostProcessor 为例,在该子类的实现中,完成了对 @Configuration注解类的解析。

public interface BeanFactoryPostProcessor {

	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

  在 ConfigurationClassPostProcessor 类中通过实现上述的方法,完成了对配置类的动态代理。

1.2 在容器中的存储方式

  由于 BeanFactoryPostProcessorBeanPostProcessor 都是已接口的方式被定义在容器中,因此,在 IoC 容器中,定义了两个集合来存放。

private final List<BeanPostProcessor> beanPostProcessors = new CopyOnWriteArrayList<>();

  通过 beanPostProcessors 来存放 BeanPostProcessor 的子类。当你想在容器中获取这个集合时,通过下面的方法便可得到:

public List<BeanPostProcessor> getBeanPostProcessors() {
		return this.beanPostProcessors;
	}

  对于 BeanFactoryPostProcessor 的子类也是类似的,在 Spring 中将其存放在:beanFactoryPostProcessors 中。

private final List<BeanFactoryPostProcessor> beanFactoryPostProcessors = new ArrayList<>();

  通过 getBeanFactoryPostProcessors() 方法来获取:

public List<BeanFactoryPostProcessor> getBeanFactoryPostProcessors() {
      return this.beanFactoryPostProcessors;
  }

  这里这样说,应该是不太严谨的,因为 getBeanFactoryPostProcessors() 获取到的是程序员自己定义且没有交给 Spring 管理的 BeanFactoryPostProcessor 的子类。这里就不在赘述了,可以查看:invokeBeanFactoryPostProcessors()方法的处理里面有详细的解释。

1.3 子类的处理方式

  对于实现了这两个类的子类,在容器中会存在一批。对于每个子类的处理,都是通过 for 循环这一批子类,来执行每个子类所对应的方法。

BeanFactoryPostProcessor的子类的处理方式

BeanPostProcessor子类的处理方式

  可以看出,这两个类子类的处理方式都是相同的处理方式,在容器初始化的时候,将他们添加到对应的变量中存起来。后面要用到的时候拿出来,然后在循环处理。

2.不同点

2.1 面向的对象不同

  见名知义,这两个对象,一个是面对 BeanFactory 搞事情的。一个是面对 Bean 搞事情的?是不是觉得有点抽象?(试着去感受他,而不是理解他,我承认我被《信条》带偏了,原谅我智商不够,我没看懂。需要补的知识点有点多。。。还是学习吧!)没事,这个我想我应该还是可以解释清楚的。

  要分析 BeanFactoryPostProcessor 是面对 BeanFactory 的。这个还是要存容器的 refresh() 方法说起了。在执行 refresh() 方法之前,我们自己定义的对象注册的 BeanDefinitionMap 中的只有一个,那就是我们的配置类。在完成 invokeBeanFactoryPostProcessors(beanFactory) 后,容器中的 BeanDefinitionMap 包含了我们开发中自己定义的需要交给 Spring 管理的对象。这写处理都是在ConfigurationClassPostProcessor类中来完成的。这样才使得我们的容器中的内容更加丰富了。当你的容器中有了 BD(BeanDefinition)才为后面有关 getBea() 的操作提供了可能。然后还要指出一点,对于加了 @Configuration的类,在其 BD 中会被打标为 Full 模式,最后通过 CGLIB 生成动态代理的对象。

  要分析 BeanPostProcessor 是面对 Bean 的。这个我选一个在开发中经用到的注解 @Autowired 来说起。@Autowired 的设计,就是用来完成对象的属性注入的。对与对象之间依赖关系的处理是在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) 这个方法中来处理的。在这个方法中有一段代码上面图片(BeanPostProcessor子类的处理方式)所示,判断后置处理器的类型然后去执行方法。

  在 AutowiredAnnotationBeanPostProcessor 中有通过如下一段代码开始了对象之间的依赖注入,其中 findAutowiringMetadata() 获取到当前对象中所有被注入的属性,然后做了相应的处理。这里不在赘述依赖注入的过程,感兴趣请查看Spring 注入对象处理过程

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    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;
}

  通过上述的分析,应该可以理解 BeanFactoryPostProcessor 是面对 BeanFactory 搞事;BeanPostProcessor 是面对 Bean 搞事情的吧?

2.2 通过代码来理解一下

  如果还是不太理解上面所说面向对象的不同,我们通过下面的例子来看一下,首先我们自定义一个 BeanFactoryPostProcessor 的实现类,在期子类实现中,我们通过 getBean() 来获取对象,然后调用对象的 test() 方法。

@Component
public class MyFactoryPostProcessor implements BeanFactoryPostProcessor {

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		int count = beanFactory.getBeanDefinitionCount();
		String[] names = beanFactory.getBeanDefinitionNames();
		System.out.println("当前BeanFactory中有"+count+" 个Bean");
		System.out.println(Arrays.asList(names));
		MyService bean = beanFactory.getBean(MyService.class);
		System.out.println(bean);
		bean.test();
	}
}

  定义两个类,MyServiceMyTest。起中讲 MyTest 注入给 MyService。然后调用 MyTesttest 方法。

@Service
public class MyService {
	@Autowired
	MyTest myTest;

	public void test(){
		System.out.println("hello test");
		myTest.test();
	}
}

  代码如上所述,你猜运行结果是什么?调试运行结果如下:

示例结果

  我将上述的结果分成了三部分,第一部分说明 BeanDefinitionMap中已经包含了我们容器中所有的对象,因为对应的 @Configuration 注解的类被处理完了。

  第二部分,在这里 getBean() 可以触发 Bean 的实例化等一系列的操作。

  第三部分,抛出空指针,说明依赖的对象没有注入进来。纠其原因,说明在这个时候,BeanFactoryPostProcessor 的子类已经插手了容器的初始化,影响到了容器中的对象。但是对象的后置处理器还没有添加到容器中。前面说了 @Autowired 是通过对象的后置处理器来完成属性注入的,导致对应的属性没有被注入到对象中。

  综上所述我想:BeanFactoryPostProcessor 是面对 BeanFactory 搞事;BeanPostProcessor 是面对 Bean 搞事情。 这个我应该解释清楚了吧。

3.总结

  这篇文章对比分析了 Spring 的扩展点 BeanFactoryPostProcessorBeanPostProcessor 的异同。通篇就是为了说明一个事情:BeanFactoryPostProcessor 是面对 BeanFactory 搞事;BeanPostProcessor 是面对 Bean 搞事情。 当然,在 BeanFactoryPostProcessor 也可完成对对象属性的设置。这里就不在做更详细的区分了。