Adrninistrator / java-all-call-graph

Generate all call graph for Java Code.
Apache License 2.0
437 stars 111 forks source link

调用关系与变量命名有关联 #48

Closed xiongxt86 closed 1 year ago

xiongxt86 commented 1 year ago

大致描述:

  1. 接口 IUserService 和 实现 UserServiceImpl
  2. 接口 IDeptService 和 实现 DeptServiceImpl
  3. 使用方式 3.1 使用方式1: public DeptServiceImpl implement IDeptService { @AutoWired private IUserService userService; } 3.2 使用方式2: public DeptServiceImpl implement IDeptService { @AutoWired private IUserService userServiceImpl; } 4.结果 方式1调用链 :UserServiceImpl[0](唯一实现)-> IUserService[1] -> DeptServiceImpl[2] -> IDeptService[3] 方式2调用链(两条): UserServiceImpl [0] -> IUserService[1] 和 UserServiceImpl[0]-> DeptServiceImpl[1] -> IDeptService[2]

预期仅有一条调用链(方式1调用链 ),但因为变量名字不规范就生成错误的调用关系。 猜测会优先通过变量名去获取spring工厂的实例,而忽略了声明的类型(接口)。

Adrninistrator commented 1 year ago

麻烦看一下使用的是不是最新版,之前有一个应该是类似的问题修复了,https://github.com/Adrninistrator/java-callgraph2/issues/30 如果是最新版的话,麻烦把上面的类的方法也发一下,方便看一点

xiongxt86 commented 1 year ago

已经是最新版本了;正式项目在远程桌面,没法复制;我本地构建一个类似的项目,生成文档对比,等会贴出来。 `

com.github.adrninistrator
        <artifactId>java-all-call-graph</artifactId>
        <version>1.0.12</version>
    </dependency>`
xiongxt86 commented 1 year ago

版本用的最新的1.0.12,仅仅修改了变量的名称(声明类型不变,仍然是接口),结果会有差异。 注入写法1: @Autowired private IUserService userServiceImpl;

com.macro.mall.dao.UserMapper:selectById(java.lang.String) [0]#UserMapper:selectById [1]# UserServiceImpl:queryById (UserServiceImpl:17) 这里接口调用没有融入到主链重 [2]# IUserService:queryById (IUserService:0) !entry! [2]# DeptServiceImpl:queryById (DeptServiceImpl:22) [3]# IDeptService:queryById (IDeptService:0) [4]# DeptController:queryById@io.swagger.annotations.ApiOperation@org.springframework.web.bind.annotation.RequestMapping(/dept/queryById)@org.springframework.web.bind.annotation.ResponseBody (DeptController:32) !entry!

注入写法2(结果正确的): @Autowired private IUserService userService;

com.macro.mall.dao.UserMapper:selectById(java.lang.String) [0]#UserMapper:selectById [1]# UserServiceImpl:queryById (UserServiceImpl:17) [2]# IUserService:queryById (IUserService:0) [3]# DeptServiceImpl:queryById (DeptServiceImpl:22) [4]# IDeptService:queryById (IDeptService:0) [5]# DeptController:queryById@io.swagger.annotations.ApiOperation@org.springframework.web.bind.annotation.RequestMapping(/dept/queryById)@org.springframework.web.bind.annotation.ResponseBody (DeptController:32) !entry!

xiongxt86 commented 1 year ago

有些场景声明类型是接口,仅仅变量名称恰好是spring工厂的bean对象,出现偏差了。

xiongxt86 commented 1 year ago

我看了另一个问题,他是根据名称找错接口了,我这个根据名称找对了接口,实现的spring对象也是对的,但多出了一个链路。

Adrninistrator commented 1 year ago

通过@Autowired注解注入的字段,注入的是接口的时候,之前有人希望能通过实现类找到对应的接口,所以在生成调用关系时没有把接口替换成实现类 要把调用关系里的接口替换成实现类是可以做到的,如果需要的话我增加一个参数来控制是否需要替换

xiongxt86 commented 1 year ago

文件清单: 1.DeptController.java

@Controller @Api(tags = "DeptController") @Tag(name = "DeptController", description = "Dept对象存储管理") @RequestMapping("/dept") public class DeptController {

@Autowired
private IDeptService deptService;

@ApiOperation("部门查询")
@RequestMapping(value = "/queryById", method = RequestMethod.POST)
@ResponseBody
public CommonResult<Object> queryById(@RequestParam("deptId") String deptId) {
    try {
        Dept dept = deptService.queryById(deptId);
        return CommonResult.success(dept);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return CommonResult.failed();
}

}

2.IDeptService.java public interface IDeptService {

public Dept queryById(String deptId);

}

3.DeptServiceImpl.java @Service public class DeptServiceImpl implements IDeptService {

@Autowired
private DeptMapper deptMapper;

@Autowired
private IUserService userService;

@Override
public Dept queryById(String deptId) {
    userService.queryById(deptId);
    return deptMapper.selectById(deptId);
}

} 4.IUserService.java public interface IUserService {

public User queryById(String userId); 

} 5.UserServiceImpl.java @Service public class UserServiceImpl implements IUserService {

@Autowired
private UserMapper userMapper;

@Override
public User queryById(String userId) {
    return userMapper.selectById(userId);
}

} 6.DeptMapper.java public interface DeptMapper { public Dept selectById(String deptId); } 7.UserMapper.java public interface UserMapper { public User selectById(String userId); }

xiongxt86 commented 1 year ago

可能误会我的意思了,不是把接口类替换为实现类;目前生成调用链是没有问题的,实现类->接口类->调用方实现类->调用方接口类; 问题出在,调用方注入被调用方声明变量时,名字换了个(由userService换成userServiceImpl),就会出错:《由一条调用链,变成了两条调用链》,我想要的效果是,变量的名字不管取啥(包括特例spring工厂的对象名,类似userServiceImpl),都只有一条调用链。

通过@Autowired注解注入的字段,注入的是接口的时候,之前有人希望能通过实现类找到对应的接口,所以在生成调用关系时没有把接口替换成实现类 要把调用关系里的接口替换成实现类是可以做到的,如果需要的话我增加一个参数来控制是否需要替换

Adrninistrator commented 1 year ago

使用@Autowired注解注入字段的时候,如果接口存在多个实现类,是可以通过字段的变量名来指定使用哪一个,Spring是这样实现的,现在分析代码的时候也是这么做的,所以上面的现象是和Spring特性一致的 如果需要解决的话可以有两种方法,一是像上面说的增加开关,决定是否需要把接口替换成实现类 二是获取到向上的方法调用的入口方法的时候,从数据库class_info表的access_flags字段判断当前类是否为接口,是接口就可以忽略掉,或者是通过这个表对应的文件记录来判断