记录一次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<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); Object retVal;
//判断方法是不是公共方法 是的话直接使用target去调用 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())); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
2016-05-07 java陷阱之spring事物未提交和回滚导致不可预知问题