Problems and solutions encountered by the Springboot project using third-party Jar packages

1. Background introduction

I want to extract the common code in multiple projects and provide it to other projects in the form of a third-party Jar package to reduce maintenance costs and reduce code redundancy.

The extracted public code is a Springboot project, and the one that uses the public Jar package is also a Springboot project.

Use Mybatis to implement database-related operations.

2. Question summary

2.1 Springboot automatic scanning cannot scan the annotation beans in the jar package

(1) Use the class in the jar, there is a bean named baseRedisDao injected with the @Autowired annotation in this class, a class object in Jar is newly created in the project that uses the jar, and baseRedisDao is found to be null

SingleCaseDebugAPI class in the third-party Jar package

public class SingleCaseDebugAPI {
    @Autowired
    BaseRedisDao baseRedisDao;

    @Autowired
    GatewayCasePbService gatewayCasePbService;

    @Autowired
    GatewayCaseModuleService gatewayCaseModuleService;

    @Autowired
    GatewayCaseUserService gatewayCaseUserService;

Use the method provided in the third-party Jar package

@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;
}

Reason:
using @Autowired annotation to inject bean instances into the container is handed over to Springboot management; and the new instance is out of Springboot management, the two are not under the management of the same manager, so they cannot be linked together. Therefore, it will be null when using @Autowired injected beans

Solution:
Do not instantiate objects in the new way, but also use annotations. Add @Component annotation to the instance class that needs new, and use the instantiated class through dependency injection

Modified SingleCaseDebugAPI class

@Component
public class SingleCaseDebugAPI {
    @Autowired
    BaseRedisDao baseRedisDao;

    @Autowired
    GatewayCasePbService gatewayCasePbService;

    @Autowired
    GatewayCaseModuleService gatewayCaseModuleService;

    @Autowired
    GatewayCaseUserService gatewayCaseUserService;

Correctly use the code of the third-party Jar package to provide the interface

@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) After solving the above problem, it is found that the bean annotated with @Autowire still cannot be used, and the value is still null. Later, it was found that it should be caused by the bean problem of the third-party Jar that Springboot did not scan.

Solution:
Use the scanBasePackages attribute of the @SpringBootApplication annotation to specify the package path scanned by Springboot, including the path of the third-party jar package and its own project package. If not specified, only @Component, @Service and other annotations under the startup class and its children will be scanned by default

@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) After the modification in step 2, the problem of the annotation bean being null is finally solved, but when using the database-related methods in the third-party jar package, the following error is reported.
org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.umetrip.qa.mapper.GatewayCaseModuleMapper.findGatewayCaseModuleByrpid
This error is generally due to the mapper interface class and mapper xml The file does not correspond to the cause, I carefully checked the code and did not find the wrong place, so I feel that it is still caused by not loading the mapper xml file into the project when using the jar package

Solution:

Use the @MapperScan annotation on the project startup class to specify the third-party jar package and the package address of the mapper interface class of this project

@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);
    }
}

Modify the application.properties configuration file

  • classpath* means loading the mapper xml file imported into the jar package
  • Note that the package name of the xml file stored in the third-party jar must not be named "mybatis-mapper", which contains special characters, which cannot be recognized
mybatis.mapper-locations=classpath*:/mapper/**/*.xml

Guess you like

Origin blog.csdn.net/sinat_34241861/article/details/112844954