Spring 的 BeanPostProcessor 原理剖析及使用


一、接口介绍

        spring提供了一个接口类 BeanPostProcessor,我们称其为后置处理器,作用是在 bean 的实例化的过程中对 bean 进行自定义的包装处理,其提供了两个方法。先看看 BeanPostProcessor 的定义。

public interface BeanPostProcessor{ 

        public abstract Object postProcessBeforeInitialization(Object obj, String s) throws BeansException; 

          public abstract Object postProcessAfterInitialization(Object obj, String s) throws BeansException;  

}

        根据类的名称,我们可以猜测两个接口方法执行的位置:

        1、在bean初始化之前执行;

   2、在bean的初始化之后执行。

二、源码解析

        理解其原理以及执行的位置,需要到源码中寻找答案。跟踪创建 bean 的实例的 getBean 方法,层层跟进,在AbstractAutowireCapableBeanFactory 类中,找到了执行方法:

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {

        if (System.getSecurityManager() != null) { 

                AccessController.doPrivileged(new PrivilegedAction<Object>() { 

                        @Override public 

                        Object run() { 

                                 invokeAwareMethods(beanName, bean); 

                                return null; 

                        } 

                }, getAccessControlContext());       

       } else {

             invokeAwareMethods(beanName, bean); 

        }  

        Object wrappedBean = bean;

        if (mbd == null || !mbd.isSynthetic()) { 

                wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); 

        } 

        try {          

                invokeInitMethods(beanName, wrappedBean, mbd);

         } catch (Throwable ex) { 

                throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); 

        } 

         if (mbd == null || !mbd.isSynthetic()) { 

                wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);    

        }      

        return wrappedBean;

}       

        如上加粗部分是主要的代码,invokeAwareMethods(beanName, bean)、 applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);invokeInitMethods(beanName, wrappedBean, mbd)、invokeInitMethods(beanName, wrappedBean, mbd)、applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName)。

        1、invokeAwareMethods(beanName, bean),对实现了 aware 接口的 bean 进行特殊的处理,实现aware 接口的 bean 在被初始化后,可以取得一些相对应的资源。比如, applicationAware , beanFactoryAware 等。

        2applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)后置处理器初始化前方法调用。

        3、invokeInitMethods(beanName, wrappedBean, mbd),用于用户初始化方法的调用,如实现了 InitializingBean 接口的 bean ,调用其 afterPropertiesSet 和 用户配置的 init-method 方法的调用。

        进入applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)和applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName),查看其源码:

public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException { 

        Object result = existingBean; 

        for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) { 

                result = beanProcessor.postProcessBeforeInitialization(result, beanName); 

                if (result == null) { 

                         return result;  

                } 

         }   

         return result;  

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {

        Object result = existingBean;

        for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {

            result = beanProcessor.postProcessAfterInitialization(result, beanName);

            if(result ==null) {

                return result;

            }

        }

        return result;

}

三、使用

1、定义接口和实例 

publi interface DemoService {

    public void sayHello();

}

public interface NameInit { 

     public void setName(String name);  

}

public class DemoServiceImpl implements DemoService,NameInit {

        String name; 

        @Override 

        public void sayHello() { 

                System.out.println("hello "+name); 

        }     

        @Override      

        public void setName(String name) {  

                this.name=name;    

        }

}

2、定义bean的配置

<bean id="demoService" class="com.zjl.beanpostprocessor.DemoServiceImpl"> </bean>

3、定义一个BeanPostProcessor 实例

        凡是继承了NameInit的接口,均实例化,注入name值。此处定义接口一方面是要使用接口中提供的setName方法,另一方面减轻系统压力,防止每个bean都进行注入。

public class NameBeanPostProcessor implements BeanPostProcessor { 

        String name;

        public String getName() { 

                return name;

         } 

        public void setName(String name) { 

                this.name = name; 

        } 

        @Override         

        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 

                if(bean instanceof NameInit){ 

                    ((NameInit)bean).setName(name);

                  }  

                return bean; 

        }      

        @Override 

        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {  

                return bean;    

        }

 }  

5、定义bean,注入name的值

<bean id="nameBeanPostProcessor" class="com.zjl.beanpostprocessor.NameBeanPostProcessor">

        <property name="name" value="zhangsan"></property>  

</bean>

6、定义另一个BeanPostProcessor ,仅打印日志

public class LogBeanPostProcessor implements BeanPostProcessor { 

        @Override                    

         public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 

                System.out.println("正在处理"+beanName); return bean;  

        } 

        @Override      

        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 

                System.out.println("已经处理完成"+beanName);   

                 return bean;

         }   

 } 

7、定义bean

<bean id="logBeanPostProcessor" class="com.zjl.beanpostprocessor.LogBeanPostProcessor"> </bean>

8、测试类

public class BeanPostProcessorTest { 

        public static void main(String[] args) { 

                ApplicationContext context=new FileSystemXmlApplicationContext("beanpostprocessor.xml"); 

                DemoService demoService=(DemoService) context.getBean("demoService"); 

                demoService.sayHello();

         }       

}

9、测试结果

正在处理demoService 

已经处理完成demoService 

hello zhangsan 

10、总结

  两个方法均在bean实例化期间已经完成;

     name属性是根据NameInit接口自动注入;

  由于两个方法执行的时间特殊性,所以打印日志和记录时间意义不大,主要还是用于注入属性和完善配置。

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

推荐阅读更多精彩内容