当前位置 : 主页 > 编程语言 > java >

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

来源:互联网 收集:自由互联 发布时间:2023-01-30
目录 序 区别 讲道理 你这种写法是 可能出错的 原因 解决 BeanFactoryPostProcessor 为什么能解决这个问题? 源码分析 序 今天Code Review的时候 看到其他项目 static 方法需要使用 bean的实体方法
目录
  • 区别
  • 讲道理
    • 你这种写法是 可能出错的
  • 原因
    • 解决
      • BeanFactoryPostProcessor 为什么能解决这个问题?
        • 源码分析

      今天Code Review的时候 看到其他项目 static 方法需要使用 bean的实体方法,是从网上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);
      	}
      }
      

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

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

      区别

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

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

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

      讲道理

      你这种写法是 可能出错的

      定义一个 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服务启动过程中,spring会先去注册所有的bean,在注册过程中,如果发现该bean中包涵了被@PostConstruct注释的函数,那么就会先去执行这个函数,然后再继续注册其他未注册的bean。

      但是在springUtils中,无论是继承ApplicationListener,还是继承自ApplicationContextAware,都只有在bean初始化完成后,才会执行注入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容器中获取相关降级的bean
           *
           * @param fallbackClass 降级的Class类对象
           * @param paramValues   参数值
           * @return 相关降级的bean
           */
          public Object getBean(Class<?> fallbackClass, Object[] paramValues) {
              return getBeanFactory().getBean(fallbackClass, paramValues);
          }
          /**
           * 通过类型参考返回带泛型参数的Bean
           *
           * @param reference 类型参考,用于持有转换后的泛型类型
           * @param <T>       Bean类型
           * @return 带泛型参数的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);
          }
          /**
           * 获取指定类型对应的所有Bean,包括子类
           *
           * @param <T>  Bean类型
           * @param type 类、接口,null表示获取所有bean
           * @return 类型对应的bean,key是bean注册的name,value是Bean
           * @since 5.3.3
           */
          public <T> Map<String, T> getBeansOfType(Class<T> type) {
              return getBeanFactory().getBeansOfType(type);
          }
          /**
           * 获取指定类型对应的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);
          }
          /**
           * 获取应用程序名称
           *
           * @return 应用程序名称
           * @since 5.7.12
           */
          public String getApplicationName() {
              return getProperty("spring.application.name");
          }
          /**
           * 获取当前的环境配置,无配置返回null
           *
           * @return 当前的环境配置
           * @since 5.3.3
           */
          public static String[] getActiveProfiles() {
              if (null == applicationContext) {
                  return null;
              }
              return applicationContext.getEnvironment().getActiveProfiles();
          }
          /**
           * 获取当前的环境配置,当有多个环境配置时,只获取第一个
           *
           * @return 当前的环境配置
           * @since 5.3.3
           */
          public String getActiveProfile() {
              final String[] activeProfiles = getActiveProfiles();
              return ArrayUtil.isNotEmpty(activeProfiles) ? activeProfiles[0] : null;
          }
          /**
           * 动态向Spring注册Bean
           * <p>
           * 由{@link org.springframework.beans.factory.BeanFactory} 实现,通过工具开放API
           * <p>
           * 更新: shadow 2021-07-29 17:20:44 增加自动注入,修复注册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注销,请谨慎使用
           *
           * @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!");
              }
          }
          /**
           * 发布事件
           *
           * @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),可以调整Bean的属性
      • 上下文可以自动检测BeanFactoryPostProcessor,并且在Bean实例化之前调用

      源码分析

      BeanFactoryPostProcessor是在Bean被实例化之前对Bean的定义信息进行修改,那么Spring是如何实现对自定义BeanFactoryPostProcessor的调用的,下面通过源码来看一下,首先还是从refresh()方法入手,在refresh()方法中会调用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
      			}
      			//这里是优先级的处理,如果我们有多个自定义的BeanFactoryPostProcessor,可以通过优先级来定义执行顺序
      			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.
      		//这里先处理实现了PriorityOrdered接口的BeanFactoryPostProcessor,也就是定义了优先级的先处理
      		sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
      		invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
      		// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
      		//再处理实现了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,然后调用postProcessBeanFactory()方法,这个方法就是我们自定义BeanFactoryPostProcessor时需要去实现的方法,至此整个流程就已经很清晰了

      以上就是PostConstruct注解标记类ApplicationContext未加载空指针的详细内容,更多关于PostConstruct ApplicationContext的资料请关注自由互联其它相关文章!

      网友评论