一、背景介绍
我想把多个项目中通用的代码抽离出来,以第三方Jar包形式提供给其他工程使用,减少维护成本,减少代码冗余。
抽离出来的公共代码是一个Springboot工程,使用该公共Jar包的也是Springboot工程。
使用Mybatis实现数据库相关操作。
二、问题汇总
2.1 Springboot自动扫描无法扫描jar包中的注解bean
(1)使用jar里的类,该类里存在使用了@Autowired注解注入的名为baseRedisDao的bean,在使用该jar的工程中new出来一个Jar里的类对象,发现baseRedisDao为null
第三方Jar包中的SingleCaseDebugAPI类
public class SingleCaseDebugAPI {
@Autowired
BaseRedisDao baseRedisDao;
@Autowired
GatewayCasePbService gatewayCasePbService;
@Autowired
GatewayCaseModuleService gatewayCaseModuleService;
@Autowired
GatewayCaseUserService gatewayCaseUserService;
使用第三方Jar包中提供的方法
@RequestMapping(value = "debugCase", method = RequestMethod.POST)
public ResMsg debugCase(@RequestBody JSONObject requestBodyParameters) {
ResMsg resMsg = new ResMsg();
System.out.println("前端传过来的用例内容:" + requestBodyParameters);
boolean isShowDev = requestBodyParameters.getBooleanValue("isShowDev");
if(isShowDev){
System.out.println("开发人员调试接口:" +isShowDev);
DebugAPI debugAPI = new DebugAPI();
resMsg = debugAPI.debugAPIForDev(requestBodyParameters);
}else {
System.out.println("测试人员调试用例:" + isShowDev);
// 错误写法,不应该new对象
SingleCaseDebugAPI singleCaseDebugAPI = new SingleCaseDebugAPI();
resMsg = singleCaseDebugAPI.singleCaseDebug(requestBodyParameters);
}
System.out.println("resMsg status:" + (resMsg.getStatus()));
System.out.println("resMsg data:" + (resMsg.getData()));
System.out.println("222222222" + JSONObject.toJSONString(resMsg));
return resMsg;
}
原因:
使用@Autowired注解向容器中注入bean实例是交给Springboot管理;而new出来的实例脱离了Springboot的管理,二者不在一个管理者管理下,所以没法联系在一起。因此使用@Autowired注入的bean时就会为null
解决办法:
不要用new的方式实例化对象,也采用注解的方式。在需要new的实例类上加@Component注解,通过依赖注入的方式使用实例化类
修改后的SingleCaseDebugAPI类
@Component
public class SingleCaseDebugAPI {
@Autowired
BaseRedisDao baseRedisDao;
@Autowired
GatewayCasePbService gatewayCasePbService;
@Autowired
GatewayCaseModuleService gatewayCaseModuleService;
@Autowired
GatewayCaseUserService gatewayCaseUserService;
正确使用第三方Jar包提供接口的代码
@RestController
@RequestMapping(value = "api/umeapiplus/action")
@Component
public class ActionController {
private final static Gson gson = new Gson();
private final Logger logger = LoggerFactory.getLogger(ActionController.class);
// 以@Autowired注解方式拿到 singleCaseDebugAPI bean
@Autowired
public SingleCaseDebugAPI singleCaseDebugAPI;
@RequestMapping(value = "debugCase", method = RequestMethod.POST)
public ResMsg debugCase(@RequestBody JSONObject requestBodyParameters) {
ResMsg resMsg = new ResMsg();
System.out.println("前端传过来的用例内容:" + requestBodyParameters);
boolean isShowDev = requestBodyParameters.getBooleanValue("isShowDev");
if(isShowDev){
System.out.println("开发人员调试接口:" +isShowDev);
DebugAPI debugAPI = new DebugAPI();
resMsg = debugAPI.debugAPIForDev(requestBodyParameters);
}else {
System.out.println("测试人员调试用例:" + isShowDev);
resMsg = singleCaseDebugAPI.singleCaseDebug(requestBodyParameters);
}
System.out.println("resMsg status:" + (resMsg.getStatus()));
System.out.println("resMsg data:" + (resMsg.getData()));
System.out.println("222222222" + JSONObject.toJSONString(resMsg));
return resMsg;
}
}
(2)解决了上述问题后,发现还是无法使用@Autowire注解的bean,取值仍然为null。后来发现应该是Springboot未扫描到第三方Jar的bean问题造成的
解决办法:
使用@SpringBootApplication注解的scanBasePackages属性指定Springboot扫描的包路径,包括第三方jar包和自身工程包的路径。如果不指定的话,默认只会扫描启动类同级及其子级下的@Component、@Service等注解
@SpringBootApplication(scanBasePackages = {"com.umetrip.qa","com.taobao.rigel.rap"}, exclude = { DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, SecurityAutoConfiguration.class })
public class UmeApiPlusApplication {
public static void main(String[] args) {
SpringApplication.run(UmeApiPlusApplication.class, args);
}
}
(3)经过第2步中的修改,终于解决了注解bean为null的问题,但是当使用第三方jar包中的数据库相关方法时,报如下错误
org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.umetrip.qa.mapper.GatewayCaseModuleMapper.findGatewayCaseModuleByrpid
这个错误一般都是由于mapper接口类与mapper xml文件对应不上导致的,我仔细检查了代码并未发现错误的地方,所以感觉还是由于使用jar包时没有将mapper的xml文件加载至工程中导致的
解决办法:
在工程启动类上使用@MapperScan注解,指定第三方jar包及本工程的mapper接口类所在包地址
@SpringBootApplication(scanBasePackages = {"com.umetrip.qa","com.taobao.rigel.rap"}, exclude = { DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, SecurityAutoConfiguration.class })
@ServletComponentScan
@MapperScan(basePackages = {"com.umetrip.qa.mapper", "com.taobao.rigel.rap.mapper"})
public class UmeApiPlusApplication {
public static void main(String[] args) {
SpringApplication.run(UmeApiPlusApplication.class, args);
}
}
修改application.properties配置文件
- classpath*表示加载引入jar包中的mapper xml文件
- 注意第三方jar中存放xml文件的包名千万不要命名成“mybatis-mapper”这种含有特殊字符的名称,无法识别
mybatis.mapper-locations=classpath*:/mapper/**/*.xml