掘金 后端 ( ) • 2022-05-27 17:50

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情

问题由来

在使用AOP实现多数据源事务的过程中发现使用Autowired注解注入的ScDeptService为null,报空指针异常。

首先排除项目配置问题,接口都能正常使用,只是加了@MyTransactional这个自定义的注解后就出现了问题。

代码如下:(剧透一下,最后问题解决只改了一个单词就好了 ^_^)

//1.controller代码    
    @Autowired
    private ScDeptService deptService;

    //MyTransactional 自定义的注解
    @RequestMapping("test")
    @MyTransactional(transactionManagers={"transactionManager","mongoTransactionManager"})
    private void test(){
        Map<String, Object> map = new HashMap<>(2);
        map.put("id","00004351cfd34bd9bc8a0304fbbf1f99");
        ScDept dept = deptService.find(map);
        dept.setUpdateTime(LocalDateTime.now());
        deptService.delete(dept);
    }

//2.自定义了一个切面类
    @Component
    @Aspect
    public class MultiDataSourceTransactionAspect {
        @Pointcut("@annotation(com.njusc.entity.annotation.MyTransactional)")
        public void pointcut() {
        }
        @Before("pointcut() && @annotation(transactional)")
        public void before(MyTransactional transactional) {
            //逻辑代码.....
        }
    }

问题解决

通过百度发现也有许多小可爱出现了类似情况:在使用springAOP实现操作日志时导致Autowired注入失败。

最终验证了他们的解决方法得到答案:接口方法为private修饰的,在AOP适配的时候会导致service注入失败,并且同一个service在其他的public方法中没有这种情况。

改成public或者protected均能解决问题。

问题又来了

@Autowired背后实现的原理是怎样的?

为啥aop能影响我的@Autowired注入失败?

Spring默认AOP代理方式是什么?

动态代理原理是啥来着?

我改成@Resource还会注入失败吗?

@Autowired和@Resource的区别又是啥呢?

我为啥啥都不知道?

解答

@Autowired背后实现的原理是怎样的?

看这个大佬讲的就完事了。@Autowired注解的实现原理

为啥aop能影响我的@Autowired注入失败?

定义在切面AOP下的Controller类会走代理,不管private还是public方法bean都是null值,是因为CGLIB在做初始化的时候本身是没有bean属性注入的。

当方法为private的时候,由于没有被AOP拦截,它继续使用代理类,代理类中的 bean=null。

因此我们可以判定public方法在AOP过程中有其他的操作,不然bean的属性也是null。

org.springframework.aop.framework.CglibAopProxy类中有一个静态内部类CglibMethodInvocation,其中有一个方法invokeJoinpoint()

当publicMethod=true的时候,就会用实际对象来进行反射调用,实际对象的bean属性值我们之前已经看到了,是已经注入的。

因此public方法的bean会重新赋值。

大佬解释链接:为什么你写的Controller里,private方法中的bean=null?

Private方法造成NullPointerException

不禁让我感叹:CSDN大佬还是多呀。 另外:百度真TM辣鸡,有条件的谷歌助手安排一下吧,谷歌前两条直接解决我的疑惑。