spring boot啟動擴展點整理及實測
-
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
-
-
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í)行"); } }
-
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注冊之后"); } }
-
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; } }
-
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; } }
-
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作特殊化的定制"); } }
-
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)接口的事情
-
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的值"); } }
@PostConstruct注解
在bean的初始化階段饲趋,如果對一個方法標(biāo)注了@PostConstruct
拐揭,會先調(diào)用這個方法,這個觸發(fā)點是在postProcessBeforeInitialization之后奕塑,InitializingBean.afterPropertiesSet之前-
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之后"); } }
-
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來進行排序"); } }
-
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