ThinkPHP容器源碼深度解析
本文主要針對框架內部容器的實現做為基準點來進行深度解析
ThinkPHP容器源碼深度解析
前言
一、單例模式
二、注冊樹模式
三、如何理解控制反轉和依賴注入
四、必會反射機制
五、玩轉自己的容器類
六、Container容器類剖析之Countable巧用
七、Container容器類剖析
八、容器源碼閱讀后總結
前言
在這之前已經剖析過了類的自動加載、配置文件加載的源碼解析,本文為第三期的文章,主要針對容器以及門面類的實現,解析源碼。以及學習實現此功能的一些知識點。
第一期文章:ThinkPHP自動加載Loader源碼分析
第二期文章:ThinkPHP配置文件源碼分析
一、單例模式
在學習容器以及門面之前需要必須了解的倆個設計模式,單例模式、注冊樹模式。
先對單例模式做一個簡單的說明。
擁有一個構造函數,并且屬性為private
擁有一個靜態成員變量來保存類的實例
擁有一個靜態方法來訪問這個實例
一下就是咔咔實現的一個簡單的單例模式,對照一下上面的三大特性看是否一致。
靜態變量為instance
擁有構造并且還是私有的
最后一個就是有一個getInstance這個靜態方法
接下來進行一下簡單的測試
還是在index控制器中做測試,為了證實其類只被實例化過一次,調用了其四次
訪問這個方法來看一下
new-class只執行了一次,就直接證明了創建的類只實例化了一次。
在這里咔咔之前有過一個疑問就是,這里的構造函數為什么要使用私有的屬性。
你之前有過這個疑問嗎?咔咔帶你一起來解答一下
在本類定義私有屬性的構造方法是為了防止其類在外部被實例化。
當在外部實例化這個類就會報下圖的錯。
那么為什么會在這里提一嘴單例模式呢!是因為在接下來的學習容器的源碼中會使用到
例如下圖thinkphp/library/think/Container.php類中就存在一個獲取當前容器的實例。
截止到這里單例模式就簡單的了解完了,了解單例模式也是為了更好的理解容器。
二、注冊樹模式
為什么在這里說這個注冊樹模式,因為在框架中注冊樹模式就是一個主導位置,所以必須去了解它!
那什么是注冊樹模呢!
注冊樹模式就是將對象實例注冊到一顆樹上(這里的樹可不是真的樹啊!就是注冊到一個全局的屬性里邊)
然后可以通過內部方法從全局的樹上獲取對應的對象實例。
這樣說的話肯定也不能更好的理解,接下來咔咔帶大家看一個簡單的案例來簡單的了解一下。
一個注冊樹模式需要的東西就是四個,注冊樹的池子,將對象掛載到注冊池里,從注冊池里獲取對象,從注冊池里卸載對象。
如下圖是咔咔寫的一個簡單的注冊樹模式。
代碼如果看不懂的就需要去補補基礎了哈!
接下來在到同一目錄創建一個TestTree文件
來到控制器測試寫的注冊樹模式是否有問題
在做測試的時候一定要注意命名空間問題哈!這里的kaka目錄是之前在類的自動加載哪里配置的,如有不會的可以去第一期文章查看。
這里就相當于先把TestTree這個類實例化出來
然后使用注冊樹模式把這個實例注冊到object樹池子中
最后使用get方式將這個類獲取出來就可以直接調用TestTree中的方法了。
最后看一下最終打印結果,結果就是TestTree類中getTreeContent方法的返回值。
注冊樹模式就是以上咔咔說明的這些內容,就是不去針對源碼學習,這些內容也是我們必須要去學會使用的。
三、如何理解控制反轉和依賴注入
其實這倆個就是指的一個東西,就是一種編程思想而已,不要想的那么難以理解和高大上。
那么什么是容器,容器直面理解就是裝東西的東西。在編程中,我們常見的變量、對象屬性都是一個容器。一個容器里邊能夠裝什么,完全取決于對該容器的定義。
然而現在我們討論的是另外一種容器,它存儲的既不是文本、數值,而是對象、類、接口通過這種容器,得以實現很多高級功能,最常用的就是代碼之間的解耦、依賴注入。
那么為什么會存在倆種概念,為什么要說控制反轉和依賴注入呢!在上文也提到過,它們其實指的就是一種東西,只是描述的角度不同而已。
就跟你是爸爸的兒子,你還是你爺爺的孫子,不管兒子還是孫子都指的是一個人。只是站在不同的角度看待問題而已。
控制反轉
是站在容器的角度看待問題,容器控制著應用程序,由容器反向的向應用程序注入應用程序需要的外部資源。
依賴注入
是站在應用程序的角度看待問題,應用程序依賴容器創建并注入它所需要的外部資源。
作用
主要用來減少代碼之間的耦合程度。
有效的分離對象和應用程序所需要的外部資源。
下面倆幅圖就可以很清晰的說明問題
給大家整一個簡單的案例
定義倆個類分別為Person、Car,在Person中實例并調用Car中的pay方法。
然后在控制器中調用,并且打印結果肯定就是Car返回的123,這個就不去打印了。
那這個時候我們把代碼修改一下,把Car類直接傳給Person類,在Person類中直接用傳過來的對象去調用對應的方法。
這只是一個簡單的實現過程,為了給閱讀框架容器代碼做一個鋪墊,在后文中會詳細說明框架中的容器注入。
四、必會反射機制
不知道大家有沒有了解過GO的反射機制,咔咔在當時看了go的反射機制后說實話有點暈乎乎的。
但是在后來看了PHP的反射之后,不僅對go的反射有了一定的深入了解,并且對于PHP的反射也是更好的理解。
反射這一概念是在PHP5.0被引出來的,在目前使用的框架中咔咔知道的就有thinkphp和laravel都使用了反射來實現依賴注入。
對于反射的理解:其實就是從根獲取根以外的東西,放在編程中講就是只要知道一個類就可以知道這個類所有的屬性和方法。
案例
這只是一個簡單的實現案例,獲取類的全部方法和屬性。可以看下圖中的打印結果跟TestReflection是否一致。
這個也從側面表現出現一個問題,就是會暴露出來一些本不應該暴露出來的信息。
關于反射提供的接口還有很多,這里就介紹幾個常用的,其余的在框架源碼中解析。
使用反射執行一個類的方法
打印出來的結果就是咔咔
使用反射執行一個類中帶參數的方法
使用反射執行一個類中不帶參數的方法
其它的方法你們自己可以嘗試嘗試,因為這個反射的接口在平時基礎開發是不怎么用的,這咔咔給大家介紹的都是后邊在閱讀源碼都是可以用的到的。
既然了解到了反射,那么反射可以做什么事情呢!其中有一個功能點自動生成文檔。
反射到這里就簡單的了解一下,至于還想了解更多的接口使用可以去官方查看對應的接口信息。
在了解完反射之后就要開始進入正題了,就需要正式進入我們的容器環節了。只有上邊的基礎打好接下來的容器才能更好的理解。
五、玩轉自己的容器類
經歷了九九八十一難終于來到了容器這一環節,在這一環節我們先來實現一個自己的容器,將之前講解的單例模式、注冊樹模式、反射進行一個串聯,從而進行加深印象和更好的理解。
還記得之前在依賴注入里邊說過這樣一個方法dependency,這個方法就是進行了依賴注入,從而對代碼進行解耦。
但是這次呢!會使用容器來解決這一問題。
首先先把需要的類定義好,這一個類就使用了單例模式和注冊樹模式,之前的文章沒有好好看的,一定要仔細看一下,否則后文會很難理解的。
instances[$key] = $value; } public function get ($key) { return $this->instances[$key]; } }
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
為了方便以后查看方便,這里把每節的案例演示都放在對應的控制器中
這里把之前的依賴注入的代碼移植過來,并且配置上注解路由進行訪問,看最終結果是否為Car方法返回的123
測試一下打印結果,一切ok
使用單例模式和注冊樹模式配合后修改的這份代碼
修改后打印出其結果,同樣也是car返回的值123。
在這里需要注意一下就是在同一個方法中set和get方法是不會共存的,這里只是為了給大家做一個演示寫到一起的。
后邊在看容器源碼時就知道set和get方法到底是怎么使用的,這里只是讓大家體驗一下單例模式和注冊樹模式。
這里做一個小修改,修改上文中最后倆行代碼
場景二
此時我們把Person 的文件修改一下
添加一個構造函數,把參數使用構造函數進行賦值,在buy方法中就不需要在進行傳遞參數,只需要使用this->obj即可。
此時如果還是直接運行dependency路由就會報下邊一個錯,那是因為在Person中構造函數有個參數,的但是我們沒有傳。
此時就需要在修改一處,就是在實例化Person時把Car的實例當參數給傳進去就沒有任何問題了。
但是你會發現上邊這都是什么代碼,本來簡簡單單的幾行代碼被復雜成這個樣子,這個時候就已經弊大于利了,不管設計模式在好,盲目的使用對項目來說也是一種負擔。
所以這個時候反射就來了,反射在上文中也進行簡單的介紹過,一定要看哈!文章都是一環套著一環的。
反射之戰優化代碼
最終優化完成的代碼就是這樣的,接下來對這段代碼進行簡單的解析。
在之前代碼的基礎上只修改了kaka/container/Container.php這個類里邊的get方法
判斷這個名person是否在容器中
使用反射接口,然后獲取傳進去person類的構造方法
如果person沒有構造方法就直接返回person這個實例即可
如存person在構造函數,則獲取person構造函數的方法
由于person類里邊的構造函數的參數不會僅限于一個
所以需要循環來獲取每個參數的對象
最后使用反射的 newInstanceArgs接口創建對應的實例
instances[$key] = $value; } /** * User : 咔咔 * Notes: 獲取容器里邊的實例 使用反射 * Time :2020/9/21 22:04 * @param $key * @return mixed */ public function get ($key) { if(!empty($this->instances[$key])){ $key = $this->instances[$key]; } $reflect = new \ReflectionClass($key); // 獲取類的構造函數 $c = $reflect->getConstructor(); if(!$c){ return new $key; } // 獲取構造函數的參數 $params = $c->getParameters(); foreach ($params as $param) { /** ReflectionClass Object ( [name] => container\dependency\Car ) */ $class = $param->getClass(); if(!$class){ }else{ // container\dependency\Car $args[] = $this->get($class->name); } } // 從給出的參數創建一個新的類實例 return $reflect->newInstanceArgs($args); } }
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
文件application/index/controller/Container.php這里就是修改之后的變動
問題一:kaka/container/dependency/Person.php里邊的參數Car是什么意思
這個問題其實很簡單,你可以看到這個Car就是同目錄的Car.php文件。你就可以直接理解為同命名空間下的文件。
問題二:文件application/index/controller/Container.php為什么可以直接調用buy方法
首先看一下obj的值,返回的這個對象里邊就已經把Car的類實例化好了,所以無需在實例化,可直接調用buy方法,因為參數會直接傳遞過去
以上就是咔咔實現的一個簡單的容器,如有不明白或者問題可以直接評論區回復即可。
接下來就是針對框架里邊的容器進行剖析,一步一步的追溯到根源。
六、Container容器類剖析之Countable巧用
關于Countable這塊內容一直沒想好是否是文章的形式寫出展現給大家,但是在后期閱讀源碼時大量的出現了Countable的應用。
為了大家能看懂每一個技術點,咔咔還是寫了出來。
在文件thinkphp/library/think/Container.php中,就可以直接看到使用了Countable接口,并且實現了它!
來到Countable這接口中,我們只能看到一個方法就是count().
根據代碼中Count elements of an object這行注釋可以了解到,這個接口是計算對象的元素
根據PHP文檔的說明在深入了解一下。
文檔說明當你執行count()方法時就相當于在執行上邊的abstract public Countable::count ( void ) : int抽象方法。
實戰案例
光說不干,事事落空;又說又干,馬到成功。直接開干
新建文件kaka/container/countableTest.php,并且添加以下內容
接著在文件application/index/controller/Container.php中學會使用Countable。
這里注意一下用法,是直接使用count();
Countable中的count()跟平時使用count()方法有什么區別
順便看一下PHP源碼中的解釋
可以看到第一個參數可以是數組也可是是countable
咔咔的理解是Countable只是重寫了SPL中的count方法,為了就是方便定制自己需要的統計規則而已。
int count ( mixed $array_or_countable [, int $mode = COUNT_NORMAL ] )
1
count你不知道的用法
既然說到了這里,咔咔給大家在普及一個count不是很常用的一個用法。
在平時開發的過程中,這樣的用法是最普遍的,也是大家最經常見到的一個使用案例。
但是如果這時給你一個多維數組,例如下圖這樣,讓你統計這個多維數組,你該怎么統計呢!
這個時候估計大多數小伙伴的想法就是循環然后定義一個計數器累計。
其實count()函數在這一塊就已經解決了這個需求。
下方打印結果就是"4----6"
直接使用count()函數一個數組得到的就是第一層數組的長度。
但是count()函數還有第二個參數,設置為1就是遞歸地計數數組中元素的數目(計算多維數組中的所有元素)
所以你這時在去看文檔就會發現,count()函數本身就有倆個參數
第一個參數是必須餓,選擇是數組
第二個參數默認是0就是不對多維數組中的所有元素進行計數
當第二個參數為1時就是遞歸的計算多維數組中的所有元素。
七、Container容器類剖析
上文中實現了一個自己創建的容器,接下來看看源碼中的容器,經過了上文容器中出現的技術點都已經囊括完了。
在接下里閱讀容器源碼就不會很吃力,如果之前的文章沒看,一定要大概過一遍哈!
大家無數次打開的一個文件public/index.php。
曾有多少次打開這個文件想對源碼進行一探究竟,但是看著看著就放棄了。
經過之前的注冊樹模式之后,你肯定就會明白這行代碼會返回什么Container::get('app')
這行代碼返回就是app的實例,可以進行簡單的斷點一下。
可以看到返回就是app類里邊的眾多屬性。
所以說注冊樹模式不會的在繼續返回去看之前寫的,要不越看越迷糊。
那么框架中的容器是怎么定義的呢!它到底是怎么實現的呢!
也就是只需要去關注這個get()方法做的事情就可以了。
代碼就會追蹤到文件thinkphp/library/think/Container.php中的get()方法
這里的getInstance()方法不陌生了吧!這就是上文說過的單例模式。
可以進行代碼追蹤getInstance()這個方法,你就會在同文件中看到這個單例模式的方法,返回Container實例。
Container實例調用make方法
代碼static::getInstance()返回了Container的實例后,就會去調用本類的make方法,接下來就是對make方法進行詳解了。
在開始閱讀make方法里邊的源碼之前,我們需要先對幾個屬性進行簡單的梳理一下。
這四個屬性一定要有點印象,并且一定要區別instance和instances。
這倆個屬性一個是單例模式返回當前類的實例,一個是容器中的所有的實例。
第一次執行結果
/** * 創建類的實例 * @access public * @param string $abstract 類名或者標識 * @param array|true $vars 變量 * @param bool $newInstance 是否每次創建新的實例 * @return object */ public function make($abstract, $vars = [], $newInstance = false) { // 判斷$vars這個變量是否為true if (true === $vars) { // 總是創建新的實例化對象 $newInstance = true; $vars = []; } // app 這里就是在容器別名里獲取傳遞過來的app 如果沒有則就是app $abstract = isset($this->name[$abstract]) ? $this->name[$abstract] : $abstract; // 從容器實例中獲取 如果存在則直接返回對應的實例 也就是使用注冊樹模式 if (isset($this->instances[$abstract]) && !$newInstance) { return $this->instances[$abstract]; } // think\App 從容器標識中獲取 if (isset($this->bind[$abstract])) { // 將think\App 復制給$concrete變量 $concrete = $this->bind[$abstract]; // 用于代表匿名函數的類 判斷是不是閉包 if ($concrete instanceof Closure) { $object = $this->invokeFunction($concrete, $vars); } else { // $this->name['app'] = think\App $this->name[$abstract] = $concrete; // 在執行一次本類的make方法,也就是本方法 return $this->make($concrete, $vars, $newInstance); } } else { $object = $this->invokeClass($abstract, $vars); } if (!$newInstance) { $this->instances[$abstract] = $object; } return $object; }
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
這是第二次執行流程
public function make($abstract, $vars = [], $newInstance = false) { // 判斷$vars這個變量是否為true if (true === $vars) { // 總是創建新的實例化對象 $newInstance = true; $vars = []; } // app 這里就是在容器別名里獲取傳遞過來的app 如果沒有則就是app // 第二次執行時 $abstract = think\App $abstract = isset($this->name[$abstract]) ? $this->name[$abstract] : $abstract; // 從容器實例中獲取 如果存在則直接返回對應的實例 也就是使用注冊樹模式 if (isset($this->instances[$abstract]) && !$newInstance) { return $this->instances[$abstract]; } // think\App 從容器標識中獲取 // 第二次執行$this->bind['think\App']不存在走else if (isset($this->bind[$abstract])) { // 將think\App 復制給$concrete變量 $concrete = $this->bind[$abstract]; // 用于代表匿名函數的類 判斷是不是閉包 if ($concrete instanceof Closure) { $object = $this->invokeFunction($concrete, $vars); } else { // $this->name['app'] = think\App $this->name[$abstract] = $concrete; // 在執行一次本類的make方法,也就是本方法 // think\App return $this->make($concrete, $vars, $newInstance); } } else { // think\App $object = $this->invokeClass($abstract, $vars); } if (!$newInstance) { // 把創建的容器存起來 //$this->instances['think\App'] = $object; $this->instances[$abstract] = $object; } return $object; }
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
public function invokeClass($class, $vars = []) { try { /** * ReflectionClass Object ( [name] => think\App ) */ // 這里就是之前文章提到的反射 $reflect = new ReflectionClass($class); if ($reflect->hasMethod('__make')) { $method = new ReflectionMethod($class, '__make'); if ($method->isPublic() && $method->isStatic()) { $args = $this->bindParams($method, $vars); return $method->invokeArgs(null, $args); } } // 通過反射獲取think\App的構造函數 $constructor = $reflect->getConstructor(); $args = $constructor ? $this->bindParams($constructor, $vars) : []; // 從給出的參數創建一個新的類實例 return $reflect->newInstanceArgs($args); } catch (ReflectionException $e) { throw new ClassNotFoundException('class not exists: ' . $class, $class); } }
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
執行流程圖
既然把代碼都理清楚了,這時來理一下執行的流程圖可以看的更清晰。
invokeClass方法詳細解析
不管是閱讀完上邊的代碼流程,還是上圖的流程圖,肯定都知道了最終代碼會走向一個方法invokeClass,就是這個方法。
這個方法中全部都是利用反射的知識點,不會的在去看上文或者之前的文章吧!
在invokeClass方法中,最重要的就是綁定參數的這個方法bindParams,這個方法里邊也全部運用的是反射。
所以在容器中反射起到的作用有多大就不用在去做過多的說明了。
在這之前需要把這塊說明一下,看到這個__make方法,咔咔是記憶尤深哈!
這個方法在之前學習config源碼配置那一篇文章中咔咔說暫時略過,因為當時所儲備的知識點和框架代碼執行流程還沒到說明__make這個方法的階段。
為了就是在容器這里詳細的說明__make這個方法的作用。
當你打印reflect這個變量的值時會返回倆個反射類的對象,如下圖。
代碼$reflect->hasMethod('__make')就是判斷此反射類里邊是否存在__make函數
代碼$method = new ReflectionMethod($class, '__make');就是執行反射類的一個方法 這里就指的是__make方法
當斷點這個method就會返回倆個存在__make反射類,這里是因為斷點了只有顯示了倆個反射類。
這里主要談論think\Config.
最后一行代碼$method->isPublic() && $method->isStatic()就是判斷方法是不是公公共的 判斷方法是不是靜態的
直到運行到$args = $this->bindParams($method, $vars);這行才會進入到bindParams方法,這個方法也會在下文給出詳細的解析。
解析bindParams方法
接下來就解析一下bindParams這個方法。
關于參數傳遞的就是一個反射類 第二個參數暫時不做說明,目前還沒有遇到響應的場景。
第一個參數值$reflect
使用反射方法$reflect->getNumberOfParameters()獲取反射類中對應的方法中的參數數目。按照上文的就是__make方法。容器代碼中只獲取過倆個方法的參數數目,一個是__make方法,一個是就是反射類中的構造函數。
由于目前還沒有傳遞vars變量的場景,所以這塊的內容暫時不去研究它直接略過。
代碼$params = $reflect->getParameters();也是使用反射獲取方法的參數。
打印出來可以看到的結果是倆組數據。
那么這這組數據是從哪里來的呢!往上翻一下,看一下$reflect這個參數是什么就明白了。
think\App這個反射類是沒有__make方法的,所以會獲取構造函數中的參數。
然后think\Log反射類中存在__make方法,于是就會返回__make的參數,如下圖。
就像類似于think\Log這樣的類,既有__make方法,也存在構造函數,就會走倆次bindParams方法,這個應該都明白,正是下圖邏輯。
在接下來就是循環反射類中獲取的參數。
獲取參數名、和獲取對應的反射類
最后將獲取出來的反射類傳遞給getObjectParam方法。
在這個getObjectParam方法中并沒有多少內容。
由于$vars從頭到尾都是空數組所以去除數組第一個的操作和判斷是否為閉包都不會執行。
最終會在返回去執行make方法
然后make方法會直接從容器中返回這個實例
當一個反射類存在__make方法時,最終就會執行return $method->invokeArgs(null, $args);,帶參數執行反射類方法
使用容器來調用配置類
既然已經把容器源碼讀了一次了,可不可以使用容器來實現呢!
那當然是可以的了,這里需要注意一下咔咔的命名空間,這里由于為了以后回顧方便把類名也起成了Container了,所以給加了一個別名,你們在使用的時候是不需要的哈!
截止到這里容器的源碼就講解的差不多了,后邊咔咔會做一個完整的流程圖,提供改大家查看。
八、容器源碼閱讀后總結
注冊模式
本文先從倆個設計模式開頭,分別為單例模式和注冊樹模式。
單例模式簡單理解就是在應用程序聲明周期內只會返回一個實例對象,不會再去創建新的對象。
注冊樹模式理解就是會把程序中使用的對象都會存放在一顆樹上,使用的時候直接從樹上獲取對象直接使用即可。
控制反轉依賴注入
控制反轉和依賴注入千萬不要讓名字把人虎住了,倆個看待一個事件的問題不同,一個是站在容器角度,一個是站在應用程序角度。
從容器角度來看,容器控制著應用程序,由容器反向的向應用程序注入外部資源
從應用程序的角度來看,應用程序依賴容器創建并注入它所需的外部資源。
反射
反射沒有什么需要總結的,打開文檔看一下就明白了,重要的要學會使用并且知道各自什么意思學會靈活運用即可。
容器源碼解析
容器的源碼看完后你會發現用的東西就是上邊說的三個知識點形成的,運用注冊模式來對容器中的對象管理。
對于這個圖需要牢牢記住,在源碼中就使用的這四個屬性走來走去的。
在一個就是代碼的執行流程
在容器中最重要的方法就是invokeClass和bindParams這倆個方法跟這咔咔的思路走就沒有什么問題,跟這斷點的流程一點一點執行。
這塊看的時候估計有點繞,但是仔細看完之后你會發現可以學到很多東西
堅持學習、堅持寫博、堅持分享是咔咔從業以來一直所秉持的信念。希望在偌大互聯網中咔咔的文章能帶給你一絲絲幫助。我是咔咔,下期見。
Java ThinkPHP 容器
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。