亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

PostConstruct注解標(biāo)記類ApplicationContext未加載空指針

 更新時間:2022年11月01日 08:50:45   作者:雨夜之寂  
這篇文章主要為大家介紹了@PostConstruct注解標(biāo)記類ApplicationContext未加載空指針示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

今天Code Review的時候 看到其他項目 static 方法需要使用 bean的實體方法,是從網(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)在 代碼運行沒有毛病,但是 我們有公共類SpringUtils 實現(xiàn)了相同功能,其實不應(yīng)該 重復(fù)在業(yè)務(wù)系統(tǒng)自己寫。

但是這個時候 人家可能會問 我這么寫和 用公共類 的效果不是一樣么? 都一樣

區(qū)別

  • 一方面是 代碼規(guī)范,公共功能都有現(xiàn)成的,不需要自己開發(fā),節(jié)省錯誤的概率 和 提升效率

開發(fā)的時候 有人會說 我哪知道有哪些功能是現(xiàn)在有的,關(guān)于這個 我會提供一個搜索的網(wǎng)頁,方便進(jìn)行搜索,如果搜索不到就是沒有,你感覺是公共功能,可以提交 讓別人使用。

你既然給人家推薦用公共類,那你肯定要說清楚 公共類的好處,才能讓人家信服。你不能說效果都一樣,就是用我的吧。。。

講道理

你這種寫法是 可能出錯的

定義一個 Service

@Service
public class TestService{
}

定義 一個初始化方法

@Component
public class TestInit{
    @PostConstruct
    public void init(){
        SpringUtils.getBean(TestService.class);
    }
}

報錯信息

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ù)啟動過程中,spring會先去注冊所有的bean,在注冊過程中,如果發(fā)現(xiàn)該bean中包涵了被@PostConstruct注釋的函數(shù),那么就會先去執(zhí)行這個函數(shù),然后再繼續(xù)注冊其他未注冊的bean。

但是在springUtils中,無論是繼承ApplicationListener,還是繼承自ApplicationContextAware,都只有在bean初始化完成后,才會執(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);
    }
    /**
     * 通過class獲取Bean
     *
     * @param <T>   Bean類型
     * @param clazz Bean類
     * @return Bean對象
     */
    public <T> T getBean(Class<T> clazz) {
        return getBeanFactory().getBean(clazz);
    }
    /**
     * 通過name,以及Clazz返回指定的Bean
     *
     * @param <T>   bean類型
     * @param name  Bean名稱
     * @param clazz bean類型
     * @return Bean對象
     */
    public <T> T getBean(String name, Class<T> clazz) {
        return getBeanFactory().getBean(name, clazz);
    }
    /**
     * 從spring容器中獲取相關(guān)降級的bean
     *
     * @param fallbackClass 降級的Class類對象
     * @param paramValues   參數(shù)值
     * @return 相關(guān)降級的bean
     */
    public Object getBean(Class<?> fallbackClass, Object[] paramValues) {
        return getBeanFactory().getBean(fallbackClass, paramValues);
    }
    /**
     * 通過類型參考返回帶泛型參數(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);
    }
    /**
     * 獲取指定類型對應(yīng)的所有Bean,包括子類
     *
     * @param <T>  Bean類型
     * @param type 類、接口,null表示獲取所有bean
     * @return 類型對應(yīng)的bean,key是bean注冊的name,value是Bean
     * @since 5.3.3
     */
    public <T> Map<String, T> getBeansOfType(Class<T> type) {
        return getBeanFactory().getBeansOfType(type);
    }
    /**
     * 獲取指定類型對應(yīng)的Bean名稱,包括子類
     *
     * @param type 類、接口,null表示獲取所有bean名稱
     * @return bean名稱
     * @since 5.3.3
     */
    public String[] getBeanNamesForType(Class<?> type) {
        return getBeanFactory().getBeanNamesForType(type);
    }
    /**
     * 獲取配置文件配置項的值
     *
     * @param key 配置項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)境配置,無配置返回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)有多個環(huán)境配置時,只獲取第一個
     *
     * @return 當(dāng)前的環(huán)境配置
     * @since 5.3.3
     */
    public String getActiveProfile() {
        final String[] activeProfiles = getActiveProfiles();
        return ArrayUtil.isNotEmpty(activeProfiles) ? activeProfiles[0] : null;
    }
    /**
     * 動態(tài)向Spring注冊Bean
     * <p>
     * 由{@link org.springframework.beans.factory.BeanFactory} 實現(xiàn),通過工具開放API
     * <p>
     * 更新: shadow 2021-07-29 17:20:44 增加自動注入,修復(fù)注冊bean無法反向注入的問題
     *
     * @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);
    }
    /**
     * 注銷bean
     * <p>
     * 將Spring中的bean注銷,請謹(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 為什么能解決這個問題?

@FunctionalInterface
public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory var1) throws BeansException;
}

從注釋可以看出來:

  • BeanFactoryPostProcessor接口允許修改上下文中Bean的定義(definitions),可以調(diào)整Bean的屬性
  • 上下文可以自動檢測BeanFactoryPostProcessor,并且在Bean實例化之前調(diào)用

源碼分析

BeanFactoryPostProcessor是在Bean被實例化之前對Bean的定義信息進(jìn)行修改,那么Spring是如何實現(xiàn)對自定義BeanFactoryPostProcessor的調(diào)用的,下面通過源碼來看一下,首先還是從refresh()方法入手,在refresh()方法中會調(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) {
		/**因代碼太長,省略了***/
		//這里從beanFacoty中通過BeanFactoryPostProcessor類型來獲取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)先級的處理,如果我們有多個自定義的BeanFactoryPostProcessor,可以通過優(yōu)先級來定義執(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.
		//這里先處理實現(xiàn)了PriorityOrdered接口的BeanFactoryPostProcessor,也就是定義了優(yōu)先級的先處理
		sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
		invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
		// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
		//再處理實現(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()方法的邏輯很簡單,就是去遍歷容器中的BeanFactoryPostProcessor,然后調(diào)用postProcessBeanFactory()方法,這個方法就是我們自定義BeanFactoryPostProcessor時需要去實現(xiàn)的方法,至此整個流程就已經(jīng)很清晰了

以上就是PostConstruct注解標(biāo)記類ApplicationContext未加載空指針的詳細(xì)內(nèi)容,更多關(guān)于PostConstruct ApplicationContext的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Knife4j的請求示例當(dāng)中有很多空白行的問題解決辦法

    Knife4j的請求示例當(dāng)中有很多空白行的問題解決辦法

    這篇文章主要介紹了Knife4j的請求示例當(dāng)中有很多空白行的問題解決辦法,按正常來說不應(yīng)該有上方的空白,當(dāng)然如果只是查看我也不至于非要解決他,主要是假如接口是json傳參,調(diào)試界面都沒辦法修改參數(shù),遇到同樣問題的同學(xué)可以參考閱讀本文
    2024-09-09
  • Spring MVC學(xué)習(xí)之DispatcherServlet請求處理詳析

    Spring MVC學(xué)習(xí)之DispatcherServlet請求處理詳析

    這篇文章主要給大家介紹了關(guān)于Spring MVC學(xué)習(xí)教程之DispatcherServlet請求處理的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-11-11
  • Java實現(xiàn)一個簡單計算器

    Java實現(xiàn)一個簡單計算器

    這篇文章主要介紹了Java實現(xiàn)一個簡單計算器,文章我圍繞實現(xiàn)簡單計算器的相關(guān)代碼展現(xiàn)全文,具有一定的參考價值,需要的小伙伴可以參考一下,
    2022-01-01
  • 異常點/離群點檢測算法——LOF解析

    異常點/離群點檢測算法——LOF解析

    這篇文章主要介紹了異常點/離群點檢測算法——LOF解析,通過圖解文字描述的方式詳細(xì)的解析了該算法,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • SpringBoot整合MongoDB流程詳解

    SpringBoot整合MongoDB流程詳解

    這篇文章主要介紹了SpringBoot整合MongoDB流程詳解,MongoDB是一種面向文檔的數(shù)據(jù)庫管理系統(tǒng),它是一個介于關(guān)系型數(shù)據(jù)庫和非關(guān)系型數(shù)據(jù)庫之間的產(chǎn)品,MongoDB支持一種類似JSON的BSON數(shù)據(jù)格式,既可以存儲簡單的數(shù)據(jù)格式,也可以存儲復(fù)雜的數(shù)據(jù)類型,需要的朋友可以參考下
    2024-01-01
  • RocketMQ實現(xiàn)隨緣分BUG小功能示例詳解

    RocketMQ實現(xiàn)隨緣分BUG小功能示例詳解

    這篇文章主要為大家介紹了RocketMQ實現(xiàn)隨緣分BUG小功能示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • SSM框架搭建圖文教程(推薦)

    SSM框架搭建圖文教程(推薦)

    下面小編就為大家?guī)硪黄猄SM框架搭建圖文教程(推薦)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • 實例分析java中重載與重寫的區(qū)別

    實例分析java中重載與重寫的區(qū)別

    這篇文章主要介紹了實例分析java中重載與重寫的區(qū)別,需要的朋友可以參考下
    2014-07-07
  • IDEA自動補全返回值的三種快捷方式

    IDEA自動補全返回值的三種快捷方式

    平常在編碼的過程中,可能需要調(diào)用第三方Api接口,這個過程中可能涉及到不太熟悉第三方Api接口的返回值類型,平常在編碼的過程中,可能需要調(diào)用第三方Api接口,這個過程中可能涉及到不太熟悉第三方Api接口的返回值類型,需要的朋友可以參考下
    2023-10-10
  • SpringCloud中的Hystrix保護機制詳解

    SpringCloud中的Hystrix保護機制詳解

    這篇文章主要介紹了SpringCloud中的Hystrix保護機制詳解,Hystrix,英文意思是豪豬,全身是刺,看起來就不好惹,是一種保護機制,Hystrix也是Netflix公司的一款組件,需要的朋友可以參考下
    2023-12-12

最新評論