<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          自己動手實現(xiàn)一個簡單的 IOC,牛皮??!

          共 7058字,需瀏覽 15分鐘

           ·

          2020-11-10 22:28

          Java技術(shù)棧

          www.javastack.cn

          關(guān)注閱讀更多優(yōu)質(zhì)文章



          我們將分為幾步來編寫簡易 IOC,首先設(shè)計組件,再設(shè)計接口,然后關(guān)注實現(xiàn)。

          1. 設(shè)計組件。

          我們還記得Spring中最重要的有哪些組件嗎?BeanFactory 容器,BeanDefinition Bean的基本數(shù)據(jù)結(jié)構(gòu),當(dāng)然還需要加載Bean的資源加載器。大概最后最重要的就是這幾個組件。

          容器用來存放初始化好的Bean,BeanDefinition 就是Bean的基本數(shù)據(jù)結(jié)構(gòu),比如Bean的名稱,Bean的屬性 PropertyValue,Bean的方法,是否延遲加載,依賴關(guān)系等。資源加載器就簡單了,就是一個讀取XML配置文件的類,讀取每個標(biāo)簽并解析。

          2. 設(shè)計接口

          首先肯定需要一個BeanFactory,就是Bean容器,容器接口至少有2個最簡單的方法,一個是獲取Bean,一個注冊Bean.

          /**
          ?*?需要一個beanFactory?定義ioc?容器的一些行為?比如根據(jù)名稱獲取bean,?比如注冊bean,參數(shù)為bean的名稱,bean的定義
          ?*
          ?*?@author?stateis0
          ?*?@version?1.0.0
          ?*?@Date?2017/11/30
          ?*/public?interface?BeanFactory?{
          ?
          ??/**
          ???*?根據(jù)bean的名稱從容器中獲取bean對象
          ???*
          ???*?@param?name?bean?名稱
          ???*?@return?bean實例
          ???*?@throws?Exception?異常
          ???*/
          ??Object?getBean(String?name)?throws?Exception;
          ?
          ??/**
          ???*?將bean注冊到容器中
          ???*
          ???*?@param?name?bean?名稱
          ???*?@param?bean?bean實例
          ???*?@throws?Exception?異常
          ???*/
          ??void?registerBeanDefinition(String?name,?BeanDefinition?bean)?throws?Exception;}

          根據(jù)Bean的名字獲取Bean對象,注冊參數(shù)有2個,一個是Bean的名字,一個是 BeanDefinition 對象。

          定義完了Bean最基本的容器,還需要一個最簡單 BeanDefinition ?接口,我們?yōu)榱朔奖悖驗槲覀冞@個不必考慮擴展,因此可以直接設(shè)計為類,BeanDefinition ?需要哪些元素和方法呢?

          需要一個 Bean 對象,一個Class對象,一個ClassName字符串,還需要一個元素集合 PropertyValues。這些就能組成一個最基本的 BeanDefinition 類了。那么需要哪些方法呢?其實就是這些屬性的get set 方法。

          我們看看該類的詳細:

          package?cn.thinkinjava.myspring;

          /**
          *?bean?的定義
          *
          *?@author?stateis0
          */public?class?BeanDefinition?{
          ?
          ??/**
          ???*?bean
          ???*/
          ??private?Object?bean;
          ?
          ??/**
          ???*?bean?的?CLass?對象
          ???*/
          ??private?Class?beanClass;
          ?
          ??/**
          ???*?bean?的類全限定名稱
          ???*/
          ??private?String?ClassName;
          ?
          ??/**
          ???*?類的屬性集合
          ???*/
          ??private?PropertyValues?propertyValues?=?new?PropertyValues();
          ?
          ??/**
          ???*?獲取bean對象
          ???*/
          ??public?Object?getBean()?{
          ????return?this.bean;
          ??}
          ?
          ??/**
          ???*?設(shè)置bean的對象
          ???*/
          ??public?void?setBean(Object?bean)?{
          ????this.bean?=?bean;
          ??}
          ?
          ??/**
          ???*?獲取bean的Class對象
          ???*/
          ??public?Class?getBeanclass()?{
          ????return?this.beanClass;
          ??}
          ?
          ??/**
          ???*?通過設(shè)置類名稱反射生成Class對象
          ???*/
          ??public?void?setClassname(String?name)?{
          ????this.ClassName?=?name;
          ????try?{
          ??????this.beanClass?=?Class.forName(name);
          ????}?catch?(ClassNotFoundException?e)?{
          ??????e.printStackTrace();
          ????}
          ??}
          ?
          ??/**
          ???*?獲取bean的屬性集合
          ???*/
          ??public?PropertyValues?getPropertyValues()?{
          ????return?this.propertyValues;
          ??}
          ?
          ??/**
          ???*?設(shè)置bean的屬性
          ???*/
          ??public?void?setPropertyValues(PropertyValues?pv)?{
          ????this.propertyValues?=?pv;
          ??}

          }

          有了基本的 BeanDefinition 數(shù)據(jù)結(jié)構(gòu),還需要一個從XML中讀取并解析為 BeanDefinition 的操作類,首先我們定義一個 BeanDefinitionReader 接口,該接口只是一個標(biāo)識,具體由抽象類去實現(xiàn)一個基本方法和定義一些基本屬性,比如一個讀取時需要存放的注冊容器,還需要一個委托一個資源加載器 ResourceLoader, 用于加載XML文件,并且我們需要設(shè)置該構(gòu)造器必須含有資源加載器,當(dāng)然還有一些get set 方法。

          package?cn.thinkinjava.myspring;
          import?cn.thinkinjava.myspring.io.ResourceLoader;
          import?java.util.HashMap;
          import?java.util.Map;

          /**
          ?*?抽象的bean定義讀取類
          ?*
          ?*?@author?stateis0
          ?*
          /
          public?abstract?class?AbstractBeanDefinitionReader?implements?BeanDefinitionReader?{
          ?
          ??/**
          ???*?注冊bean容器
          ???*/
          ??private?Map?registry;
          ?
          ??/**
          ???*?資源加載器
          ???*/
          ??private?ResourceLoader?resourceLoader;
          ?
          ??/**
          ???*?構(gòu)造器器必須有一個資源加載器,?默認(rèn)插件創(chuàng)建一個map容器
          ???*
          ???*?@param?resourceLoader?資源加載器
          ???*/
          ??protected?AbstractBeanDefinitionReader(ResourceLoader?resourceLoader)?{
          ????this.registry?=?new?HashMap<>();
          ????this.resourceLoader?=?resourceLoader;
          ??}
          ?
          ??/**
          ???*?獲取容器
          ???*/
          ??public?Map?getRegistry()?{
          ????return?registry;
          ??}
          ?
          ??/**
          ???*?獲取資源加載器
          ???*/
          ??public?ResourceLoader?getResourceLoader()?{
          ????return?resourceLoader;
          ??}
          ??
          }

          有了這幾個抽象類和接口,我們基本能形成一個雛形,BeanDefinitionReader 用于從XML中讀取配置文件,生成 BeanDefinition 實例,存放在 BeanFactory 容器中,初始化之后,就可以調(diào)用 getBean 方法獲取初始化成功的Bean。形成一個完美的閉環(huán)。

          3. 如何實現(xiàn)

          剛剛我們說了具體的流程:從XML中讀取配置文件, 解析成 BeanDefinition,最終放進容器。說白了就3步。那么我們就先來設(shè)計第一步。推薦看下《Java 必看的 Spring 知識匯總,關(guān)注公眾號Java技術(shù)棧獲取更多Spring系列教程。

          1. ?從XML中讀取配置文件, 解析成 BeanDefinition

          我們剛剛設(shè)計了一個讀取BeanDefinition 的接口 BeanDefinitionReader 和一個實現(xiàn)它的抽象類 AbstractBeanDefinitionReader,抽象了定義了一些簡單的方法,其中由一個委托類-----ResourceLoader, 我們還沒有創(chuàng)建, 該類是資源加載器,根據(jù)給定的路徑來加載資源。

          我們可以使用Java 默認(rèn)的類庫 java.net.URL 來實現(xiàn),定義兩個類,一個是包裝了URL的類 ResourceUrl, 一個是依賴 ResourceUrl 的資源加載類。

          ResourceUrl 代碼實現(xiàn)

          /**
          ?*?資源URL
          ?*
          /
          public?class?ResourceUrl?implements?Resource?{
          ?
          ??/**
          ???*?類庫URL
          ???*/
          ??private?final?URL?url;
          ?
          ??/**
          ???*?需要一個類庫URL
          ???*/
          ??public?ResourceUrl(URL?url)?{
          ????this.url?=?url;
          ??}
          ?
          ??/**
          ???*?從URL中獲取輸入流
          ???*/
          ??@Override
          ??public?InputStream?getInputstream()?throws?Exception?{
          ????URLConnection?urlConnection?=?url.openConnection();
          ????urlConnection.connect();
          ????return?urlConnection.getInputStream();
          ?
          ??}
          }

          ResourceLoader 實現(xiàn)

          /**
          ?*?資源URL
          */
          public?class?ResourceUrl?implements?Resource?{
          ?
          ??/**
          ???*?類庫URL
          ???*/
          ??private?final?URL?url;
          ?
          ??/**
          ???*?需要一個類庫URL
          ???*/
          ??public?ResourceUrl(URL?url)?{
          ????this.url?=?url;
          ??}
          ?
          ??/**
          ???*?從URL中獲取輸入流
          ???*/
          ??@Override
          ??public?InputStream?getInputstream()?throws?Exception?{
          ????URLConnection?urlConnection?=?url.openConnection();
          ????urlConnection.connect();
          ????return?urlConnection.getInputStream();
          ??}
          ??
          }

          當(dāng)然還需要一個接口,只定義了一個抽象方法

          package?cn.thinkinjava.myspring.io;
          import?java.io.InputStream;

          /**
          ?*?資源定義
          ?*
          ?*?@author?stateis0
          ?*/
          public?interface?Resource?{
          ?
          ??/**
          ???*?獲取輸入流
          ???*/
          ??InputStream?getInputstream()?throws?Exception;

          }

          好了, AbstractBeanDefinitionReader 需要的元素已經(jīng)有了,但是,很明顯該方法不能實現(xiàn)讀取 BeanDefinition 的任務(wù)。那么我們需要一個類去繼承抽象類,去實現(xiàn)具體的方法, 既然我們是XML 配置文件讀取,那么我們就定義一個 XmlBeanDefinitionReader 繼承 AbstractBeanDefinitionReader ,實現(xiàn)一些我們需要的方法, 比如讀取XML 的readrXML, 比如將解析出來的元素注冊到 registry 的 Map 中, 一些解析的細節(jié)。我們還是看代碼吧。

          XmlBeanDefinitionReader ?實現(xiàn)讀取配置文件并解析成Bean

          package?cn.thinkinjava.myspring.xml;

          import?cn.thinkinjava.myspring.AbstractBeanDefinitionReader;
          import?cn.thinkinjava.myspring.BeanDefinition;
          import?cn.thinkinjava.myspring.BeanReference;
          import?cn.thinkinjava.myspring.PropertyValue;
          import?cn.thinkinjava.myspring.io.ResourceLoader;
          import?java.io.InputStream;
          import?javax.xml.parsers.DocumentBuilder;
          import?javax.xml.parsers.DocumentBuilderFactory;
          import?org.w3c.dom.Document;
          import?org.w3c.dom.Element;
          import?org.w3c.dom.Node;
          import?org.w3c.dom.NodeList;

          /**
          ?*?解析XML文件
          ?*
          ?*?@author?stateis0
          ?*/
          public?class?XmlBeanDefinitionReader?extends?AbstractBeanDefinitionReader?{

          ??/**
          ???*?構(gòu)造器,必須包含一個資源加載器
          ???*
          ???*?@param?resourceLoader?資源加載器
          ???*/
          ??public?XmlBeanDefinitionReader(ResourceLoader?resourceLoader)?{
          ????super(resourceLoader);
          ??}

          ??public?void?readerXML(String?location)?throws?Exception?{
          ????//?創(chuàng)建一個資源加載器
          ????ResourceLoader?resourceloader?=?new?ResourceLoader();
          ????//?從資源加載器中獲取輸入流
          ????InputStream?inputstream?=?resourceloader.getResource(location).getInputstream();
          ????//?獲取文檔建造者工廠實例
          ????DocumentBuilderFactory?factory?=?DocumentBuilderFactory.newInstance();
          ????//?工廠創(chuàng)建文檔建造者
          ????DocumentBuilder?docBuilder?=?factory.newDocumentBuilder();
          ????//?文檔建造者解析流?返回文檔對象
          ????Document?doc?=?docBuilder.parse(inputstream);
          ????//?根據(jù)給定的文檔對象進行解析,并注冊到bean容器中
          ????registerBeanDefinitions(doc);
          ????//?關(guān)閉流
          ????inputstream.close();
          ??}

          ??/**
          ???*?根據(jù)給定的文檔對象進行解析,并注冊到bean容器中
          ???*
          ???*?@param?doc?文檔對象
          ???*/
          ??private?void?registerBeanDefinitions(Document?doc)?{
          ????//?讀取文檔的根元素
          ????Element?root?=?doc.getDocumentElement();
          ????//?解析元素的根節(jié)點及根節(jié)點下的所有子節(jié)點并添加進注冊容器
          ????parseBeanDefinitions(root);
          ??}

          ??/**
          ???*?解析元素的根節(jié)點及根節(jié)點下的所有子節(jié)點并添加進注冊容器
          ???*
          ???*?@param?root?XML?文件根節(jié)點
          ???*/
          ??private?void?parseBeanDefinitions(Element?root)?{
          ????//?讀取根元素的所有子元素
          ????NodeList?nl?=?root.getChildNodes();
          ????//?遍歷子元素
          ????for?(int?i?=?0;?i???????//?獲取根元素的給定位置的節(jié)點
          ??????Node?node?=?nl.item(i);
          ??????//?類型判斷
          ??????if?(node?instanceof?Element)?{
          ????????//?強轉(zhuǎn)為父類型元素
          ????????Element?ele?=?(Element)?node;
          ????????//?解析給給定的節(jié)點,包括name,class,property,?name,?value,ref
          ????????processBeanDefinition(ele);
          ??????}
          ????}
          ??}

          ??/**
          ???*?解析給給定的節(jié)點,包括name,class,property,?name,?value,ref
          ???*
          ???*?@param?ele?XML?解析元素
          ???*/
          ??private?void?processBeanDefinition(Element?ele)?{
          ????//?獲取給定元素的?name?屬性
          ????String?name?=?ele.getAttribute("name");
          ????//?獲取給定元素的?class?屬性
          ????String?className?=?ele.getAttribute("class");
          ????//?創(chuàng)建一個bean定義對象
          ????BeanDefinition?beanDefinition?=?new?BeanDefinition();
          ????//?設(shè)置bean?定義對象的?全限定類名
          ????beanDefinition.setClassname(className);
          ????//?向?bean?注入配置文件中的成員變量
          ????addPropertyValues(ele,?beanDefinition);
          ????//?向注冊容器?添加bean名稱和bean定義
          ????getRegistry().put(name,?beanDefinition);
          ??}

          ??/**
          ???*?添加配置文件中的屬性元素到bean定義實例中
          ???*
          ???*?@param?ele?元素
          ???*?@param?beandefinition?bean定義?對象
          ???*/
          ??private?void?addPropertyValues(Element?ele,?BeanDefinition?beandefinition)?{
          ????//?獲取給定元素的?property?屬性集合
          ????NodeList?propertyNode?=?ele.getElementsByTagName("property");
          ????//?循環(huán)集合
          ????for?(int?i?=?0;?i???????//?獲取集合中某個給定位置的節(jié)點
          ??????Node?node?=?propertyNode.item(i);
          ??????//?類型判斷
          ??????if?(node?instanceof?Element)?{
          ????????//?將節(jié)點向下強轉(zhuǎn)為子元素
          ????????Element?propertyEle?=?(Element)?node;
          ????????//?元素對象獲取?name?屬性
          ????????String?name?=?propertyEle.getAttribute("name");
          ????????//?元素對象獲取?value?屬性值
          ????????String?value?=?propertyEle.getAttribute("value");
          ????????//?判斷value不為空
          ????????if?(value?!=?null?&&?value.length()?>?0)?{
          ??????????//?向給定的?“bean定義”?實例中添加該成員變量
          ??????????beandefinition.getPropertyValues().addPropertyValue(new?PropertyValue(name,?value));
          ????????}?else?{
          ??????????//?如果為空,則獲取屬性ref
          ??????????String?ref?=?propertyEle.getAttribute("ref");
          ??????????if?(ref?==?null?||?ref.length()?==?0)?{
          ????????????//?如果屬性ref為空,則拋出異常
          ????????????throw?new?IllegalArgumentException(
          ????????????????"Configuration?problem:??element?for?property?'"
          ????????????????????+?name?+?"'?must?specify?a?ref?or?value");
          ??????????}
          ??????????//?如果不為空,測創(chuàng)建一個?“bean的引用”?實例,構(gòu)造參數(shù)為名稱,實例暫時為空
          ??????????BeanReference?beanRef?=?new?BeanReference(name);
          ??????????//?向給定的?“bean定義”?中添加成員變量
          ??????????beandefinition.getPropertyValues().addPropertyValue(new?PropertyValue(name,?beanRef));
          ????????}
          ??????}
          ????}
          ??}

          }

          可以說代碼注釋寫的非常詳細,該類方法如下:

          1. public void readerXML(String location) 公開的解析XML的方法,給定一個位置的字符串參數(shù)即可。

          2. private void registerBeanDefinitions(Document doc) 給定一個文檔對象,并進行解析。

          3. private void parseBeanDefinitions(Element root) 給定一個根元素,循環(huán)解析根元素下所有子元素。

          4. private void processBeanDefinition(Element ele) 給定一個子元素,并對元素進行解析,然后拿著解析出來的數(shù)據(jù)創(chuàng)建一個 BeanDefinition 對象。并注冊到BeanDefinitionReader 的 Map 容器(該容器存放著解析時的所有Bean)中。

          5. private void addPropertyValues(Element ele, BeanDefinition beandefinition) 給定一個元素,一個 BeanDefinition 對象,解析元素中的 ?property 元素, 并注入到 BeanDefinition ?實例中。

          一共5步,完成了解析XML文件的所有操作。最終的目的是將解析出來的文件放入到 BeanDefinitionReader 的 Map 容器中。

          好了,到這里,我們已經(jīng)完成了從XML文件讀取并解析的步驟,那么什么時候放進BeanFactory的容器呢?剛剛我們只是放進了 AbstractBeanDefinitionReader 的注冊容器中。

          因此我們要根據(jù)BeanFactory 的設(shè)計來實現(xiàn)如何構(gòu)建成一個真正能用的Bean呢?因為剛才的哪些Bean只是一些Bean的信息。沒有我們真正業(yè)務(wù)需要的Bean。

          2. 初始化我們需要的Bean(不是Bean定義)并且實現(xiàn)依賴注入

          我們知道Bean定義是不能干活的,只是一些Bean的信息,就好比一個人,BeanDefinition 就相當(dāng)你在公安局的檔案,但是你人不在公安局,可只要公安局拿著你的檔案就能找到你。就是這樣一個關(guān)系。

          那我們就根據(jù)BeanFactory的設(shè)計來設(shè)計一個抽象類 AbstractBeanFactory。

          package?cn.thinkinjava.myspring.factory;

          import?cn.thinkinjava.myspring.BeanDefinition;
          import?java.util.HashMap;

          /**
          ?*?一個抽象類,?實現(xiàn)了?bean?的方法,包含一個map,用于存儲bean?的名字和bean的定義
          ?*
          ?*?@author?stateis0
          ?*/
          public?abstract?class?AbstractBeanFactory?implements?BeanFactory?{

          ??/**
          ???*?容器
          ???*/
          ??private?HashMap?map?=?new?HashMap<>();

          ??/**
          ???*?根據(jù)bean的名稱獲取bean,?如果沒有,則拋出異常?如果有,?則從bean定義對象獲取bean實例
          ???*/
          ??@Override
          ??public?Object?getBean(String?name)?throws?Exception?{
          ????BeanDefinition?beandefinition?=?map.get(name);
          ????if?(beandefinition?==?null)?{
          ??????throw?new?IllegalArgumentException("No?bean?named?"?+?name?+?"?is?defined");
          ????}
          ????Object?bean?=?beandefinition.getBean();
          ????if?(bean?==?null)?{
          ??????bean?=?doCreate(beandefinition);
          ????}
          ????return?bean;
          ??}

          ??/**
          ???*?注冊?bean定義?的抽象方法實現(xiàn),這是一個模板方法,?調(diào)用子類方法doCreate,
          ???*/
          ??@Override
          ??public?void?registerBeanDefinition(String?name,?BeanDefinition?beandefinition)?throws?Exception?{
          ????Object?bean?=?doCreate(beandefinition);
          ????beandefinition.setBean(bean);
          ????map.put(name,?beandefinition);
          ??}

          ??/**
          ???*?減少一個bean
          ???*/
          ??abstract?Object?doCreate(BeanDefinition?beandefinition)?throws?Exception;
          }package?cn.thinkinjava.myspring.factory;

          import?cn.thinkinjava.myspring.BeanDefinition;
          import?cn.thinkinjava.myspring.PropertyValue;
          import?cn.thinkinjava.myspring.BeanReference;
          import?java.lang.reflect.Field;


          /**
          ?*?實現(xiàn)自動注入和遞歸注入(spring?的標(biāo)準(zhǔn)實現(xiàn)類?DefaultListableBeanFactory?有?1810?行)
          ?*
          ?*?@author?stateis0
          ?*/
          public?class?AutowireBeanFactory?extends?AbstractBeanFactory?{


          ??/**
          ???*?根據(jù)bean?定義創(chuàng)建實例,?并將實例作為key,?bean定義作為value存放,并調(diào)用?addPropertyValue?方法?為給定的bean的屬性進行注入
          ???*/
          ??@Override
          ??protected?Object?doCreate(BeanDefinition?beandefinition)?throws?Exception?{
          ????Object?bean?=?beandefinition.getBeanclass().newInstance();
          ????addPropertyValue(bean,?beandefinition);
          ????return?bean;
          ??}

          ??/**
          ???*?給定一個bean定義和一個bean實例,為給定的bean中的屬性注入實例。
          ???*/
          ??protected?void?addPropertyValue(Object?bean,?BeanDefinition?beandefinition)?throws?Exception?{
          ????//?循環(huán)給定?bean?的屬性集合
          ????for?(PropertyValue?pv?:?beandefinition.getPropertyValues().getPropertyValues())?{
          ??????//?根據(jù)給定屬性名稱獲取?給定的bean中的屬性對象
          ??????Field?declaredField?=?bean.getClass().getDeclaredField(pv.getname());
          ??????//?設(shè)置屬性的訪問權(quán)限
          ??????declaredField.setAccessible(true);
          ??????//?獲取定義的屬性中的對象
          ??????Object?value?=?pv.getvalue();
          ??????//?判斷這個對象是否是?BeanReference?對象
          ??????if?(value?instanceof?BeanReference)?{
          ????????//?將屬性對象轉(zhuǎn)為?BeanReference?對象
          ????????BeanReference?beanReference?=?(BeanReference)?value;
          ????????//?調(diào)用父類的?AbstractBeanFactory?的?getBean?方法,根據(jù)bean引用的名稱獲取實例,此處即是遞歸
          ????????value?=?getBean(beanReference.getName());
          ??????}
          ??????//?反射注入bean的屬性
          ??????declaredField.set(bean,?value);
          ????}

          ??}

          }

          可以看到 doCreate 方法使用了反射創(chuàng)建了一個對象,并且還需要對該對象進行屬性注入,如果屬性是 ref 類型,那么既是依賴關(guān)系,則需要調(diào)用 getBean 方法遞歸的去尋找那個Bean(因為最后一個Bean 的屬性肯定是基本類型)。這樣就完成了一次獲取實例化Bean操作,并且也實現(xiàn)類依賴注入。

          4. 總結(jié)

          我們通過這些代碼實現(xiàn)了一個簡單的 IOC 依賴注入的功能,也更加了解了 IOC, 以后遇到Spring初始化的問題再也不會手足無措了,直接看源碼就能解決。關(guān)注公眾號Java技術(shù)棧將繼續(xù)分享更多手寫開源框架系列。

          good luck ?。?!

          作者:莫那一魯?shù)?br>鏈接:https://www.jianshu.com/p/6e25dc62e3a1






          關(guān)注Java技術(shù)棧看更多干貨



          戳原文,獲取精選面試題!
          瀏覽 39
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  6—12呦国产精品 | 国产一级a毛一级a在线 | 日本AA黄色片网站 | 久久大香蕉视频 | 欧美日韩性爱视频 |