1. 程式人生 > 其它 >記錄一次spring cglib代理導致空指標異常

記錄一次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是不是同一個

報空指標的

 

不報空指標的

 

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<Object> 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()));
    }