1. 程式人生 > >RxJava之五—— observeOn 與subscribeOn 的詳解

RxJava之五—— observeOn 與subscribeOn 的詳解

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

                       

轉載請標明出處:http://blog.csdn.net/xx326664162/article/details/51967967

   文章出自:薛瑄的部落格

你也可以檢視我的其他同類文章,也會讓你有一定的收貨!

關於RxJava,從表面上看起來很容易使用,但是如果理解不夠深刻,使用過程中,往往會出現一些問題,所以我寫了五篇文章,從入門到精通,從簡單的使用到部分原始碼詳解,希望能給讀者一個質的飛躍:
1、RxJava之一——一次性學會使用RxJava RxJava簡單的使用和使用它的好處
2、RxJava之二——Single和Subject 與Observable舉足輕重的類,雖然用的少,但應該知道
3、RxJava之三——RxJava 2.0 全部操作符示例 
4、RxJava之四—— Lift()詳解

  想要了解Operators,Lift()一定要學習
5、RxJava之五—— observeOn()與subscribeOn()的詳解Scheduler執行緒切換的原理
6、RxJava之六——RxBus 通過RxJava來替換EventBus

為什麼多次呼叫subscribeOn()卻只有第一個起作用?
為什麼多次呼叫observeOn()卻可以切換到不同執行緒
observeOn()後能不能再次呼叫subscribeOn()?

如果你有這些疑問,那接下來的內容必定能解決你心頭的疑惑

subscribeOn()和observeOn()的區別

subscribeOn()和observeOn()都是用來切換執行緒用的

  • subscribeOn()改變呼叫它之前程式碼的執行緒
  • observeOn()改變呼叫它之後程式碼的執行緒

這裡給出下面示例中用到的兩個函式

//用指定的名稱新建一個執行緒public static Scheduler getNamedScheduler(final String name) {        return Schedulers.from(Executors.newCachedThreadPool(new ThreadFactory() {            @Override            public Thread newThread(@android.support.annotation.NonNull Runnable runnable) {                return new Thread(runnable, name);            }        }));    }//列印當前執行緒的名稱public static void threadInfo(String caller) {        System.out.println(caller + " => " + Thread.currentThread().getName());    }
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

一、subscribeOn()

在講解他的原理之前,先來一個簡單的例子,有個感性認識,學起來更輕鬆

先說結論:subscribeOn 作用於該操作符之前的 Observable 的建立操符作以及 doOnSubscribe 操作符 ,換句話說就是 doOnSubscribe 以及 Observable 的建立操作符總是被其之後最近的 subscribeOn 控制 。沒看懂不要緊,看下面程式碼和圖你就懂了。

這裡寫圖片描述

Observable        .create(new Observable.OnSubscribe<String>() {            @Override            public void call(Subscriber<? super String> subscriber) {                threadInfo("OnSubscribe.call()");                subscriber.onNext("RxJava");            }        })        .subscribeOn(getNamedScheduler("create之後的subscribeOn"))        .doOnSubscribe(() -> threadInfo(".doOnSubscribe()-1"))        .subscribeOn(getNamedScheduler("doOnSubscribe1之後的subscribeOn"))        .doOnSubscribe(() -> threadInfo(".doOnSubscribe()-2"))        .subscribe(s -> {            threadInfo(".onNext()");            System.out.println(s + "-onNext");        });
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

結果如下:

.doOnSubscribe()-2 => main.doOnSubscribe()-1 => doOnSubscribe1之後的subscribeOnOnSubscribe.call() => create之後的subscribeOn.onNext() => create之後的subscribeOnRxJava-onNext
   
  • 1
  • 2
  • 3
  • 4
  • 5

3號框中的.doOnSubscribe(() -> threadInfo(“.doOnSubscribe()-2”)) 的之後由於沒有subscribeOn操作符所以回撥到該段程式碼被呼叫的執行緒(即主執行緒)

由於 subscribe 之前 沒有 使用observeOn 指定Scheduler,所以.onNext()的執行緒是和OnSubscribe.call()使用相同的Scheduler 。


下面通過原始碼來分析一下:

1、示例程式碼:

 Observable                .create(new Observable.OnSubscribe<String>() {                    @Override                    public void call(Subscriber<? super String> subscriber) {                        subscriber.onNext("a");                        subscriber.onNext("b");                        subscriber.onCompleted();                    }                })                .subscribeOn(Schedulers.io())                .subscribe(new Observer<String>() {                    @Override                    public void onCompleted() {                    }                    @Override                    public void onError(Throwable e) {                    }                    @Override                    public void onNext(String integer) {                        System.out.println(integer);                    }                });
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

執行如下:

ab
   
  • 1
  • 2

2、subscribeOn()原始碼

public final Observable<T> subscribeOn(Scheduler scheduler) {        if (this instanceof ScalarSynchronousObservable) {            return ((ScalarSynchronousObservable<T>)this).scalarScheduleOn(scheduler);        }        return create(new OperatorSubscribeOn<T>(this, scheduler));    }
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

很明顯,會走if之外的方法。

在這裡我們可以看到,又建立了一個OperatorSubscribeOn物件,但建立時傳入的引數為OperatorSubscribeOn(this,scheduler),我們看一下此物件以及其對應的構造方法

3、create()的原始碼:

public static <T> Observable<T> create(OnSubscribe<T> f) {        return new Observable<T>(hook.onCreate(f));    }
   
  • 1
  • 2
  • 3

我們看到這個方法,使用OperatorSubscribeOn這個類,來建立一個新的Observable,那就把它叫做Observable_2,把原來的Observable叫做Observable_1

4、OperatorSubscribeOn類的原始碼:

public final class OperatorSubscribeOn<T> implements OnSubscribe<T> {    final Scheduler scheduler;    final Observable<T> source;    public OperatorSubscribeOn(Observable<T> source, Scheduler scheduler) {        this.scheduler = scheduler;        this.source = source;    }    @Override    public void call(final Subscriber<? super T> subscriber) {        final Worker inner = scheduler.createWorker();        subscriber.add(inner);        inner.schedule(new Action0() {            @Override            public void call() {                final Thread t = Thread.currentThread();                Subscriber<T> s = new Subscriber<T>(subscriber) {                    @Override                    public void onNext(T t) {                        subscriber.onNext(t);                    }                    @Override                    public void onError(Throwable e) {                        try {                            subscriber.onError(e);                        } finally {                            inner.unsubscribe();                        }                    }                    @Override                    public void onCompleted() {                        try {                            subscriber.onCompleted();                        } finally {                            inner.unsubscribe();                        }                    }                    @Override                    public void setProducer(final Producer p) {                        subscriber.setProducer(new Producer() {                            @Override                            public void request(final long n) {                                if (t == Thread.currentThread()) {                                    p.request(n);                                } else {                                    inner.schedule(new Action0() {                                        @Override                                        public void call() {                                            p.request(n);                                        }                                    });                                }                            }                        });                    }                };                source.unsafeSubscribe(s);            }        });    }}
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  1. OperatorSubscribeOn類implements 了Onsubscribe介面,並實現call()方法
  2. OperatorSubscribeOn的構造方法,
    • 儲存了Observable物件,就是呼叫了subscribeOn()方法的Observable物件
    • 並儲存了Scheduler物件。

這裡做個總結。

 

把Observable.create()建立的稱之為Observable_1,OnSubscribe_1。
  把subscribeOn()建立的稱之為Observable_2,OnSubscribe_2

  • Observable_1是由示例程式碼的第1、2行建立的

  • OperatorSubscribeOn類是implements  Onsubscribe介面的,所以可以當做Onsubscribe類使用。(OnSubscribe_2)

  • 並且OnSubscribe_2中儲存了Observable_1的應用,即source。(在OperatorSubscribeOn原始碼的第8行)

  • subscribeOn()原始碼的倒數第二行,create(new OperatorSubscribeOn<T>(this, scheduler))返回新建立的Observable_2物件。

4.1、分析call()方法。

  • inner.schedule()改變了執行緒,此時Action的call()執行在指定的執行緒中。
  • 示例程式碼中的Subscriber包裝了一層,賦給物件S(Subscriber_2)。見上面程式碼21行。
  • source.unsafeSubscribe(s);,
    • 注意:source是Observable_1物件,這裡的s就是Subscriber_2
    • 因為呼叫過subscribeOn(Schedulers.io())後,返回Observable_2物件,所以示例程式碼第13行程式碼的subscribe()就是Observable_2.subscribe(),也就是執行OnSubscribe_2的call()方法(即OperatorSubscribeOn類的原始碼的第12行)。

4.2 看一下source.unsafeSubscribe(s);(第65行)程式碼都做了什麼

這裡的source就是Observable_1,s是Subscriber_2

unsafeSubscribe()原始碼:

public final Subscription unsafeSubscribe(Subscriber<? super T> subscriber) {        try {            // new Subscriber so onStart it            subscriber.onStart();            // allow the hook to intercept and/or decorate            hook.onSubscribeStart(this, onSubscribe).call(subscriber);            return hook.onSubscribeReturn(subscriber);        } catch (Throwable e) {            // special handling for certain Throwable/Error/Exception types            Exceptions.throwIfFatal(e);            // if an unhandled error occurs executing the onSubscribe we will propagate it            try {                subscriber.onError(hook.onSubscribeError(e));            } catch (Throwable e2) {                Exceptions.throwIfFatal(e2);                // if this happens it means the onError itself failed (perhaps an invalid function implementation)                // so we are unable to propagate the error correctly and will just throw                RuntimeException r = new RuntimeException("Error occurred attempting to subscribe [" + e.getMessage() + "] and then again while trying to pass to onError.", e2);                // TODO could the hook be the cause of the error in the on error handling.                hook.onSubscribeError(r);                // TODO why aren't we throwing the hook's return value.                throw r;            }            return Subscriptions.unsubscribed();        }    }
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

關鍵程式碼:

hook.onSubscribeStart(this, onSubscribe).call(subscriber);
   
  • 1

該方法即呼叫了OnSubscribe_1.call()方法。

注意,此時的call()方法在我們指定的執行緒中執行。起到了改變執行緒的作用。

對於以上執行緒,我們可以總結,其有如下流程:

  • Observable.create() : 建立了Observable_1和OnSubscribe_1;

  • subscribeOn(): 建立Observable_2和OperatorSubscribeOn(OnSubscribe_2),同時OperatorSubscribeOn儲存了Observable_1的引用。

  • 示例程式碼中的subscribe(Observer) 實際上就是呼叫Observable_2.subscribe(Observer):

    • 呼叫OperatorSubscribeOn的call()。call()改變了執行緒的執行,並且呼叫了Observable_1.unsafeSubscribe(s);
    • Observable_1.unsafeSubscribe(s);,該方法的實現中呼叫了OnSubscribe_1的call()。

這樣就實現了在指定執行緒執行OnSubscribe的call()函式,無論我們的subscribeOn()放在哪裡,他改變的是subscribe()的過程,而不是onNext()的過程。

那麼如果有多個subscribeOn(),那麼執行緒會怎樣執行呢。如果按照我們的邏輯,有以下程式

Observable.just("ss")                 .subscribeOn(Schedulers.io())   // ----1---                .subscribeOn(Schedulers.newThread()) //----2----                .subscribe(new Action1<String>() {                    @Override                    public void call(String s) {                    }                });
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

那麼,我們根據之前的原始碼分析其執行邏輯。

  • Observable.just(“ss”),建立Observable,OnSubscribe

  • Observable_1.subscribeOn(Schedulers.io()):建立Observable_1,OperatorSubscribeOn_1並儲存Observable的引用。

  • Observable_2.subscribeOn(Schedulers.newThread()):建立Observable_2,OperatorSubscribeOn_2並儲存Observable_1的引用。

  • Observable_3.subscribe():

    • 呼叫OperatorSubscribeOn_2.call(),改變執行緒為Schedulers.newThread()。
    • 呼叫OperatorSubscribeOn_1.call(),改變執行緒為Schedulers.io()。
    • 呼叫OnSubscribe.call(),此時call()執行在Schedulers.io()。

根據以上邏輯分析,會按照1的執行緒進行執行。

這裡寫圖片描述

二、observeOn()

先說結論:observeOn作用於該操作符之後操作符直到出現新的observeOn操作符

這裡寫圖片描述

舉個例子:

Observable.just("RxJava")        .observeOn(getNamedScheduler("map之前的observeOn"))        .map(s -> {            threadInfo(".map()-1");            return s + "-map1";        })        .map( s -> {            threadInfo(".map()-2");            return s + "-map2";        })        .observeOn(getNamedScheduler("subscribe之前的observeOn"))        .subscribe(s -> {            threadInfo(".onNext()");            System.out.println(s + "-onNext");        });
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

結果如下:

.map()-1 => map之前的observeOn.map()-2 => map之前的observeOn.onNext() => subscribe之前的observeOnRxJava-map1-map2-onNext
   
  • 1
  • 2
  • 3
  • 4

下面通過原始碼來進行分析:

1、observeOn()原始碼

public final Observable<T> observeOn(Scheduler scheduler) {        return observeOn(scheduler, RxRingBuffer.SIZE);    }public final Observable<T> observeOn(Scheduler scheduler, int bufferSize) {        return observeOn(scheduler, false, bufferSize);    }public final Observable<T> observeOn(Scheduler scheduler, boolean delayError, int bufferSize) {        if (this instanceof ScalarSynchronousObservable) {            return ((ScalarSynchronousObservable<T>)this).scalarScheduleOn(scheduler);        }        return lift(new OperatorObserveOn<T>(scheduler, delayError, bufferSize));    }
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

這裡引出了lift()函式

public final <R> Observable<R> lift(final Operator<? extends R, ? super T> operator) {        return new Observable<R>(new OnSubscribeLift<T, R>(onSubscribe, operator));    }
   
  • 1
  • 2
  • 3

關於lift的詳細介紹,如果不明白lift的原理,參考這裡:RxJava 之二—— Lift()詳解

用OperatorObserveOn物件,建立OnSubscribeLift物件(實現了OnSubscribe介面),接著建立Observable物件。為了加以區分,這裡我們把OnSubscribeLift叫做OnSubscribe_2,Observable叫做Observable_2。

2、OperatorObserveOn程式碼:

public final class OperatorObserveOn<T> implements Operator<T, T> {    private final Scheduler scheduler;    private final boolean delayError;    private final int bufferSize;    /**     * @param scheduler the scheduler to use     * @param delayError delay errors until all normal events are emitted in the other thread?     */    public OperatorObserveOn(Scheduler scheduler, boolean delayError) {        this(scheduler, delayError, RxRingBuffer.SIZE);    }    /**     * @param scheduler the scheduler to use     * @param delayError delay errors until all normal events are emitted in the other thread?     * @param bufferSize for the buffer feeding the Scheduler workers, defaults to {@code RxRingBuffer.MAX} if <= 0     */    public OperatorObserveOn(Scheduler scheduler, boolean delayError, int bufferSize) {        this.scheduler = scheduler;        this.delayError = delayError;        this.bufferSize = (bufferSize > 0) ? bufferSize : RxRingBuffer.SIZE;    }    @Override    public Subscriber<? super T> call(Subscriber<? super T> child) {        if (scheduler instanceof ImmediateScheduler) {            // avoid overhead, execute directly            return child;        } else if (scheduler instanceof TrampolineScheduler) {            // avoid overhead, execute directly            return child;        } else {            ObserveOnSubscriber<T> parent = new ObserveOnSubscriber<T>(scheduler, child, delayError, bufferSize);            parent.init();            return parent;        }    }    public static <T> Operator<T, T> rebatch(final int n) {        return new Operator<T, T>() {            @Override            public Subscriber<? super T> call(Subscriber<? super T> child) {                ObserveOnSubscriber<T> parent = new ObserveOnSubscriber<T>(Schedulers.immediate(), child, false, n);                parent.init();                return parent;            }        };    }    /** Observe through individual queue per observer. */    static final class ObserveOnSubscriber<T> extends Subscriber<T> implements Action0 {        final Subscriber<? super T> child;        final Scheduler.Worker recursiveScheduler;        final NotificationLite<T> on;        final boolean delayError;        final Queue<Object> queue;        /** The emission threshold that should trigger a replenishing request. */        final int limit;        // the status of the current stream        volatile boolean finished;        final AtomicLong requested = new AtomicLong();        final AtomicLong counter = new AtomicLong();        /**          * The single exception if not null, should be written before setting finished (release) and read after         * reading finished (acquire).         */        Throwable error;        /** Remembers how many elements have been emitted before the requests run out. */        long emitted;        // do NOT pass the Subscriber through to couple the subscription chain ... unsubscribing on the parent should        // not prevent anything downstream from consuming, which will happen if the Subscription is chained        public ObserveOnSubscriber(Scheduler scheduler, Subscriber<? super T> child, boolean delayError, int bufferSize) {            this.child = child;            this.recursiveScheduler = scheduler.createWorker();            this.delayError = delayError;            this.on = NotificationLite.instance();            int calculatedSize = (bufferSize > 0) ? bufferSize : RxRingBuffer.SIZE;            // this formula calculates the 75% of the bufferSize, rounded up to the next integer            this.limit = calculatedSize - (calculatedSize >> 2);            if (UnsafeAccess.isUnsafeAvailable()) {                queue = new SpscArrayQueue<Object>(calculatedSize);            } else {                queue = new SpscAtomicArrayQueue<Object>(calculatedSize);            }            // signal that this is an async operator capable of receiving this many            request(calculatedSize);        }        void init() {            // don't want this code in the constructor because `this` can escape through the             // setProducer call            Subscriber<? super T> localChild = child;            localChild.setProducer(new Producer() {                @Override                public void request(long n) {                    if (n > 0L) {                        BackpressureUtils.getAndAddRequest(requested, n);                        schedule();                    }                }            });            localChild.add(recursiveScheduler);            localChild.add(this);        }        @Override        public void onNext(final T t) {            if (isUnsubscribed() || finished) {                return;            }            if (!queue.offer(on.next(t))) {                onError(new MissingBackpressureException());                return;            }            schedule();        }        @Override        public void onCompleted() {            if (isUnsubscribed() || finished) {                return;            }            finished = true;            schedule();        }        @Override        public void onError(final Throwable e) {            if (isUnsubscribed() || finished) {                RxJavaHooks.onError(e);                return;            }            error = e;            finished = true;            schedule();        }        protected void schedule() {            if (counter.getAndIncrement() == 0) {                recursiveScheduler.schedule(this);            }        }        // only execute this from schedule()        @Override        public void call() {            long missed = 1L;            long currentEmission = emitted;            // these are accessed in a tight loop around atomics so            // loading them into local variables avoids the mandatory re-reading            // of the constant fields            final Queue<Object> q = this.queue;            final Subscriber<? super T> localChild = this.child;            final NotificationLite<T> localOn = this.on;            // requested and counter are not included to avoid JIT issues with register spilling            // and their access is is amortized because they are part of the outer loop which runs            // less frequently (usually after each bufferSize elements)            for (;;) {                long requestAmount = requested.get();                while (requestAmount != currentEmission) {                    boolean done = finished;                    Object v = q.poll();                    boolean empty = v == null;                    if (checkTerminated(done, empty, localChild, q)) {                        return;                    }                    if (empty) {                        break;                    }                    localChild.onNext(localOn.getValue(v));                    currentEmission++;                    if (currentEmission == limit) {                        requestAmount = BackpressureUtils.produced(requested, currentEmission);                        request(currentEmission);                        currentEmission = 0L;                    }                }                if (requestAmount == currentEmission) {                    
            
           

相關推薦

RxJava—— observeOn()subscribeOn()的

你也可以檢視我的其他同類文章,也會讓你有一定的收貨! 為什麼多次呼叫subscribeOn()卻只有第一個起作用? 為什麼多次呼叫observeOn()卻可以切換到不同執行緒 observeOn()後能不能再次呼叫subscribeOn()? 如

RxJava—— observeOn subscribeOn

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

RxJava三—— observeOn()subscribeOn()的

你也可以檢視我的其他同類文章,也會讓你有一定的收貨! 為什麼多次呼叫subscribeOn()卻只有第一個起作用?  為什麼多次呼叫observeOn()卻可以切換到不同執行緒  observeOn()後能不能再次呼叫subscribeOn()?

java資料結構:ArrayListLinkedList

前篇博文講到了Java底層怎麼知道ArrayList和LinkedList哪個是隨機訪問哪個是順序訪問的,本片博文主要介紹各自的資料特點。 ArrayList: 從他的這個建構函式我們可以知道他的底層實現就是一個Object物件陣列。 第二個建構函式構造一個空的

MySQL高階 索引失效優化

案例所用的表結構、索引、與資料如下: 索引失效與優化 1、全值匹配我最愛 2、最佳左字首法則(帶頭索引不能死,中間索引不能斷) 如果索引了多個列,要遵守最佳左字首法則。指的是查詢從索引的最左前列開始 並且 不跳過索引中的列。  正確的示例參考上圖。 錯誤的示例: 

.Neter玩轉Linux系列:crontab使用和Linux的程序管理以及網路狀態監控

基礎篇 實戰篇 一、crontab使用詳解  概述:任務排程:是指系統在某個時間執行的特定的命令或程式。 任務排程分類:       (1)系統工作:有些重要的工作必須周而 復始地執行。       (2)個別使用者工作:個別使用者可能希望執 行某些程式。

Android UI種基本佈局

Android佈局是應用介面開發的重要一環,在Android中,共有五種佈局方式,分別是: LinearLayout (線性佈局) RelativeLayout(相對佈局) FrameLayout(幀佈局) TableLayout(表格佈局)GridLay

[js高手路]原型對象(prototype)原型鏈相關屬性方法

隱式 之前 username tar uname create pro getproto .get 一,instanceof: instanceof檢測左側的__proto__原型鏈上,是否存在右側的prototype原型. 我在之前的兩篇文章 [js高手之路]構造函數的基

MyBatisMapper XML 文件()-自動映射查詢結果

java mybatis 正如你在前面一節看到的,在簡單的場景下,MyBatis可以替你自動映射查詢結果。 如果遇到復雜的場景,你需要構建一個result map。 但是在本節你將看到,你也可以混合使用這兩種策略。 讓我們到深一點的層面上看看自動映射是怎樣工作的。 當自動映射查詢結果時,MyBatis

SpringMVC 框架系列組件概述配置

align 概述 handle ont htm 配置文件 掃描器 springmvc 解析 在上一篇文章 SpringMVC 框架系列之初識與入門實例 的實例中,我們已經知道,SpringMVC 框架是一個 web 層的框架,本篇文章就詳細解釋一下 SpringMVC 框架

一看就懂!【英雄聯盟銳雯】 Python 設計模式門面模式

【網路配圖】 設計模式(Design Pattern)是一套被反覆使用、多數人知曉的、經過分類的、程式碼設計經驗的總結。使用設計模式的目的:為了程式碼可重用性、讓程式碼更容易被他人理解、保證程式碼可靠性。設計模式使程式碼編寫真正工程化;設計模式是軟體工程的基石脈絡,如同大廈的結構一樣

android 繪圖PathPaint[轉]

/** * Paint類介紹 * * Paint即畫筆,在繪圖過程中起到了極其重要的作用,畫筆主要儲存了顏色, * 樣式等繪製資訊,指定了如何繪製文字和圖形,畫筆物件有很多設定方法, * 大體上可以分為兩類,一類與圖形繪製相關,一類與文字繪製相關。 * * * 1.圖形繪製 * setARG

OSI七層TCP/IP層網路架構

(1)OSI七層模型 OSI中的層 功能 TCP/IP協議族 應用層 檔案傳輸,電子郵件,檔案服務,虛擬終端 TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 表示層 資料格式化,程式碼轉換,資料加密 沒有協議 會話層 解除或建立與別的接點的聯絡 沒有協

netty原始碼分析-SimpleChannelInboundHandlerChannelInboundHandlerAdapter(6)

每一個Handler都一定會處理出站或者入站(也可能兩者都處理)資料,例如對於入站的Handler可能會繼承SimpleChannelInboundHandler或者ChannelInboundHandlerAdapter,而SimpleChannelIn

常用註解@PathVariable@RequestParam

@PathVariable和@RequestParam 首先說一下Request瞭解一下請求引數是什麼 在訪問各種各樣網站時,經常會發現網站的URL的最後一部分形如:?xxxx=yyyy&zzzz=wwww。這就是HTTP協議中的Request引數,它有什麼用呢?先來看一個例子:

RxJava observeOn()subscribeOn()的關係

RxJava系列教程: observeOn和subscribeOn都是對observable的一種操作,區別就是subscribeOn改變了observable本身產生事件的schedule以及發出事件後相關處理事件的程式所在的scheduler,而o

計網--OSI七層TCP/IP層網路架構

OSI和TCP/IP是很基礎但又非常重要的網路基礎知識,理解得透徹對運維工程師來說非常有幫助。今天偶又複習了一下: (1)OSI七層模型 OSI中的層 功能 TCP/IP協議族 應用層 檔案傳輸,電子郵件,檔案服務,虛擬終端 TFTP,HTTP,SNMP,FT

JavaWeb檔案上傳下載

檔案上傳 檔案上傳概述 實現web開發中的檔案上傳功能,需完成如下二步操作: (1)在web頁面中新增上傳輸入項; (2)在servlet中讀取上傳檔案的資料,並儲存到本地硬碟中。 如何在web頁面中新增上傳輸入項?<input type

Android 開發的 RxJava RxAndroid以及RXbus

這篇文章的目的有兩個: 1. 給對 RxJava 感興趣的人一些入門的指引 2. 給正在使用 RxJava 但仍然心存疑惑的人一些更深入的解析在正文開始之前的最後,放上 GitHub 連結和引入依賴的 gradle 程式碼: Github: https://github.co

Java io流 FileOutputStreamFileInputStream

FileOutputStream 檔案輸出流 方法程式碼詳解: public class Demo01 { public static void main(String[] a