打造一個(gè)專注Java假數(shù)據(jù)的JBMock

前言

兩個(gè)月前寫過一篇文章基于Django設(shè)計(jì)的Restful MockServer禁灼,是基于網(wǎng)絡(luò)的轧简。很多人都覺得思路不錯(cuò),但是主要是太麻煩了匾二。本篇博文就根據(jù)Java開發(fā)一個(gè)假數(shù)據(jù)生成器哮独,稱為JBMock

Github地址

主要功能

  • 1察藐、提供同步皮璧、異步獲取數(shù)據(jù)

  • 2、異步獲取數(shù)據(jù)提供線程切換

原理圖

原理圖
  • 1分飞、使用反射悴务,獲取UserEntity的屬性和對(duì)應(yīng)注解(Type注解)

  • 2、獲取注解里面的值譬猫,然后使用TypeParser解析注解值讯檐,然后設(shè)置到對(duì)應(yīng)的屬性上

  • 3、使用ThreadDispatcher完成回調(diào)

代碼分析

包結(jié)構(gòu)

包結(jié)構(gòu)

代碼

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

這個(gè)注解的意義在于我們給某個(gè)屬性加上這個(gè)注解染服,然后根據(jù)類型去給屬性賦值别洪,例如value為username時(shí),那么這個(gè)屬性就會(huì)被賦上小明柳刮、小紅挖垛、小花等值

TypeParser類型解析器

package com.august1996.core;

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

該接口的作用是解析Type注解的value,返回對(duì)應(yīng)的值

OnMockedCallback回調(diào)

package com.august1996.callback;

import java.util.ArrayList;

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

當(dāng)異步模擬數(shù)據(jù)完成時(shí)秉颗,該回調(diào)帶著結(jié)果被執(zhí)行

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

該接口完成回調(diào)時(shí)的線程切換痢毒,本項(xiàng)目是JavaSDK的,如果接入Android時(shí)蚕甥,只需要繼承自該接口使用Handler就能完成主線程切換

DefaultDispatcher分配默認(rèn)線程

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

}

該類是最簡(jiǎn)單的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菇怀,可以通過注冊(cè)不同的ThreadDispatcher凭舶,使JBMock可以調(diào)用其他自己編寫的ThreadDispatcher

ValueHolder類型數(shù)據(jù)管理器

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

該類負(fù)責(zé)管理Type注解對(duì)應(yīng)的數(shù)據(jù)晌块,例如如果想要當(dāng)屬性的Type的value為username時(shí),JBMock就會(huì)給這個(gè)屬性賦值小明库快、小紅、小花等內(nèi)容钥顽,那么就需要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);
    }

}

該類為默認(rèn)的Parser之一义屏,結(jié)合Type注解ValueHolder根據(jù)類型解析數(shù)據(jù)

MockRunnable異步任務(wù)

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數(shù)據(jù)生產(chǎn)類

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;

    /**
     * 獲取多個(gè)Entity
     * 
     * @param cls
     *            Entity的類
     * @param size
     *            Entity的數(shù)量
     * @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;
    }

    /**
     * 獲取單個(gè)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;
    }

    /**
     * 初始化默認(rèn)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();
    }
}

該類主要是用來產(chǎn)生數(shù)據(jù)

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


}

使用

新建一個(gè)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
                + "]";
    }

}

測(cè)試代碼

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是什么

這是我們自定義的一個(gè)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";
}

可自行擴(kuò)展的地方

需要更多的擴(kuò)展,實(shí)現(xiàn)ThreadDispatcher和TypeParser即可奶浦。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末兄墅,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子澳叉,更是在濱河造成了極大的恐慌隙咸,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,997評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件成洗,死亡現(xiàn)場(chǎng)離奇詭異五督,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)瓶殃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門充包,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人遥椿,你說我怎么就攤上這事基矮。” “怎么了冠场?”我有些...
    開封第一講書人閱讀 163,359評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵家浇,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我碴裙,道長(zhǎng)蓝谨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,309評(píng)論 1 292
  • 正文 為了忘掉前任青团,我火速辦了婚禮譬巫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘督笆。我一直安慰自己芦昔,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評(píng)論 6 390
  • 文/花漫 我一把揭開白布娃肿。 她就那樣靜靜地躺著咕缎,像睡著了一般珠十。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上凭豪,一...
    開封第一講書人閱讀 51,258評(píng)論 1 300
  • 那天焙蹭,我揣著相機(jī)與錄音,去河邊找鬼嫂伞。 笑死孔厉,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的帖努。 我是一名探鬼主播撰豺,決...
    沈念sama閱讀 40,122評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼拼余!你這毒婦竟也來了污桦?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,970評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤匙监,失蹤者是張志新(化名)和其女友劉穎凡橱,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體亭姥,經(jīng)...
    沈念sama閱讀 45,403評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡梭纹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了致份。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片变抽。...
    茶點(diǎn)故事閱讀 39,769評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖氮块,靈堂內(nèi)的尸體忽然破棺而出绍载,到底是詐尸還是另有隱情,我是刑警寧澤滔蝉,帶...
    沈念sama閱讀 35,464評(píng)論 5 344
  • 正文 年R本政府宣布击儡,位于F島的核電站,受9級(jí)特大地震影響蝠引,放射性物質(zhì)發(fā)生泄漏阳谍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評(píng)論 3 327
  • 文/蒙蒙 一螃概、第九天 我趴在偏房一處隱蔽的房頂上張望矫夯。 院中可真熱鬧,春花似錦吊洼、人聲如沸训貌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)递沪。三九已至豺鼻,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間款慨,已是汗流浹背儒飒。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留檩奠,地道東北人桩了。 一個(gè)月前我還...
    沈念sama閱讀 47,831評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像笆凌,于是被迫代替她去往敵國(guó)和親圣猎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子士葫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理乞而,服務(wù)發(fā)現(xiàn),斷路器慢显,智...
    卡卡羅2017閱讀 134,654評(píng)論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,806評(píng)論 6 342
  • 1.頁(yè)面跳轉(zhuǎn)。 第一部分: 頁(yè)面2: .state('tab.chats', { url: '/chats', c...
    紫荊逸閱讀 13,528評(píng)論 5 1
  • 寫之前先廢話嘮嗑一頓应狱。 首先說明的是共郭,我并不是一個(gè) RN 的熟練使用者,接觸 RN 也就幾天時(shí)間疾呻,各種坑以及細(xì)節(jié)知...
    吟游雪人閱讀 332評(píng)論 2 1
  • 讀書除嘹,不僅要讀,還需要有輸出岸蜗。這輸出可以是你的讀書筆記尉咕,可以是你的人生感悟,當(dāng)然也可以是你的啰里啰嗦璃岳。 我認(rèn)為讀...
    TadeCathay閱讀 302評(píng)論 0 0