记录一次spring cglib代理导致空指针异常


说明

很低级的一个错误,本次记录只是加深对spring代理的理解

现象

1.同样一个类2个不同的方法一个空指针一个不空指针

    @Resource
    IProviderRegistryService providerRegistryService;

    /**
     * providerRegistryService不报空指针方法
     *
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/api/v1/provider/industry_list", method = RequestMethod.GET)
    public OpenApiResponseEntity providerIndustryList() {
        return successful(providerRegistryService.providerIndustryList());
    }

    /**
     * providerRegistryService 报空指针方法
     *
     * @param request
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/api/v1/provider/submit_info", method = RequestMethod.POST)
    private OpenApiResponseEntity submitProviderInfo(@RequestBody @Validated ProviderSubmitInfoRequest request) {
        return successful(providerRegistryService.submitProviderInfo(
                request.getProviderId(), request.getEmail(), request.getIndustryIds(),
                request.getScale(), request.getLaunchPlanned(), request.getLaunchTime(), request.getScene()));
    }

解决思路

1.首先我想着是调用2个方法打断点看this是不是同一个

报空指针的 cglib是继承方式代理所以继承的成员变量是空 是正常的

不报空指针的

2.为什么一个是走代理一个没走代理 首先想到是跟spring mvc源码 看HandelMapping看如何获取handle是否是一个获取到代理一个没获取到

看源码是根据映射的bean name根据容器获取 2个接口都是获取的同一个代理类 那是不是handleAdapter在后面某个时机替换掉了呢

 public HandlerMethod createWithResolvedBean() {
        Object handler = this.bean;
        if (this.bean instanceof String) {
            String beanName = (String)this.bean;
            handler = this.beanFactory.getBean(beanName);
        }

        return new HandlerMethod(this, handler);
    }

3.spring mvc有对象和方法最后是通过反射调用 根据打断点正常的接口走了这样一个类

org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept

 public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            Object oldProxy = null;
            boolean setProxyContext = false;
            Class<?> targetClass = null;
            Object target = null;

            Object var11;
            try {
                if (this.advised.exposeProxy) {
                    oldProxy = AopContext.setCurrentProxy(proxy);
                    setProxyContext = true;
                }
                 //拿到被代理对象,调用
                target = this.getTarget();
                if (target != null) {
                    targetClass = target.getClass();
                }

                List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
                Object retVal;
                if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
                    retVal = methodProxy.invoke(target, args);
                } else {
                    retVal = (new CglibAopProxy.CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy)).proceed();
                }
                //反射调用
                retVal = CglibAopProxy.processReturnType(proxy, target, method, retVal);
                var11 = retVal;
            } finally {
                if (target != null) {
                    this.releaseTarget(target);
                }

                if (setProxyContext) {
                    AopContext.setCurrentProxy(oldProxy);
                }

            }

            return var11;
        }

4.那为什么不正常的接口没有走呢

CGLIB是基于类继承代理 而不正常的方法定义是私有的

那是否有疑问既然是继承 私有方法又不能继承 如何可以正常执行, 因为spring mvc用的是反射调用.反射是可以调用父类私有方法的,但是this是代理类 代理类的成员变量都是null

@ResponseBody
    @RequestMapping(value = "/api/v1/provider/submit_info", method = RequestMethod.POST)
    private OpenApiResponseEntity submitProviderInfo(@RequestBody @Validated ProviderSubmitInfoRequest request) {
        return successful(providerRegistryService.submitProviderInfo(
                request.getProviderId(), request.getEmail(), request.getIndustryIds(),
                request.getScale(), request.getLaunchPlanned(), request.getLaunchTime(), request.getScene()));
    }