性能

linpeilie大约 3 分钟介绍

这里假设一个常见的例子:根据用户名关联获取用户昵称,关联条件只有一个,根据关联查询方法的入参和出参分别提供了四种场景测试:

  • 场景一:关联查询时查询方法的入参和出参类型一致,框架中不需要类型转换。

    例如:根据用户名获取用户昵称

    public String getNickNameByUsername(String username) {
        if (admin.equals(username)) {
            return nickName;
        }
        return null;
    }
    
  • 场景二:关联查询时查询方法的入参类型一致,出参类型不一致,框架中需要解析返回结果。同场景一的例子如下:

    public User getUserByUsername(String username) {
        if (admin.equals(username)) {
            return user;
        }
        return null;
    }
    
  • 场景三:关联查询时查询方法的入参类型不一致,出参类型一致,框架中需要解析入参类型。方法示例如下:

    public String getNickname(UserQueryReq userQueryReq) {
        if (admin.equals(userQueryReq.getUsername())) {
            return nickName;
        }
        return null;
    }
    
  • 场景四:关联查询时查询方法的入参和出参类型都不一致,框架中需要解析入参和出参类型。方法示例如下:

    public User getUser(UserQueryReq userQueryReq) {
        if (admin.equals(userQueryReq.getUsername())) {
            return user;
        }
        return null;
    }
    

针对于上面四种测试场景,又分别对使用默认参数生成策略和基于 MapStructPlusopen in new window 两种情况分别测试。该条件可以参考关联查询参数生成策略

在提供的测试用例中,尽可能排除其他方面的影响,这样子基本可以认为是引入框架后的性能损耗。

最终测试代码如下:

  • 数据提供者 —— UserDataProvider

    @Component
    public class UserDataProvider implements DataProvideService {
    
        private static String admin = "admin";
    
        private static String nickName = "管理员";
    
        private static User user;
    
        static {
            user = new User();
            user.setNickName(nickName);
        }
    
        @DataProvider("performanceGetNickNameByUsername")
        public String getNickNameByUsername(String username) {
            if (admin.equals(username)) {
                return nickName;
            }
            return null;
        }
    
        @DataProvider("performanceGetUserByUsername")
        public User getUserByUsername(String username) {
            if (admin.equals(username)) {
                return user;
            }
            return null;
        }
    
        @DataProvider("performanceGetNickname")
        public String getNickname(UserQueryReq userQueryReq) {
            if (admin.equals(userQueryReq.getUsername())) {
                return nickName;
            }
            return null;
        }
    
        @DataProvider("performanceGetUser")
        public User getUser(UserQueryReq userQueryReq) {
            if (admin.equals(userQueryReq.getUsername())) {
                return user;
            }
            return null;
        }
    
    }
    
  • 测试类

    @SpringBootTest
    public class PerformanceTest {
    
        private InjectRelation injectRelation;
    
        @Autowired
        private MapStructPlusMapToBeanHandle mapToBeanHandle;
    
        private Scene initScene() {
            final Scene scene = new Scene();
            scene.setUsername("admin");
            return scene;
        }
    
        private void recordExecute(InnerFunction innerFunction, String taskName) {
            StopWatch stopWatch = new StopWatch();
            for (int j = 0; j < 10; j++) {
                stopWatch.start(taskName + " : " + j);
                for (int i = 0; i < 1000000; i++) {
                    innerFunction.invoke();
                }
                stopWatch.stop();
            }
            System.out.println(stopWatch.prettyPrint(TimeUnit.MILLISECONDS));
        }
    
        @Test
        public void performanceTest() {
            // 通过 json 转换
            injectRelation = new InjectRelation(new JsonMapToBeanHandle());
            sceneTest();
    
            // 通过 MapStructPlus 转换
            injectRelation = new InjectRelation(mapToBeanHandle);
            sceneTest();
        }
    
        public void sceneTest() {
            final Scene scene = initScene();
            // 场景一:参数和返回类型都一致,不需要转换
            recordExecute(() -> {
                injectRelation.injectRelation(scene, "sceneOneNickName");
            }, "scene one");
            // 场景二:参数类型相同,不需要转换,返回类型不同,需要获取其中的一个属性
            recordExecute(() -> {
                injectRelation.injectRelation(scene, "sceneTwoNickName");
            }, "scene two");
            // 场景三:参数类型不同,需要转换,返回类型相同
            recordExecute(() -> {
                injectRelation.injectRelation(scene, "sceneThreeNickName");
            }, "scene three");
            // 场景三:参数类型和返回类型都不相同
            recordExecute(() -> {
                injectRelation.injectRelation(scene, "sceneFourNickName");
            }, "scene four");
        }
    
    }
    

测试运行环境为:

  • CPU:Intel i5 10400
  • 内存:32 GB
  • JDK:17

针对每个场景,执行10次一百万次的循环关联操作,最终结果如下:

  • 使用默认的参数生成策略:
场景一346189189185187184188188187183
场景二530424423421424422421421430432
场景三2619227122352235226622482238223522342233
场景四2528251525132519254125152513251325142510
  • 使用 MapStructPlus 的参数生成策略:
场景一209186186186186186188189187188
场景二405407404406407406405405407405
场景三418389389385386384383384385384
场景四621620620621618621618619620619