Spring处理器及其相关的使用场景

处理器继承关系

image.png

容器级处理器

BeanFactoryPostProcessor
public interface BeanFactoryPostProcessor {

   /**
    说明:
      该接口方法是在Spring容器解析完配置文件注册了BeanDefinition之后,并在bean被实例化之前被调用的;
      该接口方法定义在spring-bean模块中,但是并没有在IOC层被使用(如果要使用可以手动向BeanFactory注入该处理器),而是
    在ApplicationContext层被调用,这意味着该处理器是用于在ApplicationContext层拓展而被定义的。

    相关的应用:
      1、CustomEditorConfigurer
      配置自定义的属性编辑器时,会配置一个“org.springframework.beans.factory.config.CustomEditorConfigurer”的bean,
      并给这个bean注入自定义的属性编辑器,CustomEditorConfigurer实现了BeanFactoryPostProcessor这个后处理器接口,因此
      Spring会通过该处理器,在解析完后配置文件和实例化bean前,将我们自定义的属性编辑器添加到IOC容器中,这样便可以在后
      面属性注入的时候使用我们自定义的属性编辑器了。
   
      2、PropertyPlaceholderConfigurer
      有时候,我们会在配置文件中使用占位符的方式来配置Bean,Spring在bean注入属性的时候会去解析这些占位符,该解析动作就
      是通过PropertyPlaceholderConfigurer来实现的。PropertyPlaceholderConfigurer实现了BeanFactoryPostProcessor这个后处理器接口,在解析完后配置文件和实例化bean前,Spring会通过该处理器访问每个已经注册到容器的BeanDefinition对象,并替换${...}占位符。
      另外,当我们配置了<property-placeholder>标签,Spring 就会自行注册了一个PropertyPlaceholderConfigurer的Bean,并且该Bean是一个处理器Bean。
   
      3、CustomAutowireConfigurer
      4、CustomScopeConfigurer
      5、DeprecatedBeanWarner
    */
   void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}
BeanDefinitionRegistryPostProcessor
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {

   /**
    * 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.
    * 在标准初始化之后修改应用程序上下文的内部bean定义注册表。所有的常规bean定义都已经加载,但是还没有实例化bean。这允许
    * 在下一个后处理阶段开始之前添加更多bean定义。
    * @param registry the bean definition registry used by the application context
    * @throws org.springframework.beans.BeansException in case of errors

   说明:
      BeanDefinitionRegistryPostProcessor接口继承自BeanFactoryPostProcessor接口,该处理器接口定义在spring-bean模块中,
   但应用于ApplicationContext容器,即该处理器是为ApplicationContext容器扩展而被设计的,BeanFactoryPostProcessor处理器也
   是为扩展而设计的,但是不同的是BeanFactoryPostProcessor可以通过在BeanFactory手工设置该处理器来执行处理器方法,而
   BeanDefinitionRegistryPostProcessor即使在BeanFactory中手工设置也无法被被调用,必须在ApplicationContext中才能被调用;
   该处理器方法的调用时间是在完成 BeanDefinition 注册后,实例化bean之前被调用的,该处理主要用于修改BeanDefinition注册表
   信息,它用于被ApplicationContext调用,在bean注册到ioc后创建实例前修改bean定义和新增bean注册,这个是在context的refresh
   方法调用。BeanDefinitionRegistryPostProcessor 的一个典型应用是扫描指定包及其子包下面拥有指定注解的类,你会发现在
   BeanFactory中并没有使用到该后处理器,该后处理器为Spring容器扩展而设计的,IOC容器只加载一些常规的Bean配置,而像@Service、
    @Repository、@Compent和@Bean等这些注解定义的Bean是ApplicationContext容器中才扩展出来的,其中 
    BeanDefinitionRegistryPostProcessor 有一个典型的应用是Mybatis中的@Mapper。此外,这里要注意的是@Service、@Repository、
    @Compent和@Bean这些注解修饰的Bean并不是通过后处理器来注入的,而是通过自定义命名空间解析器来注入的。

   相关的应用:
      1、MapperScannerConfigurer
      在mybatis集成spring的扩展包中(mybatis-spring-xxx.jar),就是通过MapperScannerConfigurer实现
    BeanDefinitionRegistryPostProcessor接口的postProcessBeanDefinitionRegistry方法来实现扫描@Mapper注解修饰的接口,并向
    BeanDefinition注册表中注册一系列的AnnotatedBeanDefinition对象,这样Spring就可以在后续的启动流程中向IOC容器注册Mapper
    接口对象实例了,从而实现Mybatis与Spring的集成。另外,Mybatis中的那些Mapper接口,会通过动态代理的方式生成一个接口的
    代理实例,从而完成一些持久化操作,这就是为什么Mybatis只需定义Mapper接口而不用实现类的原因;并且通过MapperScannerConfigurer
    注入的AnnotatedBeanDefinition对象,在实例化完成后其Bean对象是一个Mybatis的MapperFactoryBean对象,该MapperFactoryBean
    实现了Spring的FactoryBean接口,然后Spring容器在返回Mapper接口对象Bean的时候,就会通过FactoryBean接口来代理这个Mapper
    接口,该代理操作会委托Mybatis自己来完成。总之,Mybatis集成Spring中的Mapper接口,其本质是一个MapperFactoryBean,
    MapperFactoryBean实现了FactoryBean,所以每个Mapper对象在实例化的时候会调用FactoryBean#getObject()方法,创建一个Mapper
    的实例。
    */
   void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

}

Bean级处理器

BeanPostProcessor
// 该接口作用是:如果我们需要在Spring容器完成Bean的实例化,配置和其他的初始化后添加一些自己的逻辑处理,我们就可以定义一个或者多个BeanPostProcessor接口的实现
public interface BeanPostProcessor {

   /**
    1、BeanPostProcessor#postProcessBeforeInitialization
    2、@PostConstruct修饰的方法
    3、InitializingBean#afterPropertiesSet:设置完Bean的所有属性之后被调用
    4、调用<bean>配置中的init-method方法
    */
   Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

   /**
    1、调用<bean>配置中的init-method方法
    2、BeanPostProcessor#postProcessAfterInitialization:Bean执行初始化方法后被调用
    3、@PreDestroy修饰的方法
    4、DisposableBean#destroy:在bean被销毁的时候调用
    5、调用<bean>配置中的destroy-method方法
    */
   Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}
InstantiationAwareBeanPostProcessor
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {

   /**
    说明:
      在调用bean构造函数实例化前被调用,IOC层在调用Bean构造器实例化之前会先执行该处理器方法,如果该处理器方法返回一个非
      空对象,则IOC容器会中断后续初始化流程,即后续的属性注入也就不再执行了,直接返回该非空对象作为Bean的实例。

    相关的应用:
      1、AbstractAutoProxyCreator
      Spring中的自动代理机制中就是通过该处理器方法来实现的,它通过扩展该处理器方法,在IOC层调用Bean构造器实例化之前会先
      执行该处理器方法,并遍历所有的Bean判断这个Bean是否可以被代理(该实现机制是通过配置一个目标Bean与增强匹配的表达式
      来现实的,如RegexpMethodPointcutAdvisor,并通过该表达式判断每个Bean是否存有对应的增强器,如果存在说明该bean可以被
      自动代理),然后在 InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation 处理器方法中织入增强,并返
      回代理后的代理类,返回代理类后IOC就直接返回该Bean实例了,后续的属性注入则无法再执行了。
    */
   Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException;
   // Spring调用构造器实例化Bean,然后将Bean包装为一个BeanWrapper后,并在所有的配置属性注入到Bean前该处理器方法被调用,
   // 该处理器方法的返回值是一个Boolean值,它可以用来控制是否继续注入Bean属性

   /**
    说明:
       Spring调用构造器实例化Bean,然后将Bean包装为一个BeanWrapper后,并在所有的配置属性注入到Bean前该处理器方法被调用,
      该处理器方法的返回值是一个Boolean值,它可以用来控制是否继续注入Bean属性,在Spring源码中属性注入方法populateBean()
      的执行步骤如下:
       1、执行InstantiationAwareBeanPostProcessor处理器的postProcessAfterInstantiation方法,该函数可以控制程序是否继续
         进行属性填充;
       2、根据注入类型(byName/byType),提取依赖的bean,并统一存入PropertyValues中;
       3、执行InstantiationAwareBeanPostProcessor#postProcessPropertyValues方法,属性获取完毕后,并在将PropertyValues注
         入到Bean前对属性的再次处理,典型应用是requiredAnnotationBeanPostProcessor 类中对属性的验证;
       4、将所有PropertyValues中的属性填充至BeanWrapper中。

    相关的应用:
      。。。
    */
   boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException;

   /**
    说明:
      该处理器方法是在BeanWrapper给Bean注入属性之前被调用的

    相关的应用:
       1、AutowiredAnnotationBeanPostProcessor
       BeanWrapper在将给定的属性值注入到目标Bean之前,Spring会调用AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues
       处理器方法,将所有@Autowired注解修饰的依赖Bean注入到目标Bean,也就说由@Autowired注解修饰的Bean属性最先被注入到Bean中
    
       2、RequiredAnnotationBeanPostProcessor
       如果一个bean某些字段必须含有,则可以使用@Required注释,RequiredAnnotationBeanPostProcessor#postProcessPropertyValues
       在所有属性注入到Bean前,回去检查所有被@Required注解修饰的方法(@Required只能修饰方法),判断是否有对应的属性注入。
       如果任何带有@Required的属性未设置的话 将会抛出BeanInitializationException异常。
    
       3、CommonAnnotationBeanPostProcessor
       CommonAnnotationBeanPostProcessor通过扩展该处理器方法,将那些被@Resource注解修饰的属性注入到Bean
    
       4、PersistenceAnnotationBeanPostProcessor执行@ PersistenceContext等JPA注解的注入
    */
   PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException;

}

SmartInstantiationAwareBeanPostProcessor

public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor {

   /** 在调用{@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation}前预测要返回bean的类型*/
   Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException;

   // 确定一个实例化时要用的构造器方法
   Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException;

   /**
    该方法用于返回早期的Bean引用,即半成品的Bean,已经实例化但是还没有注入属性
    比如:CircularityA引用CircularityB,CircularityB引用CircularityC,CircularityC引用CircularityA
    (1)Spring容器创建单例“circularityA” Bean:首先依据无參构造器创建“circularityA”Bean, 并暴露一个ObjectFactory,
    这个ObjectFactory用于返回提前暴露的circularityA,然后将“circularityA”放到“当前创建的Bean缓存池”中。
    然后进行setter注入“circularityB”;

    (2)Spring容器创建单例“circularityB” Bean:首先依据无參构造器创建“circularityB" Bean,并暴露一个ObjectFactory,
    于返回提前暴露的circularityB。然后将 circularityB 放入“当前创建的Bean缓存池”中,然后进行setter注入 circularityC ;

    (3)Spring容器创建单例“circularityC” Bean:首先依据无參构造器创建“circularityC”Bean,并暴露一个ObjectFactory,
    用于返回提前暴露的circularityC。并将 circularityC 放入“当前创建的Bean缓存池”中, 然后进行setter注入 circularityA ;
    进行注入“circularityA”时因为步骤(1)提前暴露了 circularityA 所以从之前的Cache里面拿BeanA,而不用反复创建。

    (4)最后在依赖注入“circularityB”和“circularityA”也是从catch里面拿提前暴露的bean。 完毕setter注入。

      该方法中,如果入参bean是 circularityA 这个Bean,则在第一次创建circularityA时会返回一个半成品的Bean,已经实例化但
    是还没有注入属性,我们称这个半成品的bean为exposedObject,即早期暴露的Bean。当circularityC创建时,会先注入这个半成品
    beanA,这样就先完成了BeanC的创建,接着会完成BeanC的创建,到最后BeanA时,BeanC已经完成了创建,所以BeanA也就可以顺利完
    成。
      此外,对于“prototype”作用域Bean。Spring容器无法完毕依赖注入,由于“prototype”作用域的Bean,Spring容器不进行缓
    存,因此无法提前暴露一个创建中的Bean。
      还有就是,构造函数循环依赖注入时,也会抛异常。
    */
   Object getEarlyBeanReference(Object bean, String beanName) throws BeansException;

}

MergedBeanDefinitionPostProcessor

public interface MergedBeanDefinitionPostProcessor extends BeanPostProcessor {

   // BeanDefinition 被包装为 BeanWrapper 后,会调用该方法;典型的应用是将自动装配@Autowired注解修饰的属性保存到RootBeanDefinition#externallyManagedConfigMembers方便后续注入到Bean实例
   void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName);

}


该处理在BeanFactory#doCreateBean的运用场景(常规Bean创建的执行步骤)如下:
//步骤一:实例化bean,将 BeanDefinition 转换为 BeanWrapper
// 这里是创建bean的地方,由createBeanInstance方法来完成,根据指定bean使用相应的策略(如:工厂方法、构造函数自动注入、简单初始化)创建实例
instanceWrapper = createBeanInstance(beanName, mbd, args);

//步骤二:MergedBeanDefinitionPostProcessor 的应用
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);

//步骤三:依赖处理(会应用SmartInstantiationAwareBeanPostProcessor处理器)
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
   // 为避免后期循环依赖,bean初始化完成前将创建实例的ObjectFactory加入缓存:
   addSingletonFactory(beanName, new ObjectFactory<Object>() {
      public Object getObject() throws BeansException {
         // 对bean再一次依赖引用,主要应用SmartInstantiationAwareBeanPostProcessor
         return getEarlyBeanReference(beanName, mbd, bean);
      }
   });
}

//步骤四:属性填充,将所有属性填充到bean实例中
populateBean(beanName, mbd, instanceWrapper);


DestructionAwareBeanPostProcessor
public interface DestructionAwareBeanPostProcessor extends BeanPostProcessor {

   // 在bean实例销毁前,将调用这个自定义销毁的回调。
   void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException;
}

补充说明

InstantiationAwareBeanPostProcessor、DestructionAwareBeanPostProcessor这两个分别是Bean在初始化和销毁之前的回调方法
SmartInstantiationAwareBeanPostProcessor这个相当于InstantiationAwareBeanPostProcessor的扩展版本,增加了一个对Bean类型预测的回调,但这个主要是Spring框架内部用的,用户还是用InstantiationAwareBeanPostProcessor即可
MergedBeanDefinitionPostProcessor则是在合并处理Bean定义的时候的回调。这个东东按我的理解也基本是框架内部使用的,用户不用管

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容