java.lang.IllegalArgumentException: Value must not be null异常排查

问题

上线新版本,系统一开始运行,系统直接报java.lang.IllegalArgumentException: Value must not be null异常具体的日志打印如下:

java.lang.IllegalArgumentException: Value must not be null
at org.springframework.util.Assert.notNull(Assert.java:198)
at org.springframework.boot.actuate.health.HealthBuilder.withDetail(Health.java:245)atnet.xmeye.vms.actuate.VersionHealthIndicator.health(VersionHealthIndicator.java:26)atorg.springframework.boot.actuate.health.HealthIndicator.getHealth(HealthIndicator.java:37)atorg.springframework.boot.actuate.health.HealthEndpointWebExtension.getHealth(HealthEndpointWebExtension.java:95)atorg.springframework.boot.actuate.health.HealthEndpointWebExtension.getHealth(HealthEndpointWebExtension.java:43)atorg.springframework.boot.actuate.health.HealthEndpointSupport.getContribution(HealthEndpointSupport.java:108)atorg.springframework.boot.actuate.health.HealthEndpointSupport.getAggregateHealth(HealthEndpointSupport.java:119)atorg.springframework.boot.actuate.health.HealthEndpointSupport.getContribution(HealthEndpointSupport.java:105)atorg.springframework.boot.actuate.health.HealthEndpointSupport.getHealth(HealthEndpointSupport.java:83)atorg.springframework.boot.actuate.health.HealthEndpointSupport.getHealth(HealthEndpointSupport.java:70)atorg.springframework.boot.actuate.health.HealthEndpointWebExtension.health(HealthEndpointWebExtension.java:81)atorg.springframework.boot.actuate.health.HealthEndpointWebExtension.health(HealthEndpointWebExtension.java:70)atsun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethod)atsun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)atsun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)atjava.lang.reflect.Method.invoke(Method.java:498)atorg.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:282)atorg.springframework.boot.actuate.endpoint.invoke.reflect.ReflectiveOperationInvoker.invoke(ReflectiveOperationInvoker.java:77)atorg.springframework.boot.actuate.endpoint.annotation.AbstractDiscoveredOperation.invoke(AbstractDiscoveredOperation.java:60)atorg.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMappingBuilder.withDetail(Health.java:245) at net.xmeye.vms.actuate.VersionHealthIndicator.health(VersionHealthIndicator.java:26) at org.springframework.boot.actuate.health.HealthIndicator.getHealth(HealthIndicator.java:37) at org.springframework.boot.actuate.health.HealthEndpointWebExtension.getHealth(HealthEndpointWebExtension.java:95) at org.springframework.boot.actuate.health.HealthEndpointWebExtension.getHealth(HealthEndpointWebExtension.java:43) at org.springframework.boot.actuate.health.HealthEndpointSupport.getContribution(HealthEndpointSupport.java:108) at org.springframework.boot.actuate.health.HealthEndpointSupport.getAggregateHealth(HealthEndpointSupport.java:119) at org.springframework.boot.actuate.health.HealthEndpointSupport.getContribution(HealthEndpointSupport.java:105) at org.springframework.boot.actuate.health.HealthEndpointSupport.getHealth(HealthEndpointSupport.java:83) at org.springframework.boot.actuate.health.HealthEndpointSupport.getHealth(HealthEndpointSupport.java:70) at org.springframework.boot.actuate.health.HealthEndpointWebExtension.health(HealthEndpointWebExtension.java:81) at org.springframework.boot.actuate.health.HealthEndpointWebExtension.health(HealthEndpointWebExtension.java:70) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:282) at org.springframework.boot.actuate.endpoint.invoke.reflect.ReflectiveOperationInvoker.invoke(ReflectiveOperationInvoker.java:77) at org.springframework.boot.actuate.endpoint.annotation.AbstractDiscoveredOperation.invoke(AbstractDiscoveredOperation.java:60) at org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMappingBuilder.withDetail(Health.java:245)atnet.xmeye.vms.actuate.VersionHealthIndicator.health(VersionHealthIndicator.java:26)atorg.springframework.boot.actuate.health.HealthIndicator.getHealth(HealthIndicator.java:37)atorg.springframework.boot.actuate.health.HealthEndpointWebExtension.getHealth(HealthEndpointWebExtension.java:95)atorg.springframework.boot.actuate.health.HealthEndpointWebExtension.getHealth(HealthEndpointWebExtension.java:43)atorg.springframework.boot.actuate.health.HealthEndpointSupport.getContribution(HealthEndpointSupport.java:108)atorg.springframework.boot.actuate.health.HealthEndpointSupport.getAggregateHealth(HealthEndpointSupport.java:119)atorg.springframework.boot.actuate.health.HealthEndpointSupport.getContribution(HealthEndpointSupport.java:105)atorg.springframework.boot.actuate.health.HealthEndpointSupport.getHealth(HealthEndpointSupport.java:83)atorg.springframework.boot.actuate.health.HealthEndpointSupport.getHealth(HealthEndpointSupport.java:70)atorg.springframework.boot.actuate.health.HealthEndpointWebExtension.health(HealthEndpointWebExtension.java:81)atorg.springframework.boot.actuate.health.HealthEndpointWebExtension.health(HealthEndpointWebExtension.java:70)atsun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethod)atsun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)atsun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)atjava.lang.reflect.Method.invoke(Method.java:498)atorg.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:282)atorg.springframework.boot.actuate.endpoint.invoke.reflect.ReflectiveOperationInvoker.invoke(ReflectiveOperationInvoker.java:77)atorg.springframework.boot.actuate.endpoint.annotation.AbstractDiscoveredOperation.invoke(AbstractDiscoveredOperation.java:60)atorg.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMappingServletWebOperationAdapter.handle(AbstractWebMvcEndpointHandlerMapping.java:305)
at org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(AbstractWebMvcEndpointHandlerMapping.java:388)

分析

根据at net.xmeye.vms.actuate.VersionHealthIndicator.health(VersionHealthIndicator.java:26)找到程序中的VersionHealthIndicator类

@Component
public class VersionHealthIndicator implements HealthIndicator, ApplicationRunner {
    private String version;
    private String timestamp;

    @Override
    public Health health() {
        return Health.status(Status.UP)
                .withDetail("package", version)
                .withDetail("buildTime", timestamp)
                .build();
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("VersionHealthIndicator start running");
        try (InputStream inputStream = VersionHealthIndicator.class.getClassLoader()
                .getResourceAsStream("packageinfo.properties")) {
            Properties properties = new Properties();
            properties.load(inputStream);
            version = properties.getProperty("version", "");
            timestamp = properties.getProperty("timestamp", "");
        }
    }
}

在Health.status(Status.UP).withDetail(“package”, version)抛出的异常。

继续看withDetail(“package”, version)方法, 是Assert.notNull(value, “Value must not be null”)抛出的 java.lang.IllegalArgumentException异常,并打印了"Value must not be null"。

public Builder withDetail(String key, Object value) {
   Assert.notNull(key, "Key must not be null");
   Assert.notNull(value, "Value must not be null");
   this.details.put(key, value);
   return this;
}

应该是传入的参数version的值为null。withDetail(“package”, version)方法中version参数是通过实现ApplicationRunner接口加载的。可能的原因是实现ApplicationRunner接口的VersionHealthIndicator类的run方法没有正常运行,或者是读取文件时发生了异常。但是没有报IO相关异常。

debug查看具体的执行过程

调用执行ApplicationRunner.run()、CommandLineRunner.run()的callRunners方法,会将实现了接口的类进行排序。

private void callRunners(ApplicationContext context, ApplicationArguments args) {
   List<Object> runners = new ArrayList<>();
   runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
   runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
   AnnotationAwareOrderComparator.sort(runners);
   for (Object runner : new LinkedHashSet<>(runners)) {
      if (runner instanceof ApplicationRunner) {
         callRunner((ApplicationRunner) runner, args);
      }
      if (runner instanceof CommandLineRunner) {
         callRunner((CommandLineRunner) runner, args);
      }
   }
}

具体的排序如下
在这里插入图片描述
会按照InitDefaultGroupRunner、SmartAdminStartupRunner、RuntimeHealthIndicator、VersionHealthIndicator依次执行。

程序执行完InitDefaultGroupRunner的run方法后,debug进入到执行SmartAdminStartupRunner的run方法,线程一直在执行SmartAdminStartupRunner的run方法。

SmartAdminStartupRunner的run方法执行了响应状态码常量的初始化,以及 aisvrService.startAnalysis()方法。

@Component
public class SmartAdminStartupRunner implements CommandLineRunner {

   @Autowired
   private AisvrService aisvrService;

    @Override
    public void run(String... args) {
        ResponseCodeConst.init();
        aisvrService.startAnalysis();
    }

}

startAnalysis()方法中有一个while()循环,如果alarmMsgAnalysis条件变量为真,线程就会一直运行startAnalysis()方法。

    public void startAnalysis() {
        while (alarmMsgAnalysis) {
           //代码省去
        }
    }

后面的RuntimeHealthIndicator、VersionHealthIndicator的run方法就都不会被执行。version变量的初始化依赖于run()方法的执行,而run()方法没有正常运行,使得version变量没有正常初始化。当执行health()方法时就会产生IllegalArgumentException异常。

问题解决

1、在实现了CommandLineRunner、ApplicationRunner接口的类上添加@Order注解,将SmartAdminStartupRunner的放在最后执行。

2、一些变量的初始化可以通过static代码块来实现。

3、将while()相关的代码开启一个新的线程执行

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐