前言
发现问题
在力资费新链路改造中,我们系统通过我司自研的RPC与外围系统要对接。我们系统作为服务消费方去引用外围系统服务提供方提供的支付服务。
通过@Reference去引用服务提供方提供的服务,报空指针异常,即通过注解的方式并没有生成代理对象。
查找问题
从报错分析,空指针的直接原因,是没有生成代理对象。而生成代理对象的这一过程是在创建服务消费方对象的时候。
重点来了,其实就是IOC容器中bean对象(本例中消费方)的生命周期。
我们找到了问题的发生时机,直接双shift快捷搜索AbstractAutowireCapableBeanFactory,找到该类的doCreateBean方法,打上条件断点"laborChargeServiceImpl".equals(beanName) ,重新启动项目。
发现进入断点两次,即服务消费方对象创建了两次。
- 第一次进入断点时,执行到populateBean,对比该过程前后服务消费方对象的属性值变化,符合正常情况下的预期,生成代理对象。
- 第二次进入断点时,执行到populateBean,再次对比该过程前后服务消费方对象的属性值变化,符合当前问题所产生的现象,没有生成代理对象。
继续查找为什么会创建对象两次?
该项目是一个web应用,第一次是由applicationContext中的IOC容器所创建的对象,而第二次是由SpringMVC中的IOC容器spring-servlet创建的对象。换句话说,就是两个IOC容器的扫描包路径相同,导致后续所使用的的对象都是由spring-servlet所创建的对象。
解决问题
两种思路:
- 重新梳理项目结构。工厂量跟大,整个项目有80多万代码量,spring的配置文件又很多,十分容易遗漏某些bean的创建。
- 在spring-servlet的IOC容器中用排除扫描包的方式。这种风险最小,因为仅仅排除了由该问题导致的@Reference失效的现象。
总结
- SpringMVC中的IOC容器spring-servlet.xml与Spring应用中的其他容器applicationContext.xml一定要坚持分工管理的原则。即spring-servlet只管controller的bean对象,而applicationContext管理服务对象。
- 从该项目中,受到一点启发。就是将项目中的配置文件作为打包工程供子工程使用,避免对服务配置文件的随意修改,造成如今我们的项目结构混乱,进而导致我司提供的RPC功能被我们使用时,受到限制,项目中没有利用@Reference,而是通过工厂及代码的方式_。
- 启迪了我对我们项目关于拆分上的项目结构的合理性的思考。
3.1 一个基础设施工程,向子工程提供一种基础技术使用的统一和规范
3.2 依赖基础设施的入库应用
3.3 依赖基础设施的出库应用
3.4 依赖基础设施的库内应用
3.5 依赖基础设施的部署应用等