• @PostConstruct 注解标记的类中,由于ApplicationContext还未加载,导致空指针


    今天Code Review的时候 看到其他项目 static 方法需要使用 bean的实体方法,是从网上copy的 大概是

    1. public class SpringUtils implements ApplicationListener<ApplicationEvent> {
    2. private static ApplicationContext applicationContext;
    3. public static ApplicationContext getApplicationContext() {
    4. return applicationContext;
    5. }
    6. public void onApplicationEvent(ApplicationEvent event) {
    7. if (event instanceof ContextRefreshedEvent) {
    8. ContextRefreshedEvent e = (ContextRefreshedEvent)event;
    9. if (e.getApplicationContext().getParent() == null) {
    10. applicationContext = e.getApplicationContext();
    11. }
    12. }
    13. }
    14. public static T getBean(Class clazz){
    15. return getApplicationContext().getBean(clazz);
    16. }
    17. }
    18. 复制代码

    虽然现在 代码运行没有毛病,但是 我们有公共类SpringUtils 实现了相同功能,其实不应该 重复在业务系统自己写。

    但是这个时候 人家可能会问 我这么写和 用公共类 的效果不是一样么? 都一样

    区别

    1. 一方面是 代码规范,公共功能都有现成的,不需要自己开发,节省错误的概率 和 提升效率

    开发的时候 有人会说 我哪知道有哪些功能是现在有的,关于这个 我会提供一个搜索的网页,方便进行搜索,如果搜索不到就是没有,你感觉是公共功能,可以提交 让别人使用。

    说回来

    你既然给人家推荐用公共类,那你肯定要说清楚 公共类的好处,才能让人家信服。你不能说效果都一样,就是用我的吧。。。

    讲道理

    你这种写法是 可能出错的

    定义一个 Service

    1. @Service
    2. public class TestService{
    3. }
    4. 复制代码

    定义 一个初始化方法

    1. @Component
    2. public class TestInit{
    3. @PostConstruct
    4. public void init(){
    5. SpringUtils.getBean(TestService.class);
    6. }
    7. }
    8. 复制代码

    报错信息

    1. Caused by: java.lang.NullPointerException: null
    2. at com.example.demo.utils.SpringUtils.getBean(SpringUtils.java:25) ~[classes/:na]
    3. at com.example.demo.service.TestInit.init(TestInit.java:12) ~[classes/:na]
    4. at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_322]
    5. at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_322]
    6. at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_322]
    7. at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_322]
    8. at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:363) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    9. at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:307) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    10. at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:136) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
    11. 18 common frames omitted
    12. 复制代码

    原因

    在spring服务启动过程中,spring会先去注册所有的bean,在注册过程中,如果发现该bean中包涵了被@PostConstruct注释的函数,那么就会先去执行这个函数,然后再继续注册其他未注册的bean。但是在springUtils中,无论是继承ApplicationListener,还是继承自ApplicationContextAware,都只有在bean初始化完成后,才会执行注入applicationContext。

    解决

    可以直接拿着用

    1. @Component
    2. public class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware {
    3. private static ConfigurableListableBeanFactory beanFactory;
    4. private static ApplicationContext applicationContext;
    5. @Override
    6. public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    7. SpringUtils.beanFactory = beanFactory;
    8. }
    9. @Override
    10. public void setApplicationContext(ApplicationContext applicationContext) {
    11. SpringUtils.applicationContext = applicationContext;
    12. }
    13. /**
    14. * 获取{@link ApplicationContext}
    15. *
    16. * @return {@link ApplicationContext}
    17. */
    18. public ApplicationContext getApplicationContext() {
    19. return applicationContext;
    20. }
    21. public ListableBeanFactory getBeanFactory() {
    22. return null == beanFactory ? applicationContext : beanFactory;
    23. }
    24. public ConfigurableListableBeanFactory getConfigurableBeanFactory() throws UtilException {
    25. final ConfigurableListableBeanFactory factory;
    26. if (null != beanFactory) {
    27. factory = beanFactory;
    28. } else if (applicationContext instanceof ConfigurableApplicationContext) {
    29. factory = ((ConfigurableApplicationContext) applicationContext).getBeanFactory();
    30. } else {
    31. throw new UtilException("No ConfigurableListableBeanFactory from context!");
    32. }
    33. return factory;
    34. }
    35. @SuppressWarnings("unchecked")
    36. public <T> T getBean(String name) {
    37. return (T) getBeanFactory().getBean(name);
    38. }
    39. /**
    40. * 通过class获取Bean
    41. *
    42. * @param Bean类型
    43. * @param clazz Bean类
    44. * @return Bean对象
    45. */
    46. public <T> T getBean(Class<T> clazz) {
    47. return getBeanFactory().getBean(clazz);
    48. }
    49. /**
    50. * 通过name,以及Clazz返回指定的Bean
    51. *
    52. * @param bean类型
    53. * @param name Bean名称
    54. * @param clazz bean类型
    55. * @return Bean对象
    56. */
    57. public <T> T getBean(String name, Class<T> clazz) {
    58. return getBeanFactory().getBean(name, clazz);
    59. }
    60. /**
    61. * 从spring容器中获取相关降级的bean
    62. *
    63. * @param fallbackClass 降级的Class类对象
    64. * @param paramValues 参数值
    65. * @return 相关降级的bean
    66. */
    67. public Object getBean(Class<?> fallbackClass, Object[] paramValues) {
    68. return getBeanFactory().getBean(fallbackClass, paramValues);
    69. }
    70. /**
    71. * 通过类型参考返回带泛型参数的Bean
    72. *
    73. * @param reference 类型参考,用于持有转换后的泛型类型
    74. * @param Bean类型
    75. * @return 带泛型参数的Bean
    76. * @since 5.4.0
    77. */
    78. @SuppressWarnings("unchecked")
    79. public <T> T getBean(TypeReference<T> reference) {
    80. final ParameterizedType parameterizedType = (ParameterizedType) reference.getType();
    81. final Class<T> rawType = (Class<T>) parameterizedType.getRawType();
    82. final Class<?>[] genericTypes = Arrays.stream(parameterizedType.getActualTypeArguments()).map(type -> (Class<?>) type).toArray(Class[]::new);
    83. final String[] beanNames = getBeanFactory().getBeanNamesForType(ResolvableType.forClassWithGenerics(rawType, genericTypes));
    84. return getBean(beanNames[0], rawType);
    85. }
    86. /**
    87. * 获取指定类型对应的所有Bean,包括子类
    88. *
    89. * @param Bean类型
    90. * @param type 类、接口,null表示获取所有bean
    91. * @return 类型对应的bean,key是bean注册的name,value是Bean
    92. * @since 5.3.3
    93. */
    94. public <T> Map<String, T> getBeansOfType(Class<T> type) {
    95. return getBeanFactory().getBeansOfType(type);
    96. }
    97. /**
    98. * 获取指定类型对应的Bean名称,包括子类
    99. *
    100. * @param type 类、接口,null表示获取所有bean名称
    101. * @return bean名称
    102. * @since 5.3.3
    103. */
    104. public String[] getBeanNamesForType(Class<?> type) {
    105. return getBeanFactory().getBeanNamesForType(type);
    106. }
    107. /**
    108. * 获取配置文件配置项的值
    109. *
    110. * @param key 配置项key
    111. * @return 属性值
    112. * @since 5.3.3
    113. */
    114. public String getProperty(String key) {
    115. if (null == applicationContext) {
    116. return null;
    117. }
    118. return applicationContext.getEnvironment().getProperty(key);
    119. }
    120. /**
    121. * 获取应用程序名称
    122. *
    123. * @return 应用程序名称
    124. * @since 5.7.12
    125. */
    126. public String getApplicationName() {
    127. return getProperty("spring.application.name");
    128. }
    129. /**
    130. * 获取当前的环境配置,无配置返回null
    131. *
    132. * @return 当前的环境配置
    133. * @since 5.3.3
    134. */
    135. public static String[] getActiveProfiles() {
    136. if (null == applicationContext) {
    137. return null;
    138. }
    139. return applicationContext.getEnvironment().getActiveProfiles();
    140. }
    141. /**
    142. * 获取当前的环境配置,当有多个环境配置时,只获取第一个
    143. *
    144. * @return 当前的环境配置
    145. * @since 5.3.3
    146. */
    147. public String getActiveProfile() {
    148. final String[] activeProfiles = getActiveProfiles();
    149. return ArrayUtil.isNotEmpty(activeProfiles) ? activeProfiles[0] : null;
    150. }
    151. /**
    152. * 动态向Spring注册Bean
    153. *

    154. * 由{@link org.springframework.beans.factory.BeanFactory} 实现,通过工具开放API
    155. *

    156. * 更新: shadow 2021-07-29 17:20:44 增加自动注入,修复注册bean无法反向注入的问题
    157. *
    158. * @param Bean类型
    159. * @param beanName 名称
    160. * @param bean bean
    161. * @author shadow
    162. * @since 5.4.2
    163. */
    164. public <T> void registerBean(String beanName, T bean) {
    165. final ConfigurableListableBeanFactory factory = getConfigurableBeanFactory();
    166. factory.autowireBean(bean);
    167. factory.registerSingleton(beanName, bean);
    168. }
    169. /**
    170. * 注销bean
    171. *

    172. * 将Spring中的bean注销,请谨慎使用
    173. *
    174. * @param beanName bean名称
    175. * @author shadow
    176. * @since 5.7.7
    177. */
    178. public void unregisterBean(String beanName) {
    179. final ConfigurableListableBeanFactory factory = getConfigurableBeanFactory();
    180. if (factory instanceof DefaultSingletonBeanRegistry) {
    181. DefaultSingletonBeanRegistry registry = (DefaultSingletonBeanRegistry) factory;
    182. registry.destroySingleton(beanName);
    183. } else {
    184. throw new UtilException("Can not unregister bean, the factory is not a DefaultSingletonBeanRegistry!");
    185. }
    186. }
    187. /**
    188. * 发布事件
    189. *
    190. * @param event the event to publish
    191. * @since 5.7.12
    192. */
    193. public void publishEvent(ApplicationEvent event) {
    194. if (null != applicationContext) {
    195. applicationContext.publishEvent(event);
    196. }
    197. }
    198. }
    199. 复制代码

    BeanFactoryPostProcessor 为什么能解决这个问题?

    1. @FunctionalInterface
    2. public interface BeanFactoryPostProcessor {
    3. void postProcessBeanFactory(ConfigurableListableBeanFactory var1) throws BeansException;
    4. }
    5. 复制代码

    从注释可以看出来:

    • BeanFactoryPostProcessor接口允许修改上下文中Bean的定义(definitions),可以调整Bean的属性
    • 上下文可以自动检测BeanFactoryPostProcessor,并且在Bean实例化之前调用

    源码分析

    BeanFactoryPostProcessor是在Bean被实例化之前对Bean的定义信息进行修改,那么Spring是如何实现对自定义BeanFactoryPostProcessor的调用的,下面通过源码来看一下,首先还是从refresh()方法入手,在refresh()方法中会调用invokeBeanFactoryPostProcessors(beanFactory);

    1. protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    2. //主要是这一行
    3. PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
    4. // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
    5. // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
    6. if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
    7. beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
    8. beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    9. }
    10. }
    11. public static void invokeBeanFactoryPostProcessors(
    12. ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
    13. /**因代码太长,省略了***/
    14. //这里从beanFacoty中通过BeanFactoryPostProcessor类型来获取Bean名称,就可以拿到我们自定义的BeanFactoryPostProcessor
    15. String[] postProcessorNames =
    16. beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
    17. List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
    18. List<String> orderedPostProcessorNames = new ArrayList<>();
    19. List<String> nonOrderedPostProcessorNames = new ArrayList<>();
    20. for (String ppName : postProcessorNames) {
    21. if (processedBeans.contains(ppName)) {
    22. // skip - already processed in first phase above
    23. }
    24. //这里是优先级的处理,如果我们有多个自定义的BeanFactoryPostProcessor,可以通过优先级来定义执行顺序
    25. else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
    26. priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
    27. }
    28. else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
    29. orderedPostProcessorNames.add(ppName);
    30. }
    31. else {
    32. nonOrderedPostProcessorNames.add(ppName);
    33. }
    34. }
    35. // First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
    36. //这里先处理实现了PriorityOrdered接口的BeanFactoryPostProcessor,也就是定义了优先级的先处理
    37. sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
    38. invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
    39. // Next, invoke the BeanFactoryPostProcessors that implement Ordered.
    40. //再处理实现了Ordered接口的BeanFactoryPostProcessor
    41. List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>();
    42. for (String postProcessorName : orderedPostProcessorNames) {
    43. orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
    44. }
    45. sortPostProcessors(orderedPostProcessors, beanFactory);
    46. invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
    47. // Finally, invoke all other BeanFactoryPostProcessors.
    48. List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
    49. for (String postProcessorName : nonOrderedPostProcessorNames) {
    50. nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
    51. }
    52. //这里才到了处理普通的自定义BeanFactoryPostProcessors
    53. invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
    54. // Clear cached merged bean definitions since the post-processors might have
    55. // modified the original metadata, e.g. replacing placeholders in values...
    56. beanFactory.clearMetadataCache();
    57. }
    58. private static void invokeBeanFactoryPostProcessors(
    59. Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
    60. for (BeanFactoryPostProcessor postProcessor : postProcessors) {
    61. postProcessor.postProcessBeanFactory(beanFactory);
    62. }
    63. }
    64. 复制代码

    invokeBeanFactoryPostProcessors()方法的逻辑很简单,就是去遍历容器中的BeanFactoryPostProcessor,然后调用postProcessBeanFactory()方法,这个方法就是我们自定义BeanFactoryPostProcessor时需要去实现的方法,至此整个流程就已经很清晰了

  • 相关阅读:
    分布式消息队列RocketMQ详细下载安装教程
    基于FPGA的图像白平衡算法实现,包括tb测试文件和MATLAB辅助验证
    (附源码)springboot幼儿园管理系统 毕业设计 160901
    Windows Server 2012 R2 安装 OpenSSH
    2022 GCPC--C. Chaotic Construction
    YOLO改进系列之注意力机制(CoTAttention模型介绍)
    终于明白:有了线程,为什么还要有协程?
    【JAVA刷题初阶】刷爆力扣第十一弹——二叉树
    Java基础知识面试题
    k8s中的Controller
  • 原文地址:https://blog.csdn.net/m0_73311735/article/details/127702624