wex框架初始化完全解析_第1頁
wex框架初始化完全解析_第2頁
wex框架初始化完全解析_第3頁
wex框架初始化完全解析_第4頁
wex框架初始化完全解析_第5頁
已閱讀5頁,還剩7頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

1、 Webx框架初始化解析前言:對整個webx框架的初始化進(jìn)行了全面的分析,webx是怎么一步一步的初始化的,組件與組件時間如何相互通信,web層怎么能夠方便的拿到service的東西,service的資源存在什么地方;外部程序怎樣拿到webxcontroller的實例;又怎樣保證car與car之間的獨(dú)立,car與car之間又是怎么共享資源的,configration在整個環(huán)境是怎么起到核心作用的;希望這樣的文章會對大家有所幫助;主要是分析wex框架的內(nèi)部初始化情況,和結(jié)構(gòu)分析;(1):com.alibaba.webx.WebxControllerServlet為整個系統(tǒng)的門面,初始化工作也在這

2、里進(jìn)行; web.xml里面:michael已經(jīng)說的很清楚webx主控制器;WebxControllercom.alibaba.webx.WebxControllerServletWebxControllerServlet繼承com.alibaba.webx.controller.AbstractWebxControllerServlet;AbstractWebxControllerServlet繼承自HttpServlet;容器啟動的時候,因為WebxController的啟動狀態(tài)是1,所以直接執(zhí)行WebxControllerServlet按照servlet的規(guī)范首先執(zhí)行init();Web

3、xControllerServlet直接執(zhí)行父類AbstractWebxControllerServlet的init();public void init() throws ServletException try configure();(WebxController: init() Ready to Rumble!); catch (Exception e) log.error(WebxController: init() failed, e);/ 這個異常將被servlet engine記錄到servlet log中。UnavailableException ue = n

4、ew UnavailableException(WebxController: init() failed: + e.getMessage();ue.initCause(e);throw ue; 以上又是一個比較經(jīng)典的模板應(yīng)用,init()方法里面執(zhí)行 configure()方法,但是AbstractWebxControllerServlet定義的configure十一個抽象方法:protected abstract void configure() throws WebxInitializationException;并且由子類實現(xiàn),所以直接執(zhí)行子類的configure();具體業(yè)務(wù)邏輯子類

5、化;WebxControllerServlet的configure()方法中主要進(jìn)行兩個操作,一初始化WebxController,二將相關(guān)的servletConfig對象放到ServletContext中;WebxControllerServlet中屬性 private WebxController controller;光看名字就知道十一個比較重要的對象;所以時刻要注意它;分析configure():protected void configure() throws WebxInitializationException controller = WebxUtil.getWebxContr

6、oller(getServletContext();if (controller = null) WebxLoader loader = new WebxLoader(log, getServletContext(), getServletConfig();loader.configure();controller = loader;/ 將servletConfig放在servletContext中,供rundata filter使用。getServletContext().setAttribute(WEBX_CONTROLLER_SERVLET_CONFIG_KEY, getServletC

7、onfig();這里首先用WebxUtil工具類根據(jù)ServletContex對象來初始化controller;調(diào)用WebxUtil的public static WebxController getWebxController(ServletContext context);請看具體方法: public static WebxController getWebxController(ServletContext context) WebxController controller = null;try Reference holder = (Reference) context.getAttr

8、ibute(WEBX_CONTROLLER_KEY);/context里面并沒有這個屬性,這里拿出來應(yīng)該是空的;controller = (WebxController) holder.get(); catch (ClassCastException e) catch (NullPointerException e) return controller;這里返回的controller應(yīng)該是空的,因為context里面沒有注入“webx.controller”屬性;接下來應(yīng)該執(zhí)行: WebxLoader loader = new WebxLoader(log, getServletContext

9、(), getServletConfig();WebxLoader 實現(xiàn)了 com.alibaba.webx.controller.WebxController和com.alibaba.webx.WebxConstantcom.alibaba.webx.controller.WebxController是webx框架的核心調(diào)度者,是mvc框架的入口,它里面包含了獲取大量資源的方法,需要用的上下文對象都在接口里面提供了的;例如可以通過接口拿到:ServletContext,ServiceManager(service管理器),Scheme(scheme方案初始化配置的方案),RunDataSer

10、vice(RunData服務(wù)),ThreadContextService(線程范圍的singleton服務(wù)),WebxComponent等信息; com.alibaba.webx.WebxConstant包含一些配置信息,關(guān)于webxfreamwork的常量定義;現(xiàn)在我們應(yīng)該看一下實現(xiàn)類是怎么的結(jié)構(gòu),以及它是怎樣工作的;com.alibaba.webx.WebxLoader首先通過構(gòu)造函數(shù)進(jìn)行初始化,傳入日志對象,servlet上下文對象,servletConfig對象;WebxLoader loader = new WebxLoader(log, getServletContext(), g

11、etServletConfig();loader 初始化成功之后,執(zhí)行 loader.configure();這一步是初始化了webx的所有配置;接下來可能要花大量的篇幅講解很多初始化工作雖然有點枯燥;(2)真正的controller是怎樣產(chǎn)生的:在這里要注意看注釋的編號對應(yīng)下面的具體解釋;WebxLoader的configure() public void configure() throws WebxInitializationException /*(一)創(chuàng)建日志輸出路徑,看后面的注解*/File loggingRoot = configureLoggingRoot(); /*(二)初始

12、化 WebappBootstrapResourceLoaderService *WebappBootstrapResourceLoaderService resourceLoader = new WebappBootstrapResourceLoaderService(servletContext);/ 在配置文件中可以使用如下變量:/ $loggingRoot/ $server.host/ $server.addr/ 以及所有System.getProperties()中的變量Properties props = new Properties(System.getProperties();

13、/*(三)為配置log4j,取出相關(guān)屬性*/props.setProperty(LOGGING_ROOT_KEY, loggingRoot.getAbsolutePath();props.setProperty(LOCAL_HOST_KEY, SystemUtil.getHostInfo().getName();props.setProperty(LOCAL_ADDRESS_KEY, SystemUtil.getHostInfo().getAddress();/*(四) 配置log4j日志系統(tǒng)。*/configureLog4j(resourceLoader, props);/ 現(xiàn)在,loggi

14、ng系統(tǒng)應(yīng)該已經(jīng)可以工作了。(=);(Logging root is + loggingRoot);/*(五)取得scheme的實例*/scheme = configureScheme(resourceLoader);/*(六)取得規(guī)格化的servletPaths*/servletPaths = configureServletPaths();/*(七)取得webx配置文件*/Configuration configuration = configureWebx(resourceLoader, props, scheme);/*(八)設(shè)置默認(rèn)的services和

15、配置項*/scheme.applyDefaultConfiguration(resourceLoader, configuration, props);if (log.isDebugEnabled() log.debug(WebxController initialized with configuration: n+ (ConfigurationUtil.list(configuration);/*(九)初始化service manager*/serviceManager = configureServiceManager(resourceLoader, configuration, sch

16、eme);/*(十)其它有關(guān)WebxController配置*/configureWebxController();/*(十一)如果指定了initAllServices,則強(qiáng)制初始化所有services*/configureAllServices(); 注解:(一)創(chuàng)建日志輸出路徑的目錄,這里有三種策率,第一種是默認(rèn)策率,首先會取你的user.home,我的user.home是:C:Documents and Settingsliangkuan,然后用user.home+/.webx+ServletName+/logs,最終中生成的路徑是C:Documents and Settingslian

17、gkuan.webxWebxControllerlogs+log4j.xml中配置的路徑變量。以我本機(jī)為例;但是構(gòu)建目錄的時候首先會去尋找servletConfig里面關(guān)于日志的配置參數(shù),來決定路徑,如果在servletConfig中沒有找到的話,會去servletContext里面找配置參數(shù),如果兩者都沒有找到就會直接用第一種默認(rèn)策率來作為日志輸出路徑,相當(dāng)靈活;以我的環(huán)境為例,因為我的servlet里面配置了 loggingRoot$fortuna_loggingRoot所以直接就會在servletConfig里面找;實現(xiàn)用到的相關(guān)類和:WebxLoader.configureLoggin

18、gRoot(),.findInitParameter(),mon.lang.SystemUtil的getHomeDir();(二)初始化WebappBootstrapResourceLoaderService 簡單講下WebappBootstrapResourceLoaderService 的結(jié)構(gòu)WebappBootstrapResourceLoaderService 繼承com.alibaba.service.resource.BootstrapResourceLoaderServiceBootstrapResourceLoaderService實現(xiàn)com.al

19、ibaba.service.resourceResourceLoaderService,com.alibaba.service.MultiInstance;(實現(xiàn)它可以有多個實例)這個初始化操作只做了3個事情: 1:執(zhí)行父類的非缺省構(gòu)造函數(shù),進(jìn)本上什么都沒有做;2:setLoggerReady(false),設(shè)置日志并沒有準(zhǔn)備好;3:傳入servletContext屬性;this.servletContext = servletContext;這是一個功能強(qiáng)大的資源裝載器;可以讀取各種資源,例如url,file等;這里的資源裝載器主要是針對web應(yīng)用的資源裝載器實例;(三)將和log4j相關(guān)的

20、屬性裝入資源文件;LOGGING_ROOT_KEY對應(yīng)的是日志輸出的地址,LOCAL_HOST_KEY對應(yīng)的是主機(jī)名,LOCAL_ADDRESS_KEY對應(yīng)的是ip地址;都是通過InetAddress取出地址相關(guān)屬性的,對這個些類不熟悉可以去看一下網(wǎng)絡(luò)編程;在SystemUtil里面使用了幾個靜態(tài)內(nèi)部類,并且構(gòu)造函數(shù)都是私有的,主要是防止外面直接創(chuàng)建;(四)配置log4j應(yīng)用configureLog4j(resourceLoader, props);這里傳入了資源裝載器,和相關(guān)的資源文件;首先要取出log4j配置文件的路徑,這里還是三種策率:(1)如果servletConfig里面有配置文件的

21、引用路徑優(yōu)先考慮;(2)如果servletConfig里面沒有直接在servletContext里面尋找;(3)如果servletContext里面也不能找到直接用缺省配置/WEB-INF/log4j.xml;接下來把路徑生成相關(guān)的url對象;然后把文件取出來;這里對log4j的配置文件有兩種解析方式,如果配置文件名為*.xml,則使用DOMConfigurator,否則使用PropertyConfigurator。現(xiàn)在log4j可以工作了所以進(jìn)行下面操作 resourceLoader.setLoggerReady(true);現(xiàn)在打日志就沒有問題了;(五)取得Scheme的實例 首先取得sc

22、hemeName 先在servletConfig里面找如果沒有去servletContext 里面找如果都沒有找到暫時為turbine;構(gòu)造一個名為webx.scheme.turbine的serviceId,這里的serviceId是一個文件的名字;這個文件里面存有一條信息com.alibaba.turbine.scheme.TurbineScheme,從這里應(yīng)該看出來這是一個類的全名,并且可能是Scheme的實現(xiàn)類;接下來ClassLoaderUtil來裝載這個類,ClassLoaderUtil.loadServiceClass(serviceId)這個方法指定了當(dāng)前線程的類裝載器來裝載這個

23、類;在這里構(gòu)造了一個完整的路徑名:META-INF/services/webx/scheme/turbine,并且用當(dāng)前的類裝載器的getResource()方法把文件路徑轉(zhuǎn)換成url,然后直接把輸入流打開;得到一個完整類名,并且用當(dāng)前類裝載器把com.alibaba.turbine.scheme.TurbineScheme裝載;個人覺得這樣的實現(xiàn)有點麻煩;類裝載之后實例化com.alibaba.turbine.scheme.TurbineScheme;關(guān)于TurbineScheme Michael在注釋中寫到經(jīng)典的webx方案,可見它的重要性,在后面使用到它的地方將為大家介紹;(六)取得 取

24、得web.xml中定義的servlet paths,并規(guī)格化之,讓我們拿出的請求路徑更加有規(guī)律;這里會在servletConfig,servletContext 去找webx.servlet.paths如果找不到缺省為空字符串;根據(jù)我這里的上下文路徑這里沒有這個參數(shù);(七)Configuration配置對象的接口,Configuration configuration = configureWebx(resourceLoader, props, scheme);這里要去找一個webxConfiguration的配置路徑,首先在servletConfig,servletContext去找初始化參

25、數(shù)webx.configuration,如果都沒有找到的話使用缺省值 webxConfiguration=/WEB-INF/webx.xml而在當(dāng)前系統(tǒng)中用的是缺省配置;用web資源裝載器得到/WEB-INF/webx.xml對應(yīng)的url;從這個配置文件的名稱應(yīng)該可以看出這里拉出了webx初始化的開端;接下來讀取webx配置文件,將配置文件映射為Configuration對象,接下來會在servletConfig,servletContext中去找參數(shù)webx.configuration.loader.class如果找不到的話就以默認(rèn)值com.alibaba.webx.configuratio

26、n.WebxConfigurationLoader為使用類;WebxConfigurationLoader調(diào)用了JellyDigester的parse();這里聲明了一個內(nèi)部類 private class DefaultJellyContext extends JellyContext configuration 最終是通過Jelly引擎解析出來的;Jelly是一種基于java的腳本引擎,提供了解析xml的功能,它還具有比較強(qiáng)的動態(tài)特性;接下來在web.xml中取出ponents,這里是在servletContext中取到的,取出了六個組件auction, member, my

27、taobao, common, shop, message并且為每一個組件也就是car包分配一個資源裝載器的實例;實現(xiàn)代碼如下:ResourceLoaderService componentResourceLoader = (ResourceLoaderService) resourceLoader.getInstance(componentName);根據(jù)配置將每一個組件的配置文件轉(zhuǎn)換成Configuration,并且和主要的Configuration對象進(jìn)行合并,具體實現(xiàn)代碼如下:car包下的Configuration合并到總的Configuration時,key全部變?yōu)閏ar包的comp

28、onentname+key值;public static void loadComponent(Configuration configuration, String componentName,String className, URL url, Map properties) throws DigesterException Configuration componentConfiguration = load(className, url, properties);/ 將componentName加入到services.instances中,確保不重復(fù)。if(!configuration

29、.getList(ServiceManager.SERVICE_INSTANCES_KEY).contains(componentName) configuration.addProperty(ServiceManager.SERVICE_INSTANCES_KEY, componentName);/ 將component配置加入到主配置中。ConfigurationUtil.merge(configuration, componentConfiguration, componentName);(八):調(diào)用com.alibaba.turbine.scheme.TurbineScheme的app

30、lyDefaultConfiguration()方法;這一步主要是初始化webx的默認(rèn)配置;將相關(guān)配置文件里面配置的信息加入到Configuration中;1:首先將webx-default.xml中的配置合并到總的configuration對象中,具體步驟如下:用web資源裝載器尋找/WEB-INF/webx-default.xml如果這個文件存在,生成對應(yīng)于/WEB-INF/webx-default.xml的Configuration;生成Configuration都是用的Jelly來解析xml生成的Configuration對象;第一步將configuration對象里面標(biāo)記為servi

31、ces的配置信息讀取出來,根據(jù)configuration的規(guī)則,只搜索根目錄下面一級的services標(biāo)記例如: 根據(jù)上面例子取出來的是RunDataService,PullService,把這些值放到set里面;然后迭代這個set對象,以services為前綴,set里面取出的值;拼成的值類似于services.RunDataService最終以拼裝的值為key在defaultConfiguration將相關(guān)屬性取出,全部合并到總的Configuration中;在這里如果總的Configuration里面的配置和缺省Configuration的配置的key一樣的話,忽略缺省Configura

32、tion的配置,不進(jìn)行合并;在webx框架有一個優(yōu)先級,默認(rèn)是以webx-defaut.xml的配置為準(zhǔn),如果webx.xml的配置和webx-defaut.xml的配置相同以webx.xml為準(zhǔn);各car包下面的配置不會和以上兩個文件的配置產(chǎn)生沖突;2:將缺省對象中key為component.services對應(yīng)的配置信息讀取出來;以上面的配置文件信息為例,根據(jù)解析,最終得到一個set,這個set里面包含的數(shù)據(jù)是(liuzh,liangkuan);循環(huán)set 以liuzh為例拼裝成component.services.liuzh,并且將defaultConfiguration對應(yīng)的值取出來,

33、組合到總的Configuration中;這里添加的key的規(guī)則是instanceName + . + serviceKey + . + defaultKey;參見具體代碼:Configuration defaultConfiguration, String serviceName, boolean isSubInstance) StringserviceKey = ServiceManager.SERVICE_PREFIX + . + serviceName;boolean standalone = true;if (isSubInstance) / 如果isSubInstance,表示對每個

34、instance設(shè)置默認(rèn)值。/ 將instance.services.ServiceName.*設(shè)置到每個instanceName.services.ServiceName中for (Iterator i = configuration.getList(ServiceManager.SERVICE_INSTANCES_KEY).iterator(); i.hasNext();) standalone = false;StringinstanceName= (String) i.next();Configuration serviceConfiguration = defaultConfigur

35、ation.subset(WEBX_CONFIGURATION_DEFAULT_INSTANCE+ . + serviceKey);for (Iterator keys = serviceConfiguration.getKeys(); keys.hasNext();) String defaultKey = (String) keys.next();String key = instanceName + . + serviceKey + . + defaultKey;if (!configuration.containsKey(key) configuration.addProperty(k

36、ey, serviceConfiguration.getProperty(defaultKey); if (log.isDebugEnabled() log.debug(Added default configuration for service + serviceName + .+ instanceName);defaultConfiguration = defaultConfiguration.subset(WEBX_CONFIGURATION_DEFAULT_INSTANCE);3:加入其它非service的默認(rèn)主配置:main-instance,加入其它非service的默認(rèn)配置:s

37、ub-instances。4:將/classpath/com/alibaba/turbine/scheme/webx-turbine-default.xml的配置信息合并到總的Configuration;這個配置文件的優(yōu)先級是最低的,如果前面沒有配置和webx-turbine-default.xml相同的配置,將使用webx-turbine-default.xml里面配置的服務(wù);5:加載/classpath/com/alibaba/webx/scheme/webx-default.xml配置的信息到總的Configuration里面了;接下來組合一個keyservices.ThreadCont

38、extService.factory.default 做了下面操作;if (!configuration.getList(key).contains(RunDataObjectFactory.class.getName() configuration.addProperty(key, RunDataObjectFactory.class.getName();(九):初始化ServiceManager第一步找到ServiceManager的默認(rèn)實現(xiàn)類首先會在servletconfig里面尋找參數(shù)名為service.manager.class對應(yīng)的值,如果沒有找到去servletcontext里面

39、去找,如果都沒有找到以com.alibaba.webx.service.DefaultWebxServiceManager作為ServiceManager的實現(xiàn)類DefaultServiceManager是DefaultWebxServiceManager的父類;得到ServiceManager的實例以后把資源裝載,和總的configration注入到manager中,讓ServiceManager可以方便調(diào)用這些屬性;接下來調(diào)用manager的init(),實際上是調(diào)用的DefaultServiceManager的init方法;首先調(diào)用initMapping();通過 configurati

40、on.getStringArray(services.instances)取出一個admin, member, auction, auxiliary, crm, support, product數(shù)組;這個數(shù)組里面存儲的是所有的car的名字,根據(jù)數(shù)組長度循環(huán),configuration.subset(instanceName)取出每個car下面的configuration然后以實例名為key,具體car包下的configration為value放入map中,這樣就把每個car包下的配置獨(dú)立開來了;接下來找出每一個service的名字并且根據(jù)為每一個service配置一個初始化一個ServiceI

41、nstance,并且把每一個service對應(yīng)的configration 注入到ServiceInstance中,這樣每個service可以方便的找到屬于自己的configration;然后將每一個ServiceInstance 以instancekey為key ServiceInstance為value放入一個map中,讓ServiceManager以后可以通過相關(guān)key輕松拿到對應(yīng)service的所有信息;instancekey的構(gòu)造規(guī)則,如果是主配置的話直接是service的名字,如果是car下面的配置的話為car的名字加上service的名字下一步為初始化resource loader

42、 services;這里可以分析下怎么取到service的,一般是調(diào)用manager的getService(String serviceName, String instanceName)首先根據(jù)serviceName和instanceName取得相關(guān)service的ServiceInstance 對象;如果這里不是car包里面的service第二個參數(shù)傳null;這里manager把serviceName,instanceName組合成instancekey給據(jù)這個key在map里面取出ServiceInstance;拿到ServiceInstance的實例之后,調(diào)用manager.init

43、Service(ServiceInstance,false)ServiceInstance是DefaultServiceManager的一個內(nèi)部類;這里實際上是調(diào)用的ServiceInstance的getInstance()方法,因為ServiceInstance正好對應(yīng)一個service;getInstance()方法是一個同步方法,防止不同的線程都在初始化ServiceInstance,這樣保證了ServiceInstance,首先判斷 ServiceInstance里面的service屬性是否為空的,如果service已經(jīng)初始化了直接返回service的實例,所有我們自定義的servic

44、e是繼承于Service接口的;這里避免了反復(fù)初始化service,讓我們拿到的service都是同一個實例;接下來在service對應(yīng)的configration中把當(dāng)前service對應(yīng)的calssname取出來;這里在得實例話service的時候有多種策率,如果是主配置,直接拿到class然后執(zhí)行newInstance();在這里實現(xiàn)了service的單例和多例;得到service的實例以后執(zhí)行service的init(),將ServiceInstance參數(shù)傳給init(),這樣每個service可以方便的拿到自己的實例;最后執(zhí)行了initServices();這一步是將需要在啟動容器初

45、始化的service進(jìn)行初始化;首先取出所有的service的名字,和所有car上下文的名字分別放在兩個數(shù)組里面循環(huán)這個裝有所有的service名稱的serviceNames數(shù)組,分別取出每一個servicename對應(yīng)的instance, 取出對應(yīng)主配置的ServiceInstance用getServiceInstance(serviceName, null);取出對應(yīng)car下面的service的ServiceInstance取出car下面的ServiceInstance第二個參數(shù)不能為空應(yīng)該為對應(yīng)car實例的名字;取出了ServiceInstance之后需要判斷當(dāng)前ServiceInsta

46、nce實例對應(yīng)的configration的EarlyInit屬性是否為true如果為true說明需要初始化,執(zhí)行 initService(ServiceInstance, true); 進(jìn)入方法以后先拿到具體的service的實例, 獲取service實例還是運(yùn)用的ServiceInstance的方法getInstance();如果service已經(jīng)初始化了 ,將不進(jìn)行初始化;執(zhí)行初始化只需要調(diào)用service的init(instance)方法;一般的service里面沒有這個方法,這樣可以直接調(diào)用GenericService的init(instance)然后回調(diào)子類的init();這種調(diào)用模式可以利用GenericService的在每一個ServiceInstance里面有一個Service instance,如果service已經(jīng)實例化了之后直接可以取出來不用再次實例化service;所有需要初始化的service初始化完成以后;將WebxController的實例放在ServiceManager中以webx.controller為key;就這樣我們的service平臺就已經(jīng)構(gòu)建好了;這里要注意不能引起遞歸調(diào)用;(十)其它有關(guān)WebxController配置:首先通過serviceManager取出RunDat

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論