【Spring AOP】问题笔记 01 - Controller private 方法注入 Service 报空指针异常

前言

最近在写代码的时候犯了一个低级错误,但是要搞清楚问题背后的原理属实不太好找,这篇博客简单记录一下。

参考目录

问题描述

问题非常简单,我把 controller 里面的方法写成了 private 私有方法。导致调用 service 方法时报了 NullPointerException

一开始还没看到是修饰符的问题,而且容器中存在 service Bean,用 SpringUtil.getBean 方式可以获取到并且可以调用方法。后面发现是修饰符问题,真的是一整个无语住了。

问题重现

在一开始查询问题的时候,有看到同样遇到这个问题的朋友,大多是改为 public 修饰符即可,无意中看到一个评论说这是因为 Spring AOP 导致的,所以围绕这个问题,我写了一个 demo 来进行验证。

1、没有加入 Spring AOP 测试

1.1、Controller

分别用三种修饰符 publicprotectedprivate 调用 service 方法。

@RequiredArgsConstructor
@RestController
public class TestDemoController {

    private final ITestDemoService iTestDemoService;

    @GetMapping("/demo/public")
    public String demo1() {
        return iTestDemoService.demo();
    }

    @GetMapping("/demo/protected")
    protected String demo2() {
        return iTestDemoService.demo();
    }

    @GetMapping("/demo/private")
    private String demo3() {
        return iTestDemoService.demo();
    }

}

1.2、Service & ServiceImpl

public interface ITestDemoService {

    String demo();

}

@Service
public class TestDemoServiceImpl implements ITestDemoService {

    @Override
    public String demo() {
        return "success";
    }

}

1.3、请求结果

1.3.1、public 修饰方法

在这里插入图片描述

1.3.2、protected 修饰方法

在这里插入图片描述

1.3.3、private 修饰方法

在这里插入图片描述
由此可知,默认情况下,三种修饰符都能够调用 service 方法。

2、加入 Spring AOP 测试

Controller、Service、ServiceImpl 不变。

2.1、自定义切面 LogAspects

@Aspect
public class LogAspects {

    // 抽取公共的切入点表达式
    // 1、本类引用
    // 2、其他的切面引用
    @Pointcut("execution(String space.zlyx.zlyxdemo.controller.TestDemoController.*(..))")
    public void pointCut() {
    }

    // @Before在目标方法之前切入;切入点表达式(指定在哪个方法切入)
    @Before("pointCut()")
    public void logStart(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        System.out.println("" + joinPoint.getSignature().getName()
                + " 运行。。。@Before:参数列表是:{" + Arrays.asList(args) + "}");
    }

}

2.2、AOP 配置类 MyAopConfig

@EnableAspectJAutoProxy
@Configuration
public class MyAopConfig {

    @Bean
    public LogAspects logAspects() {
        return new LogAspects();
    }

}

2.3、请求结果

2.3.1、public 修饰方法

在这里插入图片描述
在这里插入图片描述

2.3.2、protected 修饰方法

在这里插入图片描述
在这里插入图片描述

2.3.3、private 修饰方法

在这里插入图片描述
查看控制台报错(空指针异常):
在这里插入图片描述

2.3.4、private 修饰方法,使用 getBean 方法获取

@GetMapping("/demo/private")
private String demo3() {
    ITestDemoService iTestDemoService = SpringUtil.getBean(ITestDemoService.class);
    return iTestDemoService.demo();
}

请求成功:
在这里插入图片描述
但是控制台没有打印切面方法。

问题分析

在之前学习 Spring AOP 的源码时,有提及到:在容器初始化时,有一个很重要方法:AbstractApplicationContext#refresh,里面有一步是需要实例化所有剩余的单实例 Bean。
在这里插入图片描述
从这里面深入,需要经过创建 Bean 实例,创建后置处理器等操作,然后再创建代理对象实例:
在这里插入图片描述
直到 Enhancer 类:
在这里插入图片描述

类方法的过滤

org.springframework.cglib.proxy.Enhancer#getMethods
在这里插入图片描述
org.springframework.cglib.core.CollectionUtils#filter
在这里插入图片描述
org.springframework.cglib.core.VisibilityPredicate#evaluate
在这里插入图片描述
在这里可以看到 private 修饰的方法被移除了,没有生成代理对象,因此调用方法时(实际上是代理对象调用方法)会报空指针异常。

  • 8
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MichelleChung

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值