白话文:记一次spring使用内部类bean调用外部类bean造成空指针趟的坑

问题简述

近期在赶一个功能,因为公司对代码规范有了要求,对于一些与前一个版本并行上线的功能,一般推荐策略模式,从而在局部解耦代码逻辑,减少if else.
这一阶段大量使用内部类bean定义临时简单的策略接口,在外部类bean中决定使用哪个策略,然后直接调用相应的接口方法,此前使用若干次未见问题,直到这次出现空指针,使用debug,发现内部类调用外部类方法时,外部类中注入的字段真的是null!

简单还原

首先,随便创建一个由spring管理的service bean,如命名BusinessServiceImpl,其中定义一个方法为deduceFinder,定义一个内部接口Finder和两个实现类AFinder,BFinder
在外部类中定义两个成员Finder aFinder,Finder bFinder,均用autowire注入
在外部类中用autowire注入一个mapper.
外部bean定义一个方法,加上@Transactional注解.方法中调用deduceFinder方法获取一个finder实现bean,再调用finder的find()方法进行下一步的逻辑.
在find方法中调用外部bean的某个方法,该方法使用上述的mapper,此时你会神奇的遇见一个空指针异常,是的,就这么神奇!
而且奇怪的是,我前几天就使用过几乎一样的代码,完全可以用这种内部类bean的方式调用外部类bean的方法,绝对不会出现空指针异常.简单上两个对比图:
出现空指针问题的bean
正常的bean
细心的读者很明显就发现了,不过本人还是反复debug测试了多次,这才注意到正是事务注解@Transactional的原因,内部类bean引用到的外部类bean其实是一个cglib的代理对象,该对象中关键的成员全部是null,接着在内部类bean中调用使用了这些null属性的私有方法,自然会出现空指针.

解决办法

找到问题原因,解决起来就方便了,当然,还是不明白为什么cglib代理不能将属性也一并注入了,总之说一下简单的三种解决办法.
1.解除事务注解,如果业务允许的话,取消了事务就避免了使用cglib,内部类bean调用的外部bean的方法就有相应的非null属性,可以避免空指针.
2.将内部策略接口和两个实现的内部类移出当前类,副作用就是多了三个文件,强迫症慎选.
3.在内部bean调用外部类bean方法时,避免调用私有函数,调用的最上一级函数改为public,且不需要实现接口.
原理说白了就是常见的代理模式设计,一般常见的代理模式只代理公有方法,当我们外部类bean中有方法开启了事务,那么实际工作的外部类bean由cglib生成,该代理对象中,被autowire的字段均是null,这时如果我们调用公有方法,代理对象会直接转交到被代理对象执行,而被代理对象中成功注入了非null的属性bean,故不会报错.
当然,现在很多公司会有sonar代码检查,这种public代码一般是过不了检查的.
效果图:
private遇null坑
public避坑

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值