記錄一次spring cglib代理導致空指標異常
阿新 • • 發佈:2022-05-07
說明
很低階的一個錯誤,本次記錄只是加深對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())); }