美文网首页程序员
记一次springboot项目启动报错的排查方式

记一次springboot项目启动报错的排查方式

作者: 小草莓子桑 | 来源:发表于2020-05-28 20:44 被阅读0次

    最近项目在切换springboot,一个项目开发过程中,发生了springboot项目启动失败,但是日志提醒又很少,今天在这里主要分享下问题排查的过程

    问题,springboot项目启动时候报错

    控制台异常
    • 因为设置了日志没有达到控制台,截图下具体的日志


      具体日志

    分析过程

    • 看到这堆日志刚开始有点懵逼,因为都报了一堆框架的错,一个都没有自己写的类的问题,静下心来强迫自己仔细看了下(日志从下往上看)
    SpringApplication类中容器启动的方法
    SpringApplication中容器启动的方法
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:743)
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:390)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:312)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1214)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1203)
    
    • 先看这段吧,都是SpringApplication类里面,就是容器启动的方法报错
    查看具体的报错方法
    image.png
    @Override
        public void refresh() throws BeansException, IllegalStateException {
            synchronized (this.startupShutdownMonitor) {
                // Prepare this context for refreshing.
                prepareRefresh();
    
                // Tell the subclass to refresh the internal bean factory.
                ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
                // Prepare the bean factory for use in this context.
                prepareBeanFactory(beanFactory);
    
                try {
                    // Allows post-processing of the bean factory in context subclasses.
                    postProcessBeanFactory(beanFactory);
    
                    // Invoke factory processors registered as beans in the context.
                    invokeBeanFactoryPostProcessors(beanFactory);
    
    • 具体定位到了invokeBeanFactoryPostProcessors(beanFactory)方法上了,看注释吧,这个方法,大概是调用工程处理器注册bean对象到容器上下文中,那么我们在看一下这个方法中具体报错的地方
    invokeBeanFactoryPostProcessors方法报错的地方
    这一行
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
    
    • 这一行,第一个入参beanFactory就是spring的bean工厂对象,第二个参数getBeanFactoryPostProcessors()就是就是获取bean工厂的处理类对象,是一个list对象,我们在往上看
    报错日志的at java.util.LinkedHashMap.forEach(LinkedHashMap.java:684)
    • 在往上看就看了一个比较关键的地址:


      map遍历
    • 看日志报错,发现中间有一行map遍历时候报的错,看上下文,我们大致判断是ConfigurationClassBeanDefinitionReader类的loadBeanDefinitionsFromRegistrars方法中,遍历map处理的时候报错的,我们来看下源码
    private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
            registrars.forEach((registrar, metadata) ->
                    registrar.registerBeanDefinitions(metadata, this.registry));
        }
    
    • 这段代码如果没有深入理解过spring源码的话,应该看不懂,其实我第一眼看也比较懵逼,那么我们用我们熟悉的知识看,这样代码就是遍历了Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> 这个map对象,遍历的每一key和他对象的value,registrars.forEach((registrar, metadata) 这块里面的registrar, metadata就是key和value,然后这段registrar.registerBeanDefinitions(metadata, this.registry));,就是遍历出来的key value,用key对象registrar,调用了registrar对象的registerBeanDefinitions方法
    那么我们大致判断出了问题的原因
    • 就是某一次遍历时候出现了错误,虽然我们看不懂这段代码具体是干什么的,但是我们可以在这打个断点,看看这个map里面具体的值,看看能发现问题么


      image.png
      运行结果
    • metadata对象都是这样子的,可以猜测下,就是springboot里面用的config注册类,
    • 一个springboot项目会有很多个这样的类需要注册,这个方法一直在被循环调用
    • 那么循环到一个上停止报错了,就是这个地方有问题,我一直在进行继续的操作
      经过我反复尝试


      报错点

      那么我们继续跟进,就是说这个注册的时候报错了

    EnableConfigurationPropertiesImportSelector类中
    处理类
    • 这个类,看着代码,大致就是处理注册bean工作的,而且看最上层的报错
      java.lang.ClassCastException: java.lang.NoClassDefFoundError cannot be cast to [Ljava.lang.Object;
    • 应该就是某一个属性的依赖jar包没有,导致没有创建成功
    • 我们在这打断点
      最终我们定位到了这里


      image.png

      最低层的错误就是转化成class的报错,因为这个jar包中没有这个类,说明我们jar包的版本错误了


      image.png
    最终我们定位到了问题是spring-boot-autoconfigure这个依赖版本错误了,少了这个类,所以在转化的时候报了java.lang.NoClassDefFoundError: org/springframework/boot/autoconfigure/security/SecurityPrerequisite,但是spring容器这个时候没有做处理,把这个异常直接放到属性map里面了,不知道算不算spring的一个bug。
    • 我们来看下依赖


      spring-boot-autoconfigure的版本
    • 我们用的是2.1.7版本


    • 在来看下实际打包出来的


      2.0.3
    • 实际打包出来是2.0.3,别的boot的版本都是2.1.7


      image.png

      到这里问题基本清楚了,也解决了

    文章里面主要是分享了下排查问题的过程,至于spring源码解读,我也不是很精通,有时间有机会在为大家呈上,欢迎大家来交流,指出文中一些说错的地方,让我加深认识,愿大家没有bug,谢谢!

    相关文章

      网友评论

        本文标题:记一次springboot项目启动报错的排查方式

        本文链接:https://www.haomeiwen.com/subject/nswaahtx.html