一般常见的NPE,大部分是由于对象为null,调用空对象其中的方法或者属性时,造成NPE,但是在Spring中如果不注意,private方法也会造成NPE
原因分析
在Spring中,@Transaction
注解用来保证事务,保证数据的ACDI原则,一般在多个数据的操作时,会通过事务来保证数据的一致性,防止出现脏数据,但是在Spring中,事务也会出现失效的场景,例如自身方法调用
自身方法调用,如果是通过this或者直接调用,则没有走Spring的AOP代理,所以
@Transaction
注解不能生效,因为注解是通过代理类在调用目标方法时,增加的事务,如果无法通过代理类调用,则相当于没有事务
-
解决方法
一般在自身方法调用时,通过
@Autowired
注解将自身注入,然后通过注入的代理类调用自身的方法来保证自身方法调用时可以通过代理类调用 -
NPE场景
@Autowired private BloggerMessageService self; public void save(BloggerMessage bloggerMessage) { bloggerMessage.setStatus(NOT_READ); bloggerMessage.setCreateDate(new Date()); bloggerMessageDao.save(bloggerMessage); // 通过代理类调用自身方法 self.update(bloggerMessage.getId()); } @Transactional private void update(Integer id) { User user = userService.findById(id); bloggerMessageDao.updateById(id, user); userService.update(id); }
-
在调用方法时,总是出现
NullPointerException
,经过Debug,发现在update
方法中,所有的通过@Autowired
注入的bean都为null
,所以导致空指针 -
原因分析
经过排查,发现上面的方法可以正常执行,但是当方法进入到
update
方法中所有的@Autowired
的bean都会为null
,可以看到代码中是通过self
调用的自身方法,所以表示是通过Spring的代理类,那么为什么通过代理类调用方法,所有的注入的bean都会变为null,可以肯定的是应该是代理类在调用方法时,代理类不正确或者调用的方法不正确经过测试,
self
可以正常调用方法,那么应该是调用的方法有问题,仔细查看调用方法,update
方法和普通方法的区别是权限修饰符,update
方法的权限修饰符为private
,相信看到这里,基本上就明白是怎么回事了,Spring代理类在调用方法时,无法通过代理类调用目标类的private
方法,所以虽然可以调用成功,但是真实的是没有通过代理类调用,而是通过自身方法重新调用了一遍当前类的update
方法,那么所有的通过Spring注入的bean都是无法注入的,因为当前类没有在Spring容器的管理中,无法注入Spring的bean -
总结
Spring的bean的方法调用都是通过代理类来进行调用,而代理类是无法调用目标类的私有方法,否则会导致调用之后无法注入Spring中的bean。
并且
@Transaction
注解也无法在private
方法上作用,必须为public
方法并且该方法需要通过Spring中的代理类进行调用,才会生效