前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring 切面失败和目标类空指针问题

Spring 切面失败和目标类空指针问题

作者头像
haoming1100
发布2019-01-28 11:14:01
1.5K0
发布2019-01-28 11:14:01
举报
文章被收录于专栏:步履前行步履前行

记一次Spring AOP 遇到的坑

背景

由于想记录 Controller 前后的处理情况,为什么不用 filter 处理是因为项目中有作业等其他请求,并不想做太多记录。

问题描述
  1. 加了 @Aspect 注解在切面类上,
代码语言:javascript
复制
 /**
     * 记录controller方法前所有的日志
     *
     * @param joinPoint 不能为空
     */
    @Before("execution(* com.xxx.controller..*.*(..))")
    public void pointBeforeMethodInvoke(JoinPoint joinPoint) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        StringBuilder sb = new StringBuilder("---------- Starting : ");
        sb.append(joinPoint.getSignature().getDeclaringTypeName());
        sb.append(".");
        sb.append(joinPoint.getSignature().getName());
        sb.append(logAllParamsInfo(joinPoint));
        sb.append("TAG_UUID:");
        sb.append(RequestContext.getContext().getId());
        sb.append(", Token:");
        sb.append(request.getParameter("token"));
        sb.append(" --------");
        info(sb::toString);

        RequestContext.getContext().setBeginTime(System.currentTimeMillis());
    }

然后我们的包结构如下:(xx 是把关键字隐去了)

  • com.xx
    • controller
    • controller.api
    • common
    • entity
    • 等等

可以看到我的切面是切的 controller 以及它下面的子包 api 和相关的类。

  1. 问题爆发,测试的时候发现,controller 和它下面子包的 api 除了一个类,其他的类都能被切面处理,唯独某个 Controller 进不去,而且诡异的是 在该类中注入的 service 类全为 null。 然后爆发了空指针异常。

解决异常

  1. 在爆发了空指针后,首先第一反应就是切面是否正常,查看 execution 表达式,以及测试 Controller 下的其他类,正常,所以排除 切面类的问题
  2. 在某个空指针的地方 debug,然后手动获取 注入的 Service 的 bean,发现正常,证明相关的配置注解正常。
  3. 在想不到其他办法的时候,选择使用 @PostConstruct 。在出问题的 Controller 中加入 init 手动初始化,加入 bean
代码语言:javascript
复制
public class StatisticsController {

    private static final Logger logger = LoggerFactory.getLogger(StatisticsController.class);

    @Autowired
    private IStatisticsService statisticsService;
    @Autowired
    private IKnowledgeService knowledgeService;

    @PostConstruct
    private void init() {
        StatisticsController statisticsController = this;
        statisticsController.knowledgeService = this.knowledgeService;
    }

然后在 init 方法中 debug, 发现在 Spring 启动的时候,statisticsService 和 knowledgeService 注入正常,但是在请求 Controller 的时候,2 个 Service 还是 null。

  1. 在尝试完上诉方法,想不到办法的情况下,只能上大招了(原谅作者菜)。一行一行的和正常的类比较代码, 当然,结果出来了,发现出问题的 Controller 的方法都是 private 的,正常的都是 public 的,然后查找相关的 文档,找到了原因

与AspectJ相比,Spring AOP是一种基于代理的“AOP lite”方法。它仅适用于Spring组件,仅适用于公共,非静态方法。这在Spring AOP文档中也有解释,如下所示:

由于Spring的AOP框架基于代理的特性,受保护的方法根据定义不会被拦截,既不用于JDK代理(这不适用),也不用于CGLIB代理(这在技术上可行,但不建议用于AOP)。因此,任何给定的切入点都只能与公共方法匹配!如果您的拦截需要包括受保护/私有方法甚至构造函数,请考虑使用Spring驱动的本机AspectJ编织而不是Spring的基于代理的AOP框架。这构成了具有不同特征的不同AOP使用模式,因此在做出决定之前一定要先熟悉编织。

说白了 就是 aop底层是代理, jdk是代理接口,私有方法必然不会存在在接口里,所以就不会被拦截到; cglib是子类,private的方法照样不会出现在子类里,也不能被拦截。

不是类内部直接调用方法,而是通过维护一个自身实例的代理 所以 execution(public * com.xxx.controller...(..))) 只会切 public 的方法,不写也一样,public 是默认切的方法,如果写了protected,他就什么事情都不做,连protected的方法也不拦截。 如果想要实现拦截private方法的 可以使用 原生 AspectJ 编译期/运行期织入。

而我们上面的空指针问题,其实就是 切面已经代理了 Controller 下的类,但是又没有被切到,造成了注入的失败。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-01-18 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 记一次Spring AOP 遇到的坑
    • 背景
      • 问题描述
        • 解决异常
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档