PostConstruct注解標(biāo)記類ApplicationContext未加載空指針
序
今天Code Review的時(shí)候 看到其他項(xiàng)目 static 方法需要使用 bean的實(shí)體方法,是從網(wǎng)上copy的 大概是
public class SpringUtils implements ApplicationListener<ApplicationEvent> {
private static ApplicationContext applicationContext;
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent) {
ContextRefreshedEvent e = (ContextRefreshedEvent)event;
if (e.getApplicationContext().getParent() == null) {
applicationContext = e.getApplicationContext();
}
}
}
public static <T> T getBean(Class<T> clazz){
return getApplicationContext().getBean(clazz);
}
}
雖然現(xiàn)在 代碼運(yùn)行沒(méi)有毛病,但是 我們有公共類SpringUtils 實(shí)現(xiàn)了相同功能,其實(shí)不應(yīng)該 重復(fù)在業(yè)務(wù)系統(tǒng)自己寫(xiě)。
但是這個(gè)時(shí)候 人家可能會(huì)問(wèn) 我這么寫(xiě)和 用公共類 的效果不是一樣么? 都一樣
區(qū)別
- 一方面是 代碼規(guī)范,公共功能都有現(xiàn)成的,不需要自己開(kāi)發(fā),節(jié)省錯(cuò)誤的概率 和 提升效率
開(kāi)發(fā)的時(shí)候 有人會(huì)說(shuō) 我哪知道有哪些功能是現(xiàn)在有的,關(guān)于這個(gè) 我會(huì)提供一個(gè)搜索的網(wǎng)頁(yè),方便進(jìn)行搜索,如果搜索不到就是沒(méi)有,你感覺(jué)是公共功能,可以提交 讓別人使用。
你既然給人家推薦用公共類,那你肯定要說(shuō)清楚 公共類的好處,才能讓人家信服。你不能說(shuō)效果都一樣,就是用我的吧。。。
講道理
你這種寫(xiě)法是 可能出錯(cuò)的
定義一個(gè) Service
@Service
public class TestService{
}
定義 一個(gè)初始化方法
@Component
public class TestInit{
@PostConstruct
public void init(){
SpringUtils.getBean(TestService.class);
}
}
報(bào)錯(cuò)信息
Caused by: java.lang.NullPointerException: null
at com.example.demo.utils.SpringUtils.getBean(SpringUtils.java:25) ~[classes/:na]
at com.example.demo.service.TestInit.init(TestInit.java:12) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_322]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_322]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_322]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_322]
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:363) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:307) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:136) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
18 common frames omitted
原因
在spring服務(wù)啟動(dòng)過(guò)程中,spring會(huì)先去注冊(cè)所有的bean,在注冊(cè)過(guò)程中,如果發(fā)現(xiàn)該bean中包涵了被@PostConstruct注釋的函數(shù),那么就會(huì)先去執(zhí)行這個(gè)函數(shù),然后再繼續(xù)注冊(cè)其他未注冊(cè)的bean。
但是在springUtils中,無(wú)論是繼承ApplicationListener,還是繼承自ApplicationContextAware,都只有在bean初始化完成后,才會(huì)執(zhí)行注入applicationContext。
解決
可以直接拿著用
@Component
public class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware {
private static ConfigurableListableBeanFactory beanFactory;
private static ApplicationContext applicationContext;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
SpringUtils.beanFactory = beanFactory;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
SpringUtils.applicationContext = applicationContext;
}
/**
* 獲取{@link ApplicationContext}
*
* @return {@link ApplicationContext}
*/
public ApplicationContext getApplicationContext() {
return applicationContext;
}
public ListableBeanFactory getBeanFactory() {
return null == beanFactory ? applicationContext : beanFactory;
}
public ConfigurableListableBeanFactory getConfigurableBeanFactory() throws UtilException {
final ConfigurableListableBeanFactory factory;
if (null != beanFactory) {
factory = beanFactory;
} else if (applicationContext instanceof ConfigurableApplicationContext) {
factory = ((ConfigurableApplicationContext) applicationContext).getBeanFactory();
} else {
throw new UtilException("No ConfigurableListableBeanFactory from context!");
}
return factory;
}
@SuppressWarnings("unchecked")
public <T> T getBean(String name) {
return (T) getBeanFactory().getBean(name);
}
/**
* 通過(guò)class獲取Bean
*
* @param <T> Bean類型
* @param clazz Bean類
* @return Bean對(duì)象
*/
public <T> T getBean(Class<T> clazz) {
return getBeanFactory().getBean(clazz);
}
/**
* 通過(guò)name,以及Clazz返回指定的Bean
*
* @param <T> bean類型
* @param name Bean名稱
* @param clazz bean類型
* @return Bean對(duì)象
*/
public <T> T getBean(String name, Class<T> clazz) {
return getBeanFactory().getBean(name, clazz);
}
/**
* 從spring容器中獲取相關(guān)降級(jí)的bean
*
* @param fallbackClass 降級(jí)的Class類對(duì)象
* @param paramValues 參數(shù)值
* @return 相關(guān)降級(jí)的bean
*/
public Object getBean(Class<?> fallbackClass, Object[] paramValues) {
return getBeanFactory().getBean(fallbackClass, paramValues);
}
/**
* 通過(guò)類型參考返回帶泛型參數(shù)的Bean
*
* @param reference 類型參考,用于持有轉(zhuǎn)換后的泛型類型
* @param <T> Bean類型
* @return 帶泛型參數(shù)的Bean
* @since 5.4.0
*/
@SuppressWarnings("unchecked")
public <T> T getBean(TypeReference<T> reference) {
final ParameterizedType parameterizedType = (ParameterizedType) reference.getType();
final Class<T> rawType = (Class<T>) parameterizedType.getRawType();
final Class<?>[] genericTypes = Arrays.stream(parameterizedType.getActualTypeArguments()).map(type -> (Class<?>) type).toArray(Class[]::new);
final String[] beanNames = getBeanFactory().getBeanNamesForType(ResolvableType.forClassWithGenerics(rawType, genericTypes));
return getBean(beanNames[0], rawType);
}
/**
* 獲取指定類型對(duì)應(yīng)的所有Bean,包括子類
*
* @param <T> Bean類型
* @param type 類、接口,null表示獲取所有bean
* @return 類型對(duì)應(yīng)的bean,key是bean注冊(cè)的name,value是Bean
* @since 5.3.3
*/
public <T> Map<String, T> getBeansOfType(Class<T> type) {
return getBeanFactory().getBeansOfType(type);
}
/**
* 獲取指定類型對(duì)應(yīng)的Bean名稱,包括子類
*
* @param type 類、接口,null表示獲取所有bean名稱
* @return bean名稱
* @since 5.3.3
*/
public String[] getBeanNamesForType(Class<?> type) {
return getBeanFactory().getBeanNamesForType(type);
}
/**
* 獲取配置文件配置項(xiàng)的值
*
* @param key 配置項(xiàng)key
* @return 屬性值
* @since 5.3.3
*/
public String getProperty(String key) {
if (null == applicationContext) {
return null;
}
return applicationContext.getEnvironment().getProperty(key);
}
/**
* 獲取應(yīng)用程序名稱
*
* @return 應(yīng)用程序名稱
* @since 5.7.12
*/
public String getApplicationName() {
return getProperty("spring.application.name");
}
/**
* 獲取當(dāng)前的環(huán)境配置,無(wú)配置返回null
*
* @return 當(dāng)前的環(huán)境配置
* @since 5.3.3
*/
public static String[] getActiveProfiles() {
if (null == applicationContext) {
return null;
}
return applicationContext.getEnvironment().getActiveProfiles();
}
/**
* 獲取當(dāng)前的環(huán)境配置,當(dāng)有多個(gè)環(huán)境配置時(shí),只獲取第一個(gè)
*
* @return 當(dāng)前的環(huán)境配置
* @since 5.3.3
*/
public String getActiveProfile() {
final String[] activeProfiles = getActiveProfiles();
return ArrayUtil.isNotEmpty(activeProfiles) ? activeProfiles[0] : null;
}
/**
* 動(dòng)態(tài)向Spring注冊(cè)Bean
* <p>
* 由{@link org.springframework.beans.factory.BeanFactory} 實(shí)現(xiàn),通過(guò)工具開(kāi)放API
* <p>
* 更新: shadow 2021-07-29 17:20:44 增加自動(dòng)注入,修復(fù)注冊(cè)bean無(wú)法反向注入的問(wèn)題
*
* @param <T> Bean類型
* @param beanName 名稱
* @param bean bean
* @author shadow
* @since 5.4.2
*/
public <T> void registerBean(String beanName, T bean) {
final ConfigurableListableBeanFactory factory = getConfigurableBeanFactory();
factory.autowireBean(bean);
factory.registerSingleton(beanName, bean);
}
/**
* 注銷(xiāo)bean
* <p>
* 將Spring中的bean注銷(xiāo),請(qǐng)謹(jǐn)慎使用
*
* @param beanName bean名稱
* @author shadow
* @since 5.7.7
*/
public void unregisterBean(String beanName) {
final ConfigurableListableBeanFactory factory = getConfigurableBeanFactory();
if (factory instanceof DefaultSingletonBeanRegistry) {
DefaultSingletonBeanRegistry registry = (DefaultSingletonBeanRegistry) factory;
registry.destroySingleton(beanName);
} else {
throw new UtilException("Can not unregister bean, the factory is not a DefaultSingletonBeanRegistry!");
}
}
/**
* 發(fā)布事件
*
* @param event the event to publish
* @since 5.7.12
*/
public void publishEvent(ApplicationEvent event) {
if (null != applicationContext) {
applicationContext.publishEvent(event);
}
}
}
BeanFactoryPostProcessor 為什么能解決這個(gè)問(wèn)題?
@FunctionalInterface
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory var1) throws BeansException;
}
從注釋可以看出來(lái):
- BeanFactoryPostProcessor接口允許修改上下文中Bean的定義(definitions),可以調(diào)整Bean的屬性
- 上下文可以自動(dòng)檢測(cè)BeanFactoryPostProcessor,并且在Bean實(shí)例化之前調(diào)用
源碼分析
BeanFactoryPostProcessor是在Bean被實(shí)例化之前對(duì)Bean的定義信息進(jìn)行修改,那么Spring是如何實(shí)現(xiàn)對(duì)自定義BeanFactoryPostProcessor的調(diào)用的,下面通過(guò)源碼來(lái)看一下,首先還是從refresh()方法入手,在refresh()方法中會(huì)調(diào)用invokeBeanFactoryPostProcessors(beanFactory);
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
//主要是這一行
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
/**因代碼太長(zhǎng),省略了***/
//這里從beanFacoty中通過(guò)BeanFactoryPostProcessor類型來(lái)獲取Bean名稱,就可以拿到我們自定義的BeanFactoryPostProcessor
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
if (processedBeans.contains(ppName)) {
// skip - already processed in first phase above
}
//這里是優(yōu)先級(jí)的處理,如果我們有多個(gè)自定義的BeanFactoryPostProcessor,可以通過(guò)優(yōu)先級(jí)來(lái)定義執(zhí)行順序
else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}
// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
//這里先處理實(shí)現(xiàn)了PriorityOrdered接口的BeanFactoryPostProcessor,也就是定義了優(yōu)先級(jí)的先處理
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
//再處理實(shí)現(xiàn)了Ordered接口的BeanFactoryPostProcessor
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>();
for (String postProcessorName : orderedPostProcessorNames) {
orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
sortPostProcessors(orderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
// Finally, invoke all other BeanFactoryPostProcessors.
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
for (String postProcessorName : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
//這里才到了處理普通的自定義BeanFactoryPostProcessors
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
// Clear cached merged bean definitions since the post-processors might have
// modified the original metadata, e.g. replacing placeholders in values...
beanFactory.clearMetadataCache();
}
private static void invokeBeanFactoryPostProcessors(
Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
for (BeanFactoryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanFactory(beanFactory);
}
}
invokeBeanFactoryPostProcessors()方法的邏輯很簡(jiǎn)單,就是去遍歷容器中的BeanFactoryPostProcessor,然后調(diào)用postProcessBeanFactory()方法,這個(gè)方法就是我們自定義BeanFactoryPostProcessor時(shí)需要去實(shí)現(xiàn)的方法,至此整個(gè)流程就已經(jīng)很清晰了
以上就是PostConstruct注解標(biāo)記類ApplicationContext未加載空指針的詳細(xì)內(nèi)容,更多關(guān)于PostConstruct ApplicationContext的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- Spring系列中的beanFactory與ApplicationContext
- Spring中BeanFactory和ApplicationContext的作用和區(qū)別(推薦)
- ServletWebServerApplicationContext創(chuàng)建Web容器Tomcat示例
- Spring ApplicationContext上下文核心容器深入探究
- SpringBoot項(xiàng)目報(bào)錯(cuò):"Error?starting?ApplicationContext...."解決辦法
- SpringBoot如何使用applicationContext.xml配置文件
- 基于Failed?to?load?ApplicationContext異常的解決思路
- 一文學(xué)透ApplicationContext繼承接口功能及與BeanFactory區(qū)別
相關(guān)文章
深入探究如何使用Java編寫(xiě)MapReduce程序
MapReduce是一種用于處理大規(guī)模數(shù)據(jù)集的并行編程模型,其特點(diǎn)高效性和可擴(kuò)展性,在本文中,我們將深入了解MapReduce,并使用Java編寫(xiě)一個(gè)簡(jiǎn)單的MapReduce程序,需要的朋友可以參考下2023-05-05
Java文件上傳與文件下載實(shí)現(xiàn)方法詳解
這篇文章主要介紹了Java文件上傳與文件下載實(shí)現(xiàn)方法,結(jié)合實(shí)例形式詳細(xì)分析了Java文件上傳與文件下載相關(guān)操作原理、實(shí)現(xiàn)方法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2019-02-02
在webservice里調(diào)用耗時(shí)方法出錯(cuò)的解決方案
這篇文章主要介紹了在webservice里調(diào)用耗時(shí)方法出錯(cuò)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
Mybatis的一級(jí)緩存和二級(jí)緩存原理分析與使用
mybatis-plus 是一個(gè) Mybatis 的增強(qiáng)工具,在 Mybatis 的基礎(chǔ)上只做增強(qiáng)不做改變,為簡(jiǎn)化開(kāi)發(fā)、提高效率而生,這篇文章帶你了解Mybatis的一級(jí)和二級(jí)緩存2021-11-11
@Valid 校驗(yàn)無(wú)效,BindingResult未獲得錯(cuò)誤的解決
這篇文章主要介紹了@Valid 校驗(yàn)無(wú)效,BindingResult未獲得錯(cuò)誤的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10

