CGLIB代理Controller后导致空指针异常

Cglib代理Controller后导致空指针异常

首先我们看下没有使用CGLIBController进行代理时代码的样子

原始程序

@SpringBootApplication
public class CglibProxyProblemMain {
    public static void main(String[] args) {
        SpringApplication.run(CglibProxyProblemMain.class, args);
    }
}
@Service
public class TestService {

    public String test(){
        return "test";
    }

    public String test2(){
        return "test2";
    }
}
@RestController
public class TestController {

    @Autowired
    private TestService testService;

    @GetMapping("/test")
    private String test(){
        return testService.test();
    }

    @GetMapping("/test2")
    public String test2(){
        return testService.test2();
    }
}

上面的逻辑很简单,CglibProxyProblemMain 作为Spring的启动入口,TestController中依赖了TestService。然后我们运行一下程序,并测试一下接口是否正常。
在这里插入图片描述
在这里插入图片描述

从curl的结果上看,我们TestController应该是生效了

加入切面

然后,我们引入一个切面,并在TestController的方法执行前先输出一个“before …”

@Component
@Aspect
public class TestAspect {

    @Before("pointCut()")
    public void beforeTest(){
        System.out.println("before ...");
    }

    @Pointcut("execution(public * com..TestController.*(..))")
    public void pointCut() {
        //定义切点
    }
}

然后我们再跑一次程序。服务启动正常:
在这里插入图片描述
接下来再测试一下之前的接口,然后我们的\test接口报错了:
在这里插入图片描述
在这里插入图片描述
神奇的空指针问题就这么出现了…

问题排查

既然是空指针,那我们先调试一下吧:
在这里插入图片描述
从调试的堆栈信息上看,this指向的当前对象是一个被CGLIB代理之后的对象。因为这个类没有接口,同时我们又启用了切面,所以当前对象是一个被CGLIB代理之后的对象感觉也没什么问题。
但是问题是为什么testServicenull呢?难道没有注入成功?那我们换个注入方式吧,使用构造器注入,同时将testService使用final修饰,这样testService应该就不会null了。
修改之后TestController是这样的:

在这里插入图片描述
稳妥起见,我们再跟踪一次服务启动,确认一下testService确实被成功注入了。
在这里插入图片描述
这次应该没问题了,我们再测试一下接口:
在这里插入图片描述
依旧是一个空指针的报错,再调试一次程序,发现testService依旧是null,就像下面这样:
在这里插入图片描述
这就很诡异了…
我们再测试一下/test2这个接口,更加神奇的现象出现了,/test2完全没问题
在这里插入图片描述
难道testService又有了?我们接着调试一下:
在这里插入图片描述
从调试信息中可以看到,在执行test2时,testService确实是存在的,而且this指向并不是被CGLIB代理之后的类。

问题解决

这下就找到问题的原因了

CGLIB无法代理被privatefinal修饰的方法

在这里插入图片描述
由于test方法是private的,无法被CGLIB代理,所以当这个方法被调用时,其实调用的是代理类的同名方法。但是test2由于是public的,可以被正常代理,所以始终都没有问题。

引申

我们再深入一下这个问题,为什么代理类执行test方法时,testServicenull呢?难道没有依赖注入成功吗?
要解决这个问题,首先我们得看下代理是什么时机生成的:
在这里插入图片描述
从调试信息中可以看到,生成代理时,bean已经实例化完毕,bean中的testService已经有值了。但是生成的代理类中的testService却是空的。
在这里插入图片描述
我们都知道CGLIB是基于继承的。也就是生成的代理类是被代理类的子类。但是我们既然写了构造器注入,为什么代理类的testService依旧是null呢?
在这里插入图片描述
原因是CGLIB作弊了,通过CGLIB生成的类的构造器中没有super(...),所以testServicenull了。饶了这么大一圈,其实最后还是因为对CGLIB代理的原理不够清晰才导致了这样的问题,-_-||

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值