打造一个专注Java假数据的JBMock

前言

两个月前写过一篇文章基于Django设计的Restful MockServer,是基于网络的。很多人都觉得思路不错,但是主要是太麻烦了。本篇博文就根据Java开发一个假数据生成器,称为JBMock

Github地址

主要功能

  • 1、提供同步、异步获取数据

  • 2、异步获取数据提供线程切换

原理图

原理图
  • 1、使用反射,获取UserEntity的属性和对应注解(Type注解)

  • 2、获取注解里面的值,然后使用TypeParser解析注解值,然后设置到对应的属性上

  • 3、使用ThreadDispatcher完成回调

代码分析

包结构

包结构

代码

Type注解

package com.august1996.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Type {
    String value();
}

这个注解的意义在于我们给某个属性加上这个注解,然后根据类型去给属性赋值,例如value为username时,那么这个属性就会被赋上小明、小红、小花等值

TypeParser类型解析器

package com.august1996.core;

public interface TypeParser {
    Object parser(String type);
}

该接口的作用是解析Type注解的value,返回对应的值

OnMockedCallback回调

package com.august1996.callback;

import java.util.ArrayList;

public interface OnMockedCallback<T> {
    void onMocked(ArrayList<T> listEntity);
}

当异步模拟数据完成时,该回调带着结果被执行

ThreadDispatcher线程切换

package com.august1996.thread;

import java.util.ArrayList;

import com.august1996.callback.OnMockedCallback;

public interface ThreadDispatcher {
    <T> void dispatcher(ArrayList<T> value, OnMockedCallback<T> callback);
}

该接口完成回调时的线程切换,本项目是JavaSDK的,如果接入Android时,只需要继承自该接口使用Handler就能完成主线程切换

DefaultDispatcher分配默认线程

package com.august1996.thread;

import java.util.ArrayList;

import com.august1996.callback.OnMockedCallback;

public class DefaultDispatcher implements ThreadDispatcher {

    @Override
    public <T> void dispatcher(ArrayList<T> value, OnMockedCallback<T> callback) {
        if (callback != null) {
            callback.onMocked(value);
        }
    }

}

该类是最简单的ThreadDispatcher,如果需要在Android中切换主线程,只需要继承ThreadDispatcher,然后使用Handler切换回主线程就OK了

DispatcherHolder管理类

package com.august1996.thread;

import java.util.HashMap;
import java.util.Map;

public class DispatcherHolder {

    private static final Map<Class<? extends ThreadDispatcher>, ThreadDispatcher> sMap;

    static {
        sMap = new HashMap<>();
        register(new DefaultDispatcher());
    }

    public static void register(ThreadDispatcher dispatcher) {
        sMap.put(dispatcher.getClass(), dispatcher);
    }

    public static void unregister(Class<? extends ThreadDispatcher> clazz) {
        sMap.remove(clazz);
    }

    public static ThreadDispatcher get(Class<? extends ThreadDispatcher> clazz) {
        return sMap.get(clazz);
    }

}

该类的作用是管理不同类型的ThreadDispatcher,可以通过注册不同的ThreadDispatcher,使JBMock可以调用其他自己编写的ThreadDispatcher

ValueHolder类型数据管理器

package com.august1996.parser;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

public class ValueHolder {
    private static final Map<String, List<Object>> sMap = new HashMap<String, List<Object>>();
    private static final Random sRandom = new Random();

    public static void register(String type, Object... value) {
        List<Object> list = sMap.get(type);
        if (list == null) {
            list = new ArrayList<Object>();
            sMap.put(type, list);
        }
        list.addAll(Arrays.asList(value));
    }

    public static void unregister(String type) {
        sMap.remove(type);
    }

    public static void clear() {
        sMap.clear();
    }

    static Object get(String type) {
        List<Object> list = sMap.get(type);
        if (list != null && !list.isEmpty()) {
            return list.get(sRandom.nextInt(list.size()));
        }
        return null;
    }
}

该类负责管理Type注解对应的数据,例如如果想要当属性的Type的value为username时,JBMock就会给这个属性赋值小明、小红、小花等内容,那么就需要register("username","小明","小红","小花")

HolderParser解析器

package com.august1996.parser;

import com.august1996.core.TypeParser;

public class HolderParser implements TypeParser {

    @Override
    public Object parser(String type) {
        return ValueHolder.get(type);
    }

}

该类为默认的Parser之一,结合Type注解ValueHolder根据类型解析数据

MockRunnable异步任务

package com.august1996.core;

import java.util.ArrayList;

import com.august1996.callback.OnMockedCallback;
import com.august1996.thread.DispatcherHolder;
import com.august1996.thread.ThreadDispatcher;

public class MockRunnable<T> implements Runnable {

    private Class<T> clazz;
    private int size;
    private int delay;
    private OnMockedCallback<T> callback;
    private Class<? extends ThreadDispatcher> threadDispatch;

    public MockRunnable(Class<T> clazz, int size, int delay, Class<? extends ThreadDispatcher> threadDispatch,
            OnMockedCallback<T> callback) {
        super();
        this.delay = delay;
        this.clazz = clazz;
        this.callback = callback;
        this.size = size;
        this.threadDispatch = threadDispatch;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(delay);
            ArrayList<T> listEntity = Factory.getListEntity(clazz, size);
            DispatcherHolder.get(threadDispatch).dispatcher(listEntity, callback);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

Factory数据生产类

package com.august1996.core;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import com.august1996.anno.Type;
import com.august1996.parser.DefaultParser;
import com.august1996.parser.HolderParser;

public class Factory {
    static {
        mParser = new HashMap<>();
        initDefautParser();
    }
    private static Map<Class<? extends TypeParser>, TypeParser> mParser;

    /**
     * 获取多个Entity
     * 
     * @param cls
     *            Entity的类
     * @param size
     *            Entity的数量
     * @return
     */
    static <T> ArrayList<T> getListEntity(Class<T> cls, int size) {
        ArrayList<T> result = new ArrayList<T>();
        for (int i = 0; i < size; i++) {
            result.add(getEntity(cls));
        }
        return result;
    }

    /**
     * 获取单个Entity
     * 
     * @param cls
     * @return
     */
    static <T> T getEntity(Class<T> cls) {
        T obj = null;

        try {
            obj = cls.newInstance();

            Field[] declaredFields = cls.getDeclaredFields();
            for (Field field : declaredFields) { // 获取Entity类的所有属性
                field.setAccessible(true);
                Annotation[] annotations = field.getAnnotations(); // 获取属性的注解
                for (Annotation anno : annotations) {
                    if (anno instanceof Type) {
                        for (Map.Entry<Class<? extends TypeParser>, TypeParser> parser : mParser.entrySet()) {
                            Object value = parser.getValue().parser(((Type) anno).value()); // 获取到Type注解,使用TypeParser去解析类型
                            if (value != null) {
                                field.set(obj, value);
                            }
                        }
                    }
                }
            }

        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return obj;
    }

    /**
     * 初始化默认TypeParser
     */
    private static void initDefautParser() {
        addParser(new HolderParser());
    }

    public static void addParser(TypeParser typeParser) {
        mParser.put(typeParser.getClass(), typeParser);
    }

    public static TypeParser removeParser(Class<? extends TypeParser> clazz) {
        return mParser.remove(clazz);
    }

    public static void clearParser() {
        mParser.clear();
        initDefautParser();
    }
}

该类主要是用来产生数据

JBMock核心类

package com.august1996.core;

import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.august1996.callback.OnMockedCallback;
import com.august1996.thread.ThreadDispatcher;

public class JBMock {
    private static final JBMock sInstance = new JBMock();

    public static JBMock getInstance() {
        return sInstance;
    }

    private JBMock() {
        mExecutors = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    }

    private ExecutorService mExecutors;

    public <T> ArrayList<T> syncGet(Class<T> cls, int delay, int size) {
        try {
            Thread.sleep(delay);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return Factory.getListEntity(cls, size);
    }


    public <T> void asyncGet(Class<T> cls, int delay, int size, Class<? extends ThreadDispatcher> threadDispatcherCls,
            OnMockedCallback<T> callback) {
        mExecutors.submit(new MockRunnable<T>(cls, size, delay, threadDispatcherCls, callback));
    }


}

使用

新建一个UserEntity

package com.august1996.test;

import com.august1996.anno.Type;
import com.august1996.parser.DefaultType;

public class UserEntity {
    @Type("username")
    public String username;
    @Type("icon")
    public String icon;
    @Type("gender")
    public int gender;
    @Type(DefaultType.NOW_TIMESTAMP)
    public long regTime;

    @Override
    public String toString() {
        return "UserEntity [username=" + username + ", icon=" + icon + ", gender=" + gender + ", regTime=" + regTime
                + "]";
    }

}

测试代码

package com.august1996.test;

import java.util.ArrayList;

import com.august1996.callback.OnMockedCallback;
import com.august1996.core.JBMock;
import com.august1996.parser.ValueHolder;
import com.august1996.thread.DefaultDispatcher;

public class TestDemo {
    public static void main(String[] args) {
        ValueHolder.register("username", "小明", "小红");
        ValueHolder.register("username", "小花");

        ValueHolder.register("icon", "http://www.baidu.com/1.jpg", "http://www.baidu.com/2.jpg");
        ValueHolder.register("icon", "http://www.baidu.com/3.jpg");

        ValueHolder.register("gender", 1);
        ValueHolder.register("gender", 0);

        ArrayList<UserEntity> list = JBMock.getInstance().syncGet(UserEntity.class, 3000, 10);
        for (UserEntity entity : list) {
            System.out.println(entity.toString());
        }

        JBMock.getInstance().asyncGet(UserEntity.class, 3000, 5, DefaultDispatcher.class,
                new OnMockedCallback<UserEntity>() {

                    @Override
                    public void onMocked(ArrayList<UserEntity> listEntity) {
                        System.out.println(Thread.currentThread().getName());
                        for (UserEntity entity : listEntity) {
                            System.out.println(entity.toString());
                        }
                    }
                });

    }
}

输出

pool-1-thread-1
UserEntity [username=小花, icon=http://www.baidu.com/2.jpg, gender=0, regTime=1495010339777]
UserEntity [username=小明, icon=http://www.baidu.com/3.jpg, gender=1, regTime=1495010339779]
UserEntity [username=小明, icon=http://www.baidu.com/3.jpg, gender=1, regTime=1495010339779]
UserEntity [username=小红, icon=http://www.baidu.com/3.jpg, gender=0, regTime=1495010339779]
UserEntity [username=小花, icon=http://www.baidu.com/2.jpg, gender=1, regTime=1495010339779]

DefaultType.NOW_TIMESTAMP是什么

这是我们自定义的一个TypeParser,我们使用Factory.addParser添加就OK啦。

DefaultParser

package com.august1996.parser;

import com.august1996.core.TypeParser;

public class DefaultParser implements TypeParser {

    @Override
    public Object parser(String type) {
        if (DefaultType.NOW_TIMESTAMP.equals(type)) {
            return System.currentTimeMillis();
        }
        return null;
    }
}

DefaultType

package com.august1996.parser;

public class DefaultType {
    public static final String NOW_TIMESTAMP = "jb_now";
}

可自行扩展的地方

需要更多的扩展,实现ThreadDispatcher和TypeParser即可。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容