自定义注解返回mock数据

1. 如果配置mock测试为真,返回mock数据,为假,返回真实数据

2. 实现方式

可以每次在方法中判断,但是很麻烦,好多if判断

可以定义接口,多一个mock实现,在程序启动的时候,只加载mock实现类,真实实现类不加载(可以自定义类加载器或者使用spring bean加载过滤)

也可以使用AOP切面,判断是否mock看情况返回

其他实现方式暂时没有想法或者头绪

最后确定使用AOP简单快捷

3. 代码

自定义注解

@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MockData {

    @AliasFor("method")
    String value() default "";

    /**
     * 是否开启mock数据返回
     */
    boolean isOpen() default true;

    /**
     * 指定mock数据组装 <strong>无参</strong>方法
     */
    @AliasFor("value")
    String method() default "";

    /**
     * mock方法类,默认当前类
     * class数组有且只能有一个class,该类最好使用spring bean管理
     */
    Class<?>[] classFor() default {};
}

 AOP 切面 

@Slf4j
@Aspect
@Component
public class MockAspect {

    @Value("${mockModal}")
    private boolean mockModal;

    @Autowired
    private BeanFactory beanFactory;

    /**
     * mock数据切面方法
     *
     * @param joinPoint
     * @param mockData
     */
    @Around("within(@org.springframework.stereotype.Service *) && @annotation(mockData)")
    public Object around(ProceedingJoinPoint joinPoint, MockData mockData) {
        Object res = Res.fail("服务内部mock数据错误!");
        try {
            // 判断是否开启了 mock 数据返回
            if (mockData.isOpen() && mockModal) {
                // @AliasFor 是spring 特有注解,这里使用spring提供的工具类获取注解,要不然 value和method互替不会生效
                mockData = AnnotationUtils.getAnnotation(mockData, MockData.class);
                assert mockData != null;
                Object bean;
                Method[] methods;
                if (mockData.classFor().length == 0) {
                    // 默认使用当前类
                    bean = beanFactory.getBean(joinPoint.getTarget().getClass());
                    methods = joinPoint.getTarget().getClass().getDeclaredMethods();
                } else {
                    try {
                        bean = beanFactory.getBean(mockData.classFor()[0]);
                    } catch (NoSuchBeanDefinitionException e) {
                        log.warn("mock数据访问类不在spring bean工厂中改用反射方式...");
                        bean = mockData.classFor()[0].newInstance();
                    }
                    methods = mockData.classFor()[0].getDeclaredMethods();
                }
                // mock 注解指定的 mock方法名
                String name = mockData.method();
                Optional<Method> check = Arrays.stream(methods).filter(method -> method.getName().equals(name)).findFirst();
                if (check.isPresent()) {
                    Method method = check.get();
                    method.setAccessible(true);
                    // 执行 mock 方法
                    res = method.invoke(bean);
                }
            } else {
                res = joinPoint.proceed();
            }
        } catch (Throwable throwable) {
            log.error("mock数据切面出错!", throwable);
        }
        return res;
    }
}

  

使用:在实现类方法上使用,指定mock方法

@Override 
@MockData("mockSearch")
public Res search(AddDTO dto) {...}

private Res mockSearch() {
  return Res.ok("mock data");
}

其中 Res 是返回封装类,没有其他特殊作用。mockModal 是配置是否使用mock数据的配置项。

posted @ 2021-11-03 16:20  衰草寒烟  阅读(815)  评论(0编辑  收藏  举报