[JAVA] PostConstruct注解标记类ApplicationContext未加载空指针

1611 0
黑夜隐士 2022-11-8 17:20:36 | 显示全部楼层 |阅读模式
目录

    序区别讲道理
      你这种写法是 可能出错的
    原因解决BeanFactoryPostProcessor 为什么能解决这个问题?
      源码分析





今天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> T getBean(Class<T> clazz){
  15.                 return getApplicationContext().getBean(clazz);
  16.         }
  17. }
复制代码
虽然现在 代码运行没有毛病,但是 我们有公共类SpringUtils 实现了相同功能,其实不应该 重复在业务系统自己写。
但是这个时候 人家可能会问 我这么写和 用公共类 的效果不是一样么? 都一样

区别

    一方面是 代码规范,公共功能都有现成的,不需要自己开发,节省错误的概率 和 提升效率
开发的时候 有人会说 我哪知道有哪些功能是现在有的,关于这个 我会提供一个搜索的网页,方便进行搜索,如果搜索不到就是没有,你感觉是公共功能,可以提交 让别人使用。
你既然给人家推荐用公共类,那你肯定要说清楚 公共类的好处,才能让人家信服。你不能说效果都一样,就是用我的吧。。。

讲道理


你这种写法是 可能出错的

定义一个 Service
  1. @Service
  2. public class TestService{
  3. }
复制代码
定义 一个初始化方法
  1. @Component
  2. public class TestInit{
  3.     @PostConstruct
  4.     public void init(){
  5.         SpringUtils.getBean(TestService.class);
  6.     }
  7. }
复制代码
报错信息
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服务启动过程中,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 <T>   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 <T>   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 <T>       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 <T>  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.      * <p>
  154.      * 由{@link org.springframework.beans.factory.BeanFactory} 实现,通过工具开放API
  155.      * <p>
  156.      * 更新: shadow 2021-07-29 17:20:44 增加自动注入,修复注册bean无法反向注入的问题
  157.      *
  158.      * @param <T>      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.      * <p>
  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. }
复制代码
BeanFactoryPostProcessor 为什么能解决这个问题?
  1. @FunctionalInterface
  2. public interface BeanFactoryPostProcessor {
  3.     void postProcessBeanFactory(ConfigurableListableBeanFactory var1) throws BeansException;
  4. }
复制代码
从注释可以看出来:
    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.         }
复制代码
invokeBeanFactoryPostProcessors()方法的逻辑很简单,就是去遍历容器中的BeanFactoryPostProcessor,然后调用postProcessBeanFactory()方法,这个方法就是我们自定义BeanFactoryPostProcessor时需要去实现的方法,至此整个流程就已经很清晰了
以上就是PostConstruct注解标记类ApplicationContext未加载空指针的详细内容,更多关于PostConstruct ApplicationContext的资料请关注中国红客联盟其它相关文章!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行