spring boot啟動擴展點整理及實測

spring boot啟動擴展點整理及實測
  1. ApplicationContextInitializer接口initialize方法

    package com.gitee.small.extend;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.context.ApplicationContextInitializer;
    import org.springframework.context.ConfigurableApplicationContext;
    
    @Slf4j
    public class ApplicationContextInitializerExtend implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    
        @Override
        public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
            log.info("spring啟動擴展點1: 容器初始化之前,可用于激活配置文件或動態(tài)注入字節(jié)碼");
        }
    }
    

    由于這個擴展點在spring初始化之前,所以想要擴展類生效需要額外的配置:

    • 啟動類手動加入

      package com.gitee.small;
      
      import com.gitee.small.extend.ApplicationContextInitializerExtend;
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.beans.BeansException;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.boot.ApplicationArguments;
      import org.springframework.boot.SpringApplication;
      import org.springframework.boot.autoconfigure.SpringBootApplication;
      import org.springframework.context.ApplicationContext;
      import org.springframework.context.ApplicationContextAware;
      import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
      import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
      import springfox.documentation.oas.annotations.EnableOpenApi;
      
      @Slf4j
      @SpringBootApplication
      @EnableWebSecurity
      @EnableOpenApi
      @EnableGlobalMethodSecurity(prePostEnabled = true)
      public class Application {
      
      
          public static void main(String[] args) {
              SpringApplication springApplication = new SpringApplication(Application.class);
              springApplication.addInitializers(new ApplicationContextInitializerExtend());
              springApplication.run(args);
          }
      }
      
    • 配置文件配置context.initializer.classes=com.gitee.small.extend.ApplicationContextInitializerExtend

    • 在resources目錄新建/META-INFI/spring.factories文件仿荆,并預(yù)置以下內(nèi)容,即可完成自定義ApplicationContextInitializerExtend的注冊

      org.springframework.context.ApplicationContextInitializer=com.gitee.small.extend.ApplicationContextInitializerExtend
      
  2. BeanDefinitionRegistryPostProcessor接口怎抛,可以動態(tài)注冊自己的beanDefinition

    package com.gitee.small.extend;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
    import org.springframework.stereotype.Component;
    
    @Slf4j
    @Component
    public class BeanDefinitionRegistryPostProcessorExtend implements BeanDefinitionRegistryPostProcessor {
        @Override
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
            log.info("spring啟動擴展點2: spring bean實例化之前,已讀取所有的beanDefinition信息");
        }
    
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
            log.info("spring啟動擴展點3: 在postProcessBeanDefinitionRegistry之后執(zhí)行");
        }
    }
    
  3. BeanFactoryPostProcessor接口,可以修改已注冊的beanDefinition信息

    package com.gitee.small.extend;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    import org.springframework.stereotype.Component;
    
    @Slf4j
    @Component
    public class BeanFactoryPostProcessorExtend implements BeanFactoryPostProcessor {
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
            log.info("spring啟動擴展點4: spring在讀取beanDefinition信息之后, 實例化bean之前, beanDefinition注冊之后");
        }
    }
    
  4. InstantiationAwareBeanPostProcessor接口芽淡,這個擴展點非常有用 抽诉,無論是寫中間件和業(yè)務(wù)中,都能利用這個特性吐绵。比如對實現(xiàn)了某一類接口的bean在各個生命期間進行收集迹淌,或者對某個類型的bean進行統(tǒng)一的設(shè)值等等

    package com.gitee.small.extend;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.PropertyValues;
    import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
    import org.springframework.stereotype.Component;
    
    import java.beans.PropertyDescriptor;
    
    @Slf4j
    @Component
    public class InstantiationAwareBeanPostProcessorExtend implements InstantiationAwareBeanPostProcessor {
    
        @Override
        public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
            if (beanName.equals("userService"))
                log.info("spring啟動擴展點5: 實例化bean之前, 相當(dāng)于new這個bean之前");
            return null;
        }
    
        @Override
        public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
            if (beanName.equals("userService"))
                log.info("spring啟動擴展點6: 實例化bean之后,相當(dāng)于new這個bean之后");
            return true;
        }
    
        @Override
        public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
            if (beanName.equals("userService"))
                log.info("spring啟動擴展點7: bean已經(jīng)實例化完成己单,在屬性注入時階段觸發(fā)唉窃,@Autowired,@Resource等注解原理基于此方法實現(xiàn)");
            return pvs;
        }
    
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            if (beanName.equals("userService"))
                log.info("spring啟動擴展點8: 初始化bean之前,相當(dāng)于把bean注入spring上下文之前");
            return bean;
        }
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            if (beanName.equals("userService"))
                log.info("spring啟動擴展點9: 初始化bean之后纹笼,相當(dāng)于把bean注入spring上下文之后");
            return bean;
        }
    }
    
  5. SmartInstantiationAwareBeanPostProcessor接口

    package com.gitee.small.extend;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Constructor;
    
    @Slf4j
    @Component
    public class SmartInstantiationAwareBeanPostProcessorExtend implements SmartInstantiationAwareBeanPostProcessor {
    
        @Override
        public Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {
            if (beanName.equals("userService"))
                log.info("spring啟動擴展點10: 該觸發(fā)點發(fā)生在postProcessBeforeInstantiation之前,預(yù)測bean類型");
            return null;
        }
    
        @Override
        public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {
            if (beanName.equals("userService"))
                log.info("spring啟動擴展點11: 該觸發(fā)點發(fā)生在postProcessBeforeInstantiation之后纹份,用于確定該bean的構(gòu)造函數(shù)之用,返回的是該bean的所有構(gòu)造函數(shù)列表");
            return null;
        }
    
        @Override
        public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
            if (beanName.equals("userService"))
                log.info("spring啟動擴展點12: 該觸發(fā)點發(fā)生在postProcessAfterInstantiation之后廷痘,當(dāng)有循環(huán)依賴的場景時,ean實例化好之后,為了防止有循環(huán)依賴," +
                        "會提前暴露回調(diào)方法,用于bean實例化的后置處理蔓涧。這個方法就是在提前暴露的回調(diào)方法中觸發(fā)");
            return bean;
        }
    }
    
  6. BeanFactoryAware接口

    package com.gitee.small.extend;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.beans.factory.BeanFactoryAware;
    import org.springframework.stereotype.Component;
    
    @Slf4j
    @Component
    public class BeanFactoryAwareExtend implements BeanFactoryAware {
    
        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            log.info("spring啟動擴展點13: 發(fā)生在bean的實例化之后,注入屬性之前,也就是Setter之前," +
                    "可以在bean實例化之后,但還未初始化之前,拿到 BeanFactory,在這個時候,可以對每個bean作特殊化的定制");
        }
    }
    
  7. ApplicationContextAwareProcessor
    這個類本身沒有提供擴展點笋额,但是其內(nèi)部方法涉及到了六個擴展點元暴,源碼如下

    class ApplicationContextAwareProcessor implements BeanPostProcessor {
    
     private final ConfigurableApplicationContext applicationContext;
    
     private final StringValueResolver embeddedValueResolver;
    
    
     /**
      * Create a new ApplicationContextAwareProcessor for the given context.
      */
     public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) {
         this.applicationContext = applicationContext;
         this.embeddedValueResolver = new EmbeddedValueResolver(applicationContext.getBeanFactory());
     }
    
    
     @Override
     @Nullable
     public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
         if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
                 bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
                 bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)){
             return bean;
         }
    
         AccessControlContext acc = null;
    
         if (System.getSecurityManager() != null) {
             acc = this.applicationContext.getBeanFactory().getAccessControlContext();
         }
    
         if (acc != null) {
             AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                 invokeAwareInterfaces(bean);
                 return null;
             }, acc);
         }
         else {
             invokeAwareInterfaces(bean);
         }
         return bean;
     }
    
     private void invokeAwareInterfaces(Object bean) {
         if (bean instanceof EnvironmentAware) {
             ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
         }
         if (bean instanceof EmbeddedValueResolverAware) {
             ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
         }
         if (bean instanceof ResourceLoaderAware) {
             ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
         }
         if (bean instanceof ApplicationEventPublisherAware) {
             ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
         }
         if (bean instanceof MessageSourceAware) {
             ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
         }
         if (bean instanceof ApplicationContextAware) {
             ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
         }
     }
    }
    
    • EnvironmentAware:用于獲取EnviromentAware的一個擴展類,這個變量非常有用兄猩, 可以獲得系統(tǒng)內(nèi)的所有參數(shù)茉盏。當(dāng)然個人認為這個Aware沒必要去擴展,因為spring內(nèi)部都可以通過注入的方式來直接獲得
    • EmbeddedValueResolverAware:用于獲取StringValueResolver的一個擴展類枢冤, StringValueResolver用于獲取基于String類型的properties的變量鸠姨,一般我們都用@Value的方式去獲取,如果實現(xiàn)了這個Aware接口淹真,把StringValueResolver緩存起來讶迁,通過這個類去獲取String類型的變量,效果是一樣的
    • ResourceLoaderAware:用于獲取ResourceLoader的一個擴展類核蘸,ResourceLoader可以用于獲取classpath內(nèi)所有的資源對象巍糯,可以擴展此類來拿到ResourceLoader對象
    • ApplicationEventPublisherAware:用于獲取ApplicationEventPublisher的一個擴展類,ApplicationEventPublisher可以用來發(fā)布事件值纱,結(jié)合ApplicationListener來共同使用鳞贷,下文在介紹ApplicationListener時會詳細提到。這個對象也可以通過spring注入的方式來獲得
    • MessageSourceAware:用于獲取MessageSource的一個擴展類虐唠,MessageSource主要用來做國際化
    • ApplicationContextAware:用來獲取ApplicationContext的一個擴展類搀愧,ApplicationContext應(yīng)該是很多人非常熟悉的一個類了,就是spring上下文管理器疆偿,可以手動的獲取任何在spring上下文注冊的bean咱筛,我們經(jīng)常擴展這個接口來緩存spring上下文,包裝成靜態(tài)方法杆故。同時ApplicationContext也實現(xiàn)了BeanFactory迅箩,MessageSource,ApplicationEventPublisher等接口处铛,也可以用來做相關(guān)接口的事情
  8. BeanNameAware接口

    package com.gitee.small.extend;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.BeanNameAware;
    import org.springframework.stereotype.Component;
    
    @Slf4j
    @Component
    public class BeanNameAwareExtend implements BeanNameAware {
    
        @Override
        public void setBeanName(String s) {
            log.info("spring啟動擴展點14: 觸發(fā)點在bean的初始化之前,也就是postProcessBeforeInitialization之前," +
                    "用戶可以擴展這個點,在初始化bean之前拿到spring容器中注冊的的beanName,來自行修改這個beanName的值");
        }
    }
    
  9. @PostConstruct注解
    在bean的初始化階段饲趋,如果對一個方法標(biāo)注了@PostConstruct拐揭,會先調(diào)用這個方法,這個觸發(fā)點是在postProcessBeforeInitialization之后奕塑,InitializingBean.afterPropertiesSet之前

  10. SmartInitializingSingleton接口

    package com.gitee.small.extend;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.SmartInitializingSingleton;
    import org.springframework.stereotype.Component;
    
    @Slf4j
    @Component
    public class SmartInitializingSingletonExtend implements SmartInitializingSingleton {
    
    
        @Override
        public void afterSingletonsInstantiated() {
            log.info("spring啟動擴展點16: 在spring容器管理的所有單例對象(非懶加載對象)初始化完成之后調(diào)用的回調(diào)接口堂污。其觸發(fā)時機為postProcessAfterInitialization之后");
        }
    }
    
  11. CommandLineRunner接口

    package com.gitee.small.extend;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.boot.CommandLineRunner;
    import org.springframework.stereotype.Component;
    
    @Slf4j
    @Component
    public class CommandLineRunnerExtend implements CommandLineRunner {
    
        @Override
        public void run(String... args) throws Exception {
            log.info("spring啟動擴展點17: 觸發(fā)時機為整個項目啟動完畢后,自動執(zhí)行龄砰。如果有多個CommandLineRunner盟猖,可以利用@Order來進行排序");
        }
    }
    
  12. DisposableBean接口

    package com.gitee.small.extend;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.DisposableBean;
    import org.springframework.stereotype.Component;
    
    @Slf4j
    @Component
    public class DisposableBeanExtend implements DisposableBean {
        @Override
        public void destroy() throws Exception {
            log.info("spring啟動擴展點18: 觸發(fā)時機為當(dāng)此對象銷毀時,會自動執(zhí)行這個方法");
        }
    }
    

代碼地址:https://gitee.com/smallJ_L/small
參考文章:http://www.reibang.com/p/7fec7b087774

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載换棚,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者式镐。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市固蚤,隨后出現(xiàn)的幾起案子娘汞,更是在濱河造成了極大的恐慌,老刑警劉巖颇蜡,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件价说,死亡現(xiàn)場離奇詭異,居然都是意外死亡风秤,警方通過查閱死者的電腦和手機鳖目,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來缤弦,“玉大人领迈,你說我怎么就攤上這事“澹” “怎么了狸捅?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長累提。 經(jīng)常有香客問我尘喝,道長,這世上最難降的妖魔是什么斋陪? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任朽褪,我火速辦了婚禮,結(jié)果婚禮上无虚,老公的妹妹穿的比我還像新娘缔赠。我一直安慰自己,他們只是感情好友题,可當(dāng)我...
    茶點故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布嗤堰。 她就那樣靜靜地躺著,像睡著了一般度宦。 火紅的嫁衣襯著肌膚如雪踢匣。 梳的紋絲不亂的頭發(fā)上告匠,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天,我揣著相機與錄音离唬,去河邊找鬼凫海。 笑死,一個胖子當(dāng)著我的面吹牛男娄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播漾稀,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼模闲,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了崭捍?” 一聲冷哼從身側(cè)響起尸折,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎殷蛇,沒想到半個月后实夹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡粒梦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年亮航,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片匀们。...
    茶點故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡缴淋,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出泄朴,到底是詐尸還是另有隱情重抖,我是刑警寧澤,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布祖灰,位于F島的核電站钟沛,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏局扶。R本人自食惡果不足惜恨统,卻給世界環(huán)境...
    茶點故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望详民。 院中可真熱鬧延欠,春花似錦、人聲如沸沈跨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽饿凛。三九已至狞玛,卻和暖如春软驰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背心肪。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工锭亏, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人硬鞍。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓慧瘤,卻偏偏與公主長得像,于是被迫代替她去往敵國和親固该。 傳聞我的和親對象是個殘疾皇子锅减,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,573評論 2 359

推薦閱讀更多精彩內(nèi)容