Spring Boot 2.x自定义注入器`java.lang.Object`启动失败排查记录

背景

在最近的一个项目中,同事为了使用@EnableCaching导致工程启动不了,其中报错的是我们公司内部自定义的注入解析器(类似于AutoWired注解)。

分析原因

首先根据堆栈可以得知是空指针了.

Caused by: java.lang.NullPointerException: null
	at com.xxx.rpc.spi.BeanUtils.getAllField(BeanUtils.java:40)
	at com.xxx.rpc.spi.spring.InitRpcAnnotation.initCustomer(InitRpcAnnotation.java:81)
	at com.xxx.rpc.spi.spring.InitRpcAnnotation.postProcessAfterInitialization(InitRpcAnnotation.java:73)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:430)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1798)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:594)

点击进去查看代码,如下所示:

public static List<Field> getAllField(Class<?> clazz) {
    Map<String, Field> map = (Map)FIELD_MAP.get(clazz);
    if (null == map) {
        synchronized(FIELD_MAP) {
            map = (Map)FIELD_MAP.get(clazz);
            if (null == map) {
                map = new HashMap();
                Class c = clazz;

                while(true) {
                    Field[] fields = c.getDeclaredFields();
                    Field[] var5 = fields;
                    int var6 = fields.length;

                    for(int var7 = 0; var7 < var6; ++var7) {
                        Field f = var5[var7];
                        ((Map)map).put(f.getName(), f);
                    }

                    c = c.getSuperclass();
                    if (Object.class.equals(c)) {
                        FIELD_MAP.put(clazz, map);
                        break;
                    }
                }
            }
        }
    }

    return new ArrayList(((Map)map).values());
}

首先理解一下代码的逻辑是,对class去不断向上取superClass,当superClass是Object.class时,就将获取到的字段填入进去。但是在运行时候发现当参数clazzjava.lang.Object时,获取到superClass就是null,然后下次循环时就报了空指针。

java.lang.Object这个bean是正常的吗?

使用新的demo(Spring-boot:2.5)工程进行启动,通过actuator/beans观察的确注册了java.lang.Object的bean,代表的确是正常会产生的。那么这个类究竟是如何注册或者依赖的呢?

  1. 首先进入入口org.springframework.context.support.AbstractApplicationContext#refresh查看哪一步扫描到了java.lang.Object
  2. 通过BeanFactory定位到java.lang.Object是被CacheAuotConfiguration依赖之后导入的。
  3. 通过代码一步一步定位到是org.springframework.context.annotation.ConfigurationClassParser解析出来的。
  4. 因为上一步解析的步骤很复杂,所以转变思路,通过条件断点去定位。通过在org.springframework.context.annotation.ConfigurationClass#ConfigurationClass(org.springframework.core.type.classreading.MetadataReader, org.springframework.context.annotation.ConfigurationClass)打上断点,条件是importedBy.getResource().toString().contains("CacheAutoConfiguration")
  5. 经过上面的定位,发现CacheAutoConfiguration解析出来的依赖org.springframework.boot.autoconfigure.cache.CacheType,会经过org.springframework.context.annotation.ConfigurationClassParser#asSourceClass(java.lang.Class<?>, java.util.function.Predicate<java.lang.String>)的转化,像这种的会经过两个判断,类名路径和条件是否满足的判断,如果满足任何一个就会返回java.lang.Object`

额外扩展 - Spring如何做的自动注入呢?

可以简单看一下时序图,有省略,发现最后使用了RelectionUtils.
在这里插入图片描述

总结

  1. Spring注入器应该兼容异常,之前写解析器的时候也会发现有各种异常的类。一般来说不应该阻断启动。
  2. 定位importedBy的时候可以在ConfigurationClass使用条件断点。
  3. 有关Autowired注解的debug可以在org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor中进行断点。

最后,新年快乐~
祝大家在新的一年里都学有所成,事业进步,心想事成!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值