Dagger2 依賴的接力游戲(二):依賴模型的建立和實(shí)現(xiàn)

文接《Dagger2 依賴的接力游戲(一)》

本篇代碼收錄在項(xiàng)目的chapter2分支

接下來(lái)我們要討論依賴關(guān)系及其解決方案,在這之前我們約定幾個(gè)名詞和符號(hào),在第二篇的例子當(dāng)中,我們知道Car依賴Engine,這是最簡(jiǎn)單的依賴關(guān)系模型,A依賴B,我們把A叫做需求方,B叫需求對(duì)象。

所以我們約定幾個(gè)名詞和符號(hào):

依賴模型的定義

  • 需求方R(requirement)
  • 需求對(duì)象O(object)
  • 依賴關(guān)系D(dependency)
    在表達(dá)式里也使用符號(hào) -->來(lái)表示依賴方向,如果A依賴B,則可以表示為A --D(a,b)--> B,如果不強(qiáng)調(diào)Dab的存在,則可以簡(jiǎn)化為A-->B。

那么一個(gè)最簡(jiǎn)單的依賴模型可以表示為:

R--D(r,o)-->O

把我們例子中的類代入就得到:

R(Car)--D(Car,Engine)-->O(Engine)

如果我們?cè)贑ar類的構(gòu)造方法里直接創(chuàng)建Engine對(duì)象,這種實(shí)現(xiàn)就是上述的一個(gè)依賴模型的一比一還原。通過(guò)之前的討論,我們知道D(car,engine)這種對(duì)象的依賴關(guān)系是會(huì)隨著軟件的規(guī)模遞增,變得復(fù)雜難以維護(hù)的,所以我們需要使用依賴注入的方式來(lái)滿足需求。后面我們對(duì)它進(jìn)行了一個(gè)優(yōu)化,在入口處動(dòng)態(tài)創(chuàng)建Engine對(duì)象并傳遞給Car對(duì)象。那么在這個(gè)優(yōu)化過(guò)程當(dāng)中,實(shí)際上增加了一個(gè)角色:Object的提供者。所以我們?cè)黾右粋€(gè)定義:

  • 需求提供方P(provider)

那么上述的依賴模型就變成了:

R--D(r,p)-->P--D(p,o)-->O

代入我們例子中的類:

R(Car)--D(Car,Main)-->P(Main)--D(Main,Engine)-->O(Engine)

依賴注入及優(yōu)化的模型本質(zhì)

我們發(fā)現(xiàn)依賴模型更加復(fù)雜了,是的,不管我們?cè)诰幊讨惺褂檬謩?dòng)實(shí)現(xiàn)依賴注入也好,還是通過(guò)dagger2等工具實(shí)現(xiàn)依賴注入也好,其背后都會(huì)造成依賴模型的復(fù)雜化,這帶來(lái)的一個(gè)弊端就是會(huì)導(dǎo)致我們的軟件系統(tǒng)更加難以理解,邏輯更加難以跟蹤,因?yàn)槲覀兂艘斫鈽I(yè)務(wù)邏輯之外,還要理解依賴注入的過(guò)程。

但是我們看一下上述的優(yōu)化模型,其中的D(car,engin),已經(jīng)被D(car,main)+D(main,engin)替換了。也就是D(r,o)=D(r,p)+D(p,o)。我們注意到main是我們程序的入口,其層級(jí)為1,而car的構(gòu)造函數(shù)是我們程序的內(nèi)部邏輯,其層級(jí)為2。也就是說(shuō)我們通過(guò)這樣一個(gè)代換,雖然增加了依賴關(guān)系的模型,但是降低了依賴層級(jí)。這種依賴層級(jí)的降低,使得軟件系統(tǒng)結(jié)構(gòu)更加地松散、靈活。如果使用dagger2等依賴注入工具,最終我們可以將依賴層級(jí)降為0,即不使用代碼來(lái)維護(hù)依賴關(guān)系,而是使用注解,從而避免因依賴關(guān)系變更導(dǎo)致的軟件系統(tǒng)的變更,實(shí)現(xiàn)基于注解配置,擴(kuò)展軟件功能的效果。這也就是設(shè)計(jì)模式的總則,開閉原則。有過(guò)軟件重構(gòu)和擴(kuò)展經(jīng)驗(yàn)的同學(xué)應(yīng)該對(duì)這個(gè)有比較直觀的感受。

所以我們知道,依賴關(guān)系的優(yōu)化,并不是指模型簡(jiǎn)化,而是代碼層級(jí)的降低。而dagger2的本質(zhì)功能,就是通過(guò)注解聲明的依賴關(guān)系,幫我們生成模板類來(lái)完成這種依賴降級(jí)。下面我們來(lái)實(shí)際操作一把,看看如何使用dagger2將Car和Engine的依賴層級(jí)將為0。

使用Dagger2實(shí)現(xiàn)Engine的依賴注入

要實(shí)現(xiàn)依賴注入,首先我們要聲明一個(gè)依賴對(duì)象的需求R(engine),這個(gè)聲明就是通過(guò)@Inject注解來(lái)實(shí)現(xiàn)的,所以我們的Car類的實(shí)現(xiàn)如下:

public class Car {

    String mName;

    @Inject
    Engine mEngine;
    
    public Car(Engine engine){
        mEngine = engine;
    }

    public String getName(){
        return mName;
    }

    Engine getEngine(){
        return mEngine;
    }

}

聲明了需求之后,我們要聲明提供方P(engine)。提供方的聲明有兩種方式,一種是使用@Inject注解被依賴對(duì)象的構(gòu)造方法,一種是@Provide注解Module類的普通方法。我們先用第一種來(lái)實(shí)現(xiàn)。

class Engine {

    public final int CYLINDER_FUEL_COST = 10;

    int mCylinderNumbers;

    @Inject
    public Engine(){
        mCylinderNumbers = 1;
    }

    public Engine(int cylinderNumbers){
        this.mCylinderNumbers = cylinderNumbers;
    }


    public int getCylinderNumbers(){
        return mCylinderNumbers;
    }


    public void run(Fuel fuel){
        fuel.burn(getCylinderNumbers() * CYLINDER_FUEL_COST);
    }

}

這樣我們就聲明了一個(gè)完整的依賴關(guān)系,但是在Dagger2里,我們聲明的依賴關(guān)系是無(wú)法直接使用的,我們需要使用聲明組件(Component)作為依賴關(guān)系的容器,dagger2才會(huì)根據(jù)組件里要求的依賴關(guān)系,幫我們生成組件對(duì)應(yīng)的Dagger開頭工具類。我們使用@Component來(lái)聲明一個(gè)組件。

@Component
public interface EngineComponent {

    Engine getEngine();

}

這里有幾個(gè)問(wèn)題點(diǎn):

  1. 為什么組件是一個(gè)接口,而不是一個(gè)類?
  2. getEngine方法既不見(jiàn)調(diào)用方,也不見(jiàn)提供方的,Dagger如何綁定需求方和提供方?

這里我們先不做解答,我們先試試效果,構(gòu)建一下生成DaggerEngineComponent類,并用它為Car類創(chuàng)建Engine對(duì)象。

public class Main {

    public static void main(String[] args){

        Engine engine = DaggerEngineComponent.builder().build().getEngine();
        Car car = new Car(engine);
        System.out.println("cylinderNumbers : " + car.getEngine().getCylinderNumbers());

    }
}

運(yùn)行一下看到打?。?/p>

cylinderNumbers : 1
Process finished with exit code 0

說(shuō)明我們的Engine對(duì)象確實(shí)是由標(biāo)記的構(gòu)造函數(shù)創(chuàng)建的。但是我們看main方法,我們通過(guò)Dagger組件創(chuàng)建了Engine對(duì)象,再手動(dòng)構(gòu)造Car對(duì)象,并將engine傳遞給它,然后又取出來(lái)使用,這好像不關(guān)Car什么事???而且這也太麻煩了吧?

沒(méi)錯(cuò),這確實(shí)不關(guān)Car什么事,我們?cè)贑ar類內(nèi)部,使用@Inject聲明一個(gè)依賴,Dagger并不認(rèn)為這個(gè)依賴只能Car來(lái)使用,同時(shí)Dagger2也不會(huì)將這個(gè)依賴自動(dòng)注入到Car的內(nèi)部,需要我們手動(dòng)來(lái)實(shí)現(xiàn)。Dagger2永遠(yuǎn)只關(guān)心聲明的依賴類型、提供類型、依賴的容器類型,至于你在哪聲明,怎么注入Dagger2并不關(guān)心,也沒(méi)法關(guān)心。我看資料的時(shí)候,看到很多例子,一開始就是直接調(diào)用組件對(duì)自身進(jìn)行注入的,像這樣:

XXXComponent.builder().build().inject(this);

其實(shí)很容易誤導(dǎo)我們,以為Dagger2對(duì)依賴關(guān)系的使用也進(jìn)行了約束和實(shí)現(xiàn),這是不利于我們清楚地認(rèn)知dagger2的。在上面的例子中,如果把依賴聲明放到Main中,也是可以正常工作的,有興趣的同學(xué)可以自己試驗(yàn)一把。

現(xiàn)在我們聲明了Engine的需求和提供,也通過(guò)Dagger2實(shí)現(xiàn)了自動(dòng)創(chuàng)建Engine對(duì)象,但是每次都需要手動(dòng)創(chuàng)建對(duì)象,再通過(guò)構(gòu)造方法注入到Car對(duì)象里,使用起來(lái)是很不方便的。實(shí)際上,從依賴關(guān)系的傳遞角度來(lái)看,它從原來(lái)的:

D(Car,Engin) = D(Car,Main) + D(Main,Engin)

變成了:

D(Car,Engin) = D(Car,Main) + D(Main,DaggerEngineComponent) + D(DaggerEngineComponent,Engine)

因?yàn)镈(DaggerEngineComponent,Engine)是我們用注解聲明,由Dagger2生成的,它的層級(jí)等于0,所以我們可以忽略掉這部分依賴。上述的模型就成了

D(Car,Engin) = D(Car,Main) + D(Main,DaggerEngineComponent)

這兩個(gè)依賴的代碼層級(jí)都是1,也就是說(shuō)我們還沒(méi)有實(shí)現(xiàn)完全的依賴托管,所以我們需要對(duì)這個(gè)依賴關(guān)系做進(jìn)一步的優(yōu)化。

從代碼里我們可以看到,現(xiàn)在這個(gè)依賴的存在是為了幫我們把Engine注入到Car的對(duì)象中,所以我們下一步就要使用Dagger2來(lái)進(jìn)行Engine的注入。

使用Dagger2實(shí)現(xiàn)Engine的注入

前面我們介紹了需求和提供的聲明,是通過(guò)注解來(lái)實(shí)現(xiàn)的。那要聲明一個(gè)注入,要使用什么注解呢?實(shí)際上就是同一個(gè)@Inject注解。我們?cè)贑ar的內(nèi)部,使用@Inject來(lái)聲明一個(gè)需求類型的時(shí)候,實(shí)際上我們也聲明了一個(gè)注入類型,如果我們要用表達(dá)式來(lái)符號(hào)化這種類型,依賴需求類型可以寫成:

R(Car,Engin)

為了表示注入類型,我們?cè)黾右粋€(gè)定義:

  • 注入關(guān)系I(Injector)
    I(a,b,c,...)表示a需要注入b,c,...類型的對(duì)象。

代入我們例子中的類,注入需求類型可以寫成

I(Car,Engine)

上一個(gè)小節(jié)我們說(shuō)了R(Car,Engine)只是聲明了Engine的需求,Dagger2生成的組件無(wú)法限制這種需求被誰(shuí)使用,其實(shí)可以理解為:

D(Car,Engine) = R(? , Engine) + I(Car, Engine)

所以我們上面的例子,其實(shí)只使用了R(?,Engine)這部分定義,現(xiàn)在我們要使用I(Car,Engine)的定義。因?yàn)檫@個(gè)注入類型,也是依賴關(guān)系定義的一種,所以我們也需要Component作為它的容器,才能使用。我們創(chuàng)建一個(gè)CarComponent:

@Component
public interface CarComponent {
  
    void inject(Car car);
}

這就聲明了注入類型的使用了,Dagger同樣會(huì)根據(jù)容器的聲明為我們生成Dagger開頭的組件。如果你想在EngineComponent里,通過(guò)這個(gè)方法來(lái)聲明使用這個(gè)注入類型,也是可以的。

public class Main {

    public static void main(String[] args){
        Car car = new Car(null);
        DaggerCarComponent.builder().build().inject(car);
        System.out.println("cylinderNumbers : " + car.getEngine().getCylinderNumbers());
    }
}

運(yùn)行結(jié)果和上面的調(diào)用方式是一樣的,這里就不貼了。發(fā)現(xiàn)沒(méi)有,這里我們沒(méi)有再通過(guò)組件創(chuàng)建Engine對(duì)象了,很神奇是不是?

是的,如果我們?cè)诮M件中聲明了注入或者需求類型,Dagger2會(huì)根據(jù)聲明的類型自動(dòng)查找這個(gè)類型依賴的所有的其他需求類型,并在注入組件中幫我們一一實(shí)現(xiàn)需求的創(chuàng)建(如果需求類型找不到,就會(huì)報(bào)編譯錯(cuò)誤),后面我們進(jìn)行原理分析的時(shí)候再進(jìn)行講解,這里先記住這點(diǎn)。也正是因?yàn)檫@個(gè)自動(dòng)實(shí)現(xiàn)的邏輯,讓初學(xué)者在dagger2的工作原理的理解上頗費(fèi)周章,希望這樣講解過(guò)后,讀者可以理解需求類型和注入類型是相互獨(dú)立的兩個(gè)邏輯,是可以靈活使用的。

現(xiàn)在我們?cè)賮?lái)看看依賴關(guān)系的模型,它變成了:

D(Car,Engine) = D(Car,Main) + D(Main, DaggerCarComponent)

這個(gè)依賴模型的層級(jí)同樣是1,也就是說(shuō)雖然我們使用Dagger進(jìn)行了注入,但是這個(gè)注入過(guò)程還是手動(dòng)實(shí)現(xiàn)的。有一些例子會(huì)在Car的構(gòu)造方法里,調(diào)用組件對(duì)自身進(jìn)行注入,像這樣:

public class Car {

    @Inject
    Engine mEngine;

    public Car(){
        DaggerCarComponent.builder().build().inject(car);
    }

}

那么在main方法里,會(huì)更加簡(jiǎn)潔,直接new 一個(gè) Car對(duì)象出來(lái)就可以了。這個(gè)在使用上是減少了一步,更加方便了,但是我們看到依賴模型上是這樣的:

D(Car,Engine) = D(Car,DaggerCarComponent)

只是使用DaggerCarComponent替換了Engine,依賴的代碼層級(jí)是2,也就是說(shuō)如果我的CarComponent組件發(fā)生了改變,還是要回頭來(lái)修改Car的代碼,沒(méi)有解決根本問(wèn)題。用依賴注入的術(shù)語(yǔ)來(lái)說(shuō)就是,一個(gè)類不應(yīng)該了解它所依賴的對(duì)象是如何被創(chuàng)建和注入的,而這里違反了第二個(gè)原則。

下一步要怎么優(yōu)化呢?我們來(lái)分析一下,上面這個(gè)依賴模型,我們發(fā)現(xiàn) D(Car,Main) 的存在是為了幫我們創(chuàng)建Car對(duì)象, D(Car,DaggerCarComponent)的存在是為了幫我們注入Engine對(duì)象,我們分別對(duì)它倆動(dòng)刀子。首先是Car的創(chuàng)建,我們?cè)贛ain方法里聲明一個(gè)R(Main,Car),然后使用@Inject注解Car構(gòu)造方法,聲明Car的提供。

public class Car {

    String mName;

    @Inject
    Engine mEngine;

    @Inject
    public Car(Engine engine){
        mEngine = engine;
    }

    public String getName(){
        return mName;
    }

    Engine getEngine(){
        return mEngine;
    }

}

這里我們使用了Dagger2的一個(gè)特性。因?yàn)槲覀兊臉?gòu)造方法是帶參數(shù)的,Dagger2會(huì)根據(jù)參數(shù)類型,自動(dòng)創(chuàng)建需求類型,查找提供類型,在調(diào)用這個(gè)構(gòu)造方法的時(shí)候,幫我們創(chuàng)建好這個(gè)對(duì)象,并當(dāng)做參數(shù)來(lái)使用。這樣就避免了我們手動(dòng)調(diào)用注入方法來(lái)初始化對(duì)象。這個(gè)特性使得Dagger2可以非常方便地幫我們管理復(fù)雜的依賴模型。

然后我們?cè)贑arComponent里聲明使用這個(gè)依賴關(guān)系。

@Component
public interface CarComponent {

    void inject(Car car);

    Car getCar();
}

Main方法里就成了這樣的了:

public class Main {

    @Inject
    Car mCar;

    public static void main(String[] args){

        Car car = DaggerCarComponent.builder().build().getCar();

        System.out.println("cylinderNumbers : " + car.getEngine().getCylinderNumbers());

    }
}

編譯構(gòu)建一下,結(jié)果當(dāng)然是正確的?,F(xiàn)在我們查看一下依賴模型:

D(Car,Engine) = D(DaggerCarComponent,Engine)

后者是Dagger2托管的,層級(jí)為0,所以我們實(shí)現(xiàn)了Car和Engine的完全解耦。同時(shí)也可以看到,Car對(duì)它的成員Engine對(duì)象如何被創(chuàng)建和注入,是完全沒(méi)有感知的。

依賴注入的原理

接下來(lái)我們通過(guò)Dagger2幫我們生成的模板類,來(lái)分析一下它的工作原理。首先我們對(duì)上面一個(gè)例子做一個(gè)依賴關(guān)系的梳理:

  1. 我們使用@Inject注解Car類的mEngine成員,聲明了一個(gè)R(?, Engine)和一個(gè)I(Car,Engine)
  2. 我們使用@Inject注解Engine的構(gòu)造方法,聲明了一個(gè)P(Engine)
  3. 我們使用@Inject注解Main類的mCar成員,聲明了一個(gè)R(?, Car)
  4. 我們使用@Inject注解Car的構(gòu)造方法,聲明了一個(gè)P(Car)
  5. 我們?cè)贑arComponent接口中聲明了P(Car)

然后Dagger2根據(jù)P(Car),幫我們生了DaggerCarComponent,我們來(lái)看看源碼,路徑就不貼了。


public final class DaggerCarComponent implements CarComponent {
  private DaggerCarComponent(Builder builder) {}

//Builder對(duì)象的靜態(tài)創(chuàng)建方法
  public static Builder builder() {
    return new Builder();
  }

//CarComponent對(duì)象的靜態(tài)創(chuàng)建方法
  public static CarComponent create() {
    return new Builder().build();
  }

//Car類的注入方法,因?yàn)槲覀冊(cè)贑arComponent里還聲明了inject(Car car)
  @Override
  public void inject(Car car) {
    injectCar(car);
  }

//Car類的創(chuàng)建和注入方法
  @Override
  public Car getCar() {
    return injectCar(Car_Factory.newCar(new Engine()));
  }

//Car類的實(shí)際注入方法
  private Car injectCar(Car instance) {
    Car_MembersInjector.injectMEngine(instance, new Engine());
    return instance;
  }

//Builder類,用來(lái)幫我們構(gòu)建DaggerCarComponent
  public static final class Builder {
    private Builder() {}

    public CarComponent build() {
      return new DaggerCarComponent(this);
    }
  }
}

我們看到Dagger提供了Buidler類來(lái)幫我們創(chuàng)建DaggerCarComponent對(duì)象,可以創(chuàng)建一個(gè)Builder也可以直接使用create的方法,非常貼心。然后我們看下getCar方法

  @Override
  public Car getCar() {
    return injectCar(Car_Factory.newCar(new Engine()));
  }

調(diào)用了一個(gè)Car_Factory.newCar方法創(chuàng)建一個(gè)新的對(duì)象,然后調(diào)用注入方法。我們看newCar方法的實(shí)現(xiàn):

  public static Car newCar(Object engine) {
    return new Car((Engine) engine);
  }

可以看到這里就是調(diào)用的我們聲明的Car的構(gòu)造方法。這個(gè)Car_Factory就是根據(jù)第4步聲明的P(Car)生成的。因?yàn)槲覀僂ngine類里沒(méi)有其他的依賴,因此Dagger沒(méi)有幫我們生成Factory和Injector,而是直接調(diào)用@Injector標(biāo)記的構(gòu)造方法來(lái)生成Engine對(duì)象。然后我們看下injectCar方法:

  private Car injectCar(Car instance) {
    Car_MembersInjector.injectMEngine(instance, new Engine());
    return instance;
  }

inject方法調(diào)用了Car_MembersInjector. injectMEngine方法進(jìn)行注入,我們來(lái)看下實(shí)現(xiàn)。

  public static void injectMEngine(Car instance, Object mEngine) {
    instance.mEngine = (Engine) mEngine;
  }

直接賦值了。但是我們的mEngine不是public對(duì)象呀?它怎么能夠訪問(wèn)呢?這是因?yàn)镈agger2利用了默認(rèn)修飾符對(duì)象的包可見(jiàn)性這個(gè)特性,將Car_MembersInjector類也放在了和Car類的同一個(gè)包下,只是存儲(chǔ)路徑不同。這也是為什么Dagger2無(wú)法注入private或者protected修飾的成員。

我們還注意到Car_MembersInjector的類聲明

public final class Car_MembersInjector implements MembersInjector<Car> 

MembersInjector<Car> 又繼承于

public interface MembersInjector<T> 

也就是說(shuō)Dagger會(huì)為每一個(gè)I(Host,Member1,Member2,....)都生成一個(gè)實(shí)現(xiàn)了MembersInjector<Host>的,叫做Host_MembersInjector的注入類,這里的Car_MembersInjector. injectMEngine就是根據(jù)第1步聲明的I(Car,Engine)生成的。

在這個(gè)例子當(dāng)中,因?yàn)槲覀兊倪壿嫹浅5暮?jiǎn)單,生成的模板也是非常的簡(jiǎn)潔,創(chuàng)建對(duì)象,注入對(duì)象,完事。這里分析一下源碼,主要是說(shuō)明一下源碼的生成邏輯,就是根據(jù)我們的依賴模型中的角色定義來(lái)實(shí)現(xiàn)的,后面復(fù)雜一點(diǎn)的源碼中,會(huì)對(duì)這個(gè)邏輯模型體現(xiàn)得更加完整一點(diǎn)。

兩個(gè)問(wèn)題

還記得我們?cè)谖闹刑岬膬蓚€(gè)未解答的問(wèn)題嗎?現(xiàn)在我們可以來(lái)回答一下了。

  1. 為什么組件是一個(gè)接口,而不是一個(gè)類?

組件是一個(gè)接口,是因?yàn)樗暶鞯囊蕾囮P(guān)系是通過(guò)注解來(lái)定義的,而這個(gè)綁定關(guān)系在組件聲明的時(shí)候是未知的,需要Dagger2來(lái)幫我們查找和實(shí)現(xiàn),所以它不能夠有方法實(shí)體。這也說(shuō)明了組件不一定是一個(gè)接口,也可以是一個(gè)抽象類。

  1. getEngine方法既不見(jiàn)調(diào)用方,也不見(jiàn)提供方的,Dagger如何綁定需求方和提供方?

這個(gè)我們已經(jīng)解釋過(guò)了,在組件里聲明的依賴類型,Dagger都會(huì)自動(dòng)幫我們查找和補(bǔ)全。

小結(jié)

本節(jié)我們使用了Dagger2最簡(jiǎn)單的形式,一步一步地實(shí)現(xiàn)了Car和Engine的解耦,并建立了依賴的分析模型,通過(guò)模型來(lái)分析Dagger2的源碼,理解它的工作原理。這個(gè)模型在后續(xù)的內(nèi)容里也會(huì)使用,也是理解Dagger2工作機(jī)制的核心。接下來(lái)我們會(huì)使用Module來(lái)實(shí)現(xiàn)這個(gè)小節(jié)的例子,具體請(qǐng)看《Dagger2 依賴的接力游戲(三)》。

參考文檔

知乎: 神兵利器dagger2
github : Dagger2官方入口
Dagger 2 for Android Beginners
Dagger2入門解析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容