在线不卡日本ⅴ一区v二区_精品一区二区中文字幕_天堂v在线视频_亚洲五月天婷婷中文网站

  • <menu id="lky3g"></menu>
  • <style id="lky3g"></style>
    <pre id="lky3g"><tt id="lky3g"></tt></pre>

    Spring 系列(四):我們來聊聊

    背景

    上篇最后給大家了一個建議,建議配置bean掃描包時使用如下寫法:

    spring-mvc.xml

    spring.xml

    文中提到通過以上配置,就可以在Spring MVC容器中只注冊有@Controller注解的bean,Spring容器注冊除了@Controller的其它bean。

    有的同學留言問為什么這樣寫就達到這種效果了呢?

    也有人可能認為我是無腦從網(wǎng)上抄來的,我有什么依據(jù),憑什么這么說?經(jīng)過ISO 9000認證了嗎?

    為了維護文章的權(quán)威性以及我的臉面,本篇我就繼續(xù)帶大家從官網(wǎng)和源碼兩方面進行分析。

    2. 流程分析

    2.1 Java注解

    不是說好的講嗎,怎么注解亂入了。

    放心,雖然看源碼累,寫讓大家看懂的文章更累,但是我還沒瘋。

    為什么講注解,因為Spring中很多地方用到注解,本文及前幾篇文章大家或多或少也都有看到。

    因此在這里加個小灶,和大家一起回顧一下注解的知識點。

    先查看官方文檔:

    https://docs.oracle.com/javase/tutorial/java/annotations/predefined.html

    Annotations, a form of metadata, provide data about a program that is not part of the program itself. Annotations have no direct effect on the operation of the code they annotate.Annotations have a number of uses, among them:* Information for the compiler — Annotations can be used by the compiler to detect errors or suppress warnings.* Compile-time and deployment-time processing — Software tools can process annotation information to generate code, XML files, and so forth.* Runtime processing — Some annotations are available to be examined at runtime.上面一段話翻譯過來:注解是原數(shù)據(jù)的一種形式,對標注的代碼邏輯上沒有直接的影響,只是用來提供程序的一些信息。主要用處如下:* 為編譯器提供信息,比如錯誤檢測或者警告提示。* 在編譯和部署期處理期,程序可以根據(jù)注解信息生成代碼、xml文件。* 在程序運行期用來做一些檢查。

    2.2 Java元注解

    JAVA為了開發(fā)者能夠靈活定義自己的注解,因此在java.lang.annotation包中提供了4種元注解,用來注解其它注解。

    查看官方文檔對這4種元注解的介紹:

    1.@Retention

    @Retention annotation specifies how the marked annotation is stored:* RetentionPolicy.SOURCE – The marked annotation is retained only in the source level and is ignored by the compiler.* RetentionPolicy.CLASS – The marked annotation is retained by the compiler at compile time, but is ignored by the Java Virtual Machine (JVM).* RetentionPolicy.RUNTIME – The marked annotation is retained by the JVM so it can be used by the runtime environment.

    翻譯:指定標記的注解存儲范圍??蛇x范圍是原文件、class文件、運行期。

    2.@Documented

    @Documented annotation indicates that whenever the specified annotation is used those elements should be documented using the Javadoc tool. (By default, annotations are not included in Javadoc.) For more information, see the Javadoc tools page.

    翻譯:因為注解默認是不會被JavaDoc工具處理的,因此@Documented用來要求注解能被JavaDoc工具處理并生成到API文檔中 。

    3.@Target

    @Target annotation marks another annotation to restrict what kind of Java elements the annotation can be applied to. A target annotation specifies one of the following element types as its value:* ElementType.ANNOTATION_TYPE can be applied to an annotation type.* ElementType.CONSTRUCTOR can be applied to a constructor.* ElementType.FIELD can be applied to a field or property.* ElementType.LOCAL_VARIABLE can be applied to a local variable.* ElementType.METHOD can be applied to a method-level annotation.* ElementType.PACKAGE can be applied to a package declaration.* ElementType.PARAMETER can be applied to the parameters of a method.* ElementType.TYPE can be applied to any element of a class.

    翻譯:用來標識注解的應用范圍??蛇x的范圍是注解、構(gòu)造函數(shù)、類屬性、局部變量、包、參數(shù)、類的任意元素。

    4.@Inherited

    @Inherited annotation indicates that the annotation type can be inherited from the super class. (This is not true by default.) When the user queries the annotation type and the class has no annotation for this type, the class’ superclass is queried for the annotation type. This annotation applies only to class declarations.

    翻譯:默認情況下注解不會被子類繼承,被@Inherited標示的注解可以被子類繼承。

    上面就是對4種元注解的介紹,其實大部分同學都知道,這里只是一起做個回顧,接下來進入正體。

    2.3 @Controller介紹

    查看官方文檔:

    Indicates that an annotated class is a “Controller” (e.g. a web controller).This annotation serves as a specialization of @Component, allowing for implementation classes to be autodetected through classpath scanning. It is typically used in combination with annotated handler methods based on the RequestMapping annotation.

    翻譯:@Controller注解用來標明一個類是Controller,使用該注解的類可以在掃描過程中被檢測到。通常@Controller和@RequestMapping注解一起使用來創(chuàng)建handler函數(shù)。

    我們在來看看源碼,在org.springframework.stereotype包下找到Controller類。

    @Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic @interface Controller { String value() default “”;}

    可以看到Controller聲明為注解類型,類上的@Target({ElementType.TYPE}) 注解表明@Controller可以用到任意元素上,@Retention(RetentionPolicy.RUNTIME)表明注解可以保存到運行期,@Documented表明注解可以被生成到API文檔里。

    除定義的幾個元注解外我們還看到有個@Component注解,這個注解是干什么的呢?

    查看官方文檔:

    Indicates that an annotated class is a “component”. Such classes are considered as candidates for auto-detection when using annotation-based configuration and classpath scanning.

    翻譯:被@Component注解標注的類代表該類為一個component,被標注的類可以在包掃描過程中被檢測到。

    再看源碼:

    @Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Component { String value() default “”;}

    可以看到@Component注解可以用在任意類型上,保留在運行期,能生成到API文檔中。

    再回到@Controller注解,正是因為@Controller被@Component標注,因此被@Controller標注的類也能在類掃描的過程中被發(fā)現(xiàn)并注冊。

    另外Spring中還用@Service和@Repositor注解定義bean,@Service用來聲明service類,@Repository用來聲明DAO累。

    其源碼如下:

    @Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic @interface Service { String value() default “”;}@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic @interface Repository { String value() default “”;}

    2.4 源碼剖析

    鋪墊都結(jié)束了,現(xiàn)在開始重頭戲。

    和元素一樣, 也屬于自定義命名空間,對應的解析器是ComponentScanBeanDefinitionParser。

    自定義命名空間的解析過程可以參考上篇,此處不再介紹。

    我們進入CommponentScanBeanDefinitionParser類的parse()方法。

    @Overridepublic BeanDefinition parse(Element element, ParserContext parserContext) { //此處 BASE_PACKAGE_ATTRIBUTE = “base-package”; //1.獲取要掃描的包 String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE); //此處CONFIG_LOCATION_DELIMITERS = “,; “, //把,或者;分割符分割的包放到數(shù)組里面 String[] basePackages = StringUtils.tokenizeToStringArray(basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); //2.創(chuàng)建掃描器 ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element); //3.掃描包并注冊bean Set beanDefinitions = scanner.doScan(basePackages); return null;}

    上面掃描注冊過程可以分為3步。

    (1)獲取要掃描的包。

    (2)創(chuàng)建掃描器。

    (3)掃描包并注冊bean。

    第1步邏輯比較簡單,就是單純的讀取配置文件的”base-package”屬性得到要掃描的包列表。

    我們從第2步開始分析。

    2.4.1 創(chuàng)建掃描器

    進入configureScanner方法()。

    protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) { //useDefaultFilters默認為true,即掃描所有類型bean boolean useDefaultFilters = true; //1.此處USE_DEFAULT_FILTERS_ATTRIBUTE = “use-default-filters”,獲取其XML中設(shè)置的值 if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) { useDefaultFilters = Boolean.valueOf(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)); } //2.創(chuàng)建掃描器 ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters); //3.解析過濾類型 parseTypeFilters(element, scanner, parserContext); //4.返回掃描器 return scanner;}

    創(chuàng)建掃描器的方法分為4步。

    (1)獲取掃描類范圍。

    (2)根據(jù)掃描范圍初始化掃描器。

    (3)設(shè)置掃描類的過濾器。

    (4)返回創(chuàng)建的掃描器。

    第1步也比較簡單,從配置文件中獲得“use-default-filters”屬性的值,默認是true,即掃描所有類型的注解。

    我們進入第2步的createScanner()方法,看看如何創(chuàng)建掃描器。

    protected ClassPathBeanDefinitionScanner createScanner(XmlReaderContext readerContext, boolean useDefaultFilters) { //新建一個掃描器 return new ClassPathBeanDefinitionScanner(readerContext.getRegistry(), useDefaultFilters, readerContext.getEnvironment(),readerContext.getResourceLoader());}

    沿調(diào)用棧進入ClassPathBeanDefinitionScanner()方法。

    public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, //如果useDefaultFilters為true,注冊默認過濾器 if (useDefaultFilters) { //注冊默認過濾器 registerDefaultFilters(); }}

    進入registerDefaultFilters()方法。

    protected void registerDefaultFilters() { this.includeFilters.add(new AnnotationTypeFilter(Component.class));}

    可以看到上面方法把Component注解類型加入到了掃描白名單中,因此被@Component標注的類都會被掃描注冊。

    在此,大家也明白為什么@Controller、@service、@Repository標注的類會被注冊了吧,因為這些注解都用@Component標注了。

    我們再進入第3步的parseTypeFilters()方法,看如何設(shè)置過濾器。

    protected void parseTypeFilters(Element element, ClassPathBeanDefinitionScanner scanner, ParserContext parserContext) { //解析exclude-filter和include-filter元素 //獲取元素所有子節(jié)點 NodeList nodeList = element.getChildNodes(); //遍歷元素子節(jié)點 for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { String localName = parserContext.getDelegate().getLocalName(node); //解析include-filter元素 ,此處 INCLUDE_FILTER_ELEMENT = "include-filter" if (INCLUDE_FILTER_ELEMENT.equals(localName)) { //創(chuàng)建類型過濾器 TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext); //把解析出來的類型加入白名單 scanner.addIncludeFilter(typeFilter); } //解析exclude-filter元素,此處EXCLUDE_FILTER_ELEMENT = "exclude-filter" else if (EXCLUDE_FILTER_ELEMENT.equals(localName)) { //創(chuàng)建類型過濾器 TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext); //把解析出來的類型加入黑名單 scanner.addExcludeFilter(typeFilter); } }}

    進入createTypeFilter()方法查看實現(xiàn)邏輯。

    protected TypeFilter createTypeFilter(Element element, ClassLoader classLoader, ParserContext parserContext) { //獲取xml中type屬性值,此處FILTER_TYPE_ATTRIBUTE = “type” String filterType = element.getAttribute(FILTER_TYPE_ATTRIBUTE); //獲取xml中expression屬性值,此處FILTER_EXPRESSION_ATTRIBUTE = “expression”,獲取xml中該屬性值 String expression = element.getAttribute(FILTER_EXPRESSION_ATTRIBUTE); expression = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(expression); //如果是注解類型,創(chuàng)建注解類型過濾器,并把需要過濾的注解類設(shè)置進去 if (“annotation”.equals(filterType)) { return new AnnotationTypeFilter((Class) ClassUtils.forName(expression, classLoader)); }}

    上面就是創(chuàng)建掃描器的過程,主要是將XML文件中設(shè)置的類型添加到白名單和黑名單中。

    2.4.2 掃描注冊bean

    得到掃描器后,開始掃描注冊流程。

    進入doScan()方法。

    protected Set doScan(String… basePackages) { Set beanDefinitions = new LinkedHashSet(); //遍歷所有需要掃描的包 for (String basePackage : basePackages) { //1.在該包中找出用@Component注解的類,放到候選列表中 Set candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { //2.判斷容器中是否已經(jīng)有bean信息,如果沒有就注冊 if (checkCandidate(beanName, candidate)) { //生成bean信息 BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); //添加bean信息到bean定義列表中 beanDefinitions.add(definitionHolder); //3.把bean注冊到IOC容器中 registerBeanDefinition(definitionHolder, this.registry); } }}

    掃描注冊過程分為3步。

    (1)從包中找出需要注冊的bean并放到候選列表中。

    (2)遍歷候選列表中的所有bean,判斷容器中是否已經(jīng)存在bean。

    (3)如果不存在bean,就把bean信息注冊到容器中。

    接下來依次分析上面掃描注冊流程。

    2.4.2.1 查找候選bean

    我們先看第1步,查找候選bean的過程。進入findCandidateComponents()方法。

    public Set findCandidateComponents(String basePackage) { Set candidates = new LinkedHashSet(); //1.獲取包的classpath String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + ‘/’ + this.resourcePattern; //2.把包下的所有class解析成resource資源 Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath); //遍歷所有類resource for (Resource resource : resources) { if (resource.isReadable()) { //3.獲取類的元信息 MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource); //4.判斷是否候選component if (isCandidateComponent(metadataReader)) { //5.根據(jù)類元信息生成beanDefinition ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setResource(resource); sbd.setSource(resource); //6.判斷該bean是否能實例化 if (isCandidateComponent(sbd)) { //7.加入候選類列表 candidates.add(sbd); } //8.返回候選components選列表 return candidates;}

    查找bean的流程比較繁瑣,可以分為以下8步。

    (1)獲取包掃描路徑。

    (2)把包路徑下的所有類解析成resource類。

    (3)解析resource類,獲取類的元信息。

    (4)根據(jù)類元信息判斷該類是否在白名單中。

    (5)如果在白名單中,生成beanDefinition信息。

    (6)根據(jù)beanDefinition信息判斷類是否能實例化。

    (7)如果可以實例化,將beanDefinition信息加入到候選列表中。

    (8)返回保存beanDefinition信息的候選列表。

    還記得BeanDefinition是什么吧,主要是保存bean的信息。如果不記得看看Spring注冊流程。

    因為其它邏輯比較簡單,在此我們重點分析第4步和第6步。

    先看第4步,進入isCandidateComponent()方法。

    protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { //1.遍歷黑名單,若傳入的類元信息在黑名單中返回false for (TypeFilter tf : this.excludeFilters) { //判斷是否和傳入的類匹配 if (tf.match(metadataReader, this.metadataReaderFactory)) { return false; } } //2.遍歷白名單,若傳入的類元信息在白名單中返回true for (TypeFilter tf : this.includeFilters) { if (tf.match(metadataReader, this.metadataReaderFactory)) { //根據(jù)@Conditional注解判斷是否注冊bean,如果沒有@Conditional注解,返回true. return isConditionMatch(metadataReader); } } return false;}

    可以看到上面主要邏輯是判斷該類是否在白名單或黑名單列表中,如果在白名單,則返回true,在黑名單返回false。黑、白名單的值就是創(chuàng)建掃描流程中通過parseTypeFilters()方法設(shè)置進去的。

    再稍微提一下上面@Conditional注解,此注解是Spring 4中加入的,作用是根據(jù)設(shè)置的條件來判斷要不要注冊bean,如果沒有標注該注解,默認注冊。我們在這里不展開細說,有興趣的同學可以自己查閱相關(guān)資料。

    我們再看第6步,進入isCandidateComponent()方法。

    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { //獲取元類信息 AnnotationMetadata metadata = beanDefinition.getMetadata(); //判斷是否可以實例化 return (metadata.isIndependent() && (metadata.isConcrete() || (metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));}

    可以看到上面是根據(jù)該類是不是接口、抽象類、嵌套類等信息來判斷能否實例化的。

    2.4.2.2 判斷bean是否已經(jīng)注冊

    候選bean列表信息已經(jīng)得到,再看看如何對列表中的bean做進一步判斷。

    進入checkCandiates()方法。

    protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) { if (!this.registry.containsBeanDefinition(beanName)) { return true; } return false;}

    上面方法比較簡單,主要是查看容器中是否已經(jīng)有bean的定義信息。

    2.4.2.3 注冊bean

    對bean信息判斷完成后,如果bean有效,就開始注冊bean。

    進入registerBeanDefinition()方法。

    protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) { BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);}

    再進入registerBeanDefinition()方法。

    public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) { //得到beanname String beanName = definitionHolder.getBeanName(); //注冊bean信息 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); //注冊bean的別名 String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); }}

    上面流程大家有沒有似曾相識,和Spring解析注冊流程文中注冊bean的邏輯一樣。

    到此就完成了掃描注冊bean流程的分析。接下來就是bean的實例化等流程,大家同樣可以參考Spring解析注冊流程一文。

    3.小結(jié)

    看完上面的分析,相信大家對有了深入的了解。

    現(xiàn)在回到開頭的那段代碼。會不會有“誠不我欺也”的感覺。

    最后,我再把那段代碼貼出來,大家對著代碼在腦海里想象一下其解析流程,檢驗一下掌握程度。

    如果有哪一步卡住了,建議再回頭看看我的文章,直至能在腦海中有一個完整的流程圖,甚至能想到對應的源代碼段。

    如果能做到這樣,說明你真正理解了,接下來就可以愉快的和小伙伴炫技或者和面試官去侃大山了。

    spring-mvc.xml

    spring.xml

    本文完。


    推薦閱讀:

    Spring系列(一):Spring MVC bean 解析、注冊、實例化流程源碼剖析

    Spring 系列(二):Spring MVC的父子容器

    Spring 系列(三):你真的懂@RequestMapping嗎 ?

    鄭重聲明:本文內(nèi)容及圖片均整理自互聯(lián)網(wǎng),不代表本站立場,版權(quán)歸原作者所有,如有侵權(quán)請聯(lián)系管理員(admin#wlmqw.com)刪除。
    用戶投稿
    上一篇 2022年6月25日 06:15
    下一篇 2022年6月25日 06:15

    相關(guān)推薦

    • 分享4條發(fā)微商朋友圈的方法(微商朋友圈應該怎么發(fā))

      對于微商朋友來說,朋友圈的重要性不言而喻了。 那么微商的朋友圈到底該怎么發(fā)呢? 為什么同樣是經(jīng)營一個朋友圈,有的微商看起來逼格滿滿,實際效果也不錯;而有的卻動都不動就被屏蔽甚至拉黑…

      2022年11月27日
    • 計算機網(wǎng)絡(luò)技術(shù)論文(計算機網(wǎng)絡(luò)技術(shù)論文七千字)

      今天小編給各位分享計算機網(wǎng)絡(luò)技術(shù)論文的知識,其中也會對計算機網(wǎng)絡(luò)技術(shù)論文七千字進行解釋,如果能碰巧解決你現(xiàn)在面臨的問題,別忘了關(guān)注本站,現(xiàn)在開始吧! 計算機網(wǎng)絡(luò)方面的論文3000字…

      2022年11月26日
    • 《寶可夢朱紫》夢特性怎么獲得?隱藏特性獲取方法推薦

      寶可夢朱紫里有很多寶可夢都是擁有夢特性會變強的寶可夢,很多玩家不知道夢特性怎么獲得,下面就給大家?guī)韺毧蓧糁熳想[藏特性獲取方法推薦,感興趣的小伙伴一起來看看吧,希望能幫助到大家。 …

      2022年11月25日
    • 《寶可夢朱紫》奇魯莉安怎么進化?奇魯莉安進化方法分享

      寶可夢朱紫中的奇魯莉安要怎么進化呢?很多玩家都不知道,下面就給大家?guī)韺毧蓧糁熳掀骠斃虬策M化方法分享,感興趣的小伙伴一起來看看吧,希望能幫助到大家。 奇魯莉安進化方法分享 奇魯莉安…

      2022年11月25日
    • 客服的崗位職責怎么寫(客服工作內(nèi)容及職責)

      各位小伙伴們大家周一好,又到了每周一給大家分享干貨內(nèi)容的時候啦~ 本期來跟大家分享一下客服工作管理流程以及客服崗位里面的每項職能崗位的核心細則,也是干貨滿滿推薦收藏~ 一.補償流程…

      2022年11月25日
    • 鬧劇落下帷幕,曼聯(lián)官宣 C 羅離隊

      1、鬧劇落下帷幕,曼聯(lián)官宣 C 羅離隊 在經(jīng)歷過半個賽季的激烈鬧劇、C 羅私自接受采訪炮轟曼聯(lián)之后,俱樂部終于做出了相對應的措施:正式官宣 C 羅離隊。 在曼聯(lián)俱樂部發(fā)布的聲明中寫…

      2022年11月24日
    • iqoo11什么時候上市 iqoo11發(fā)布時間最新消息

      iqoo11什么時候發(fā)布?隨著新一代旗艦芯片的發(fā)布,各家手機廠商也是公布了自己的旗艦機,那么iqoo11什么時候發(fā)布呢?下面就讓小編為大家介紹一下,一起來看看吧。 iqoo11什么…

      2022年11月24日
    • 曝小米13系列已量產(chǎn):起步價格或定在4500元左右

      高通目前已經(jīng)發(fā)布第二代驍龍8芯片,首批機型已經(jīng)蓄勢待發(fā),小米此前也已經(jīng)宣布新旗艦要率先搭載。 據(jù)澎湃報道,小米13系列已經(jīng)正式量產(chǎn),全系均搭載4nm芯片,不出意外是標配第二代驍龍8…

      2022年11月24日
    • 前三季度,市場規(guī)模超過五萬億元 信息消費展現(xiàn)蓬勃生機

      家居企業(yè)個性化全屋定制系統(tǒng),備受消費者青睞;主打?qū)I(yè)電競的新款高性能便攜式計算機,銷量表現(xiàn)創(chuàng)新高;物流企業(yè)推出數(shù)智化供應鏈興農(nóng)服務項目,助力優(yōu)質(zhì)農(nóng)產(chǎn)品出深山…… 不久前,工信部發(fā)布…

      2022年11月24日
    • 王者榮耀高幀率模式支持機型列表最新 新增機型名單

      11月24日上午8:30-9:30,《王者榮耀》將進行全服不停機更新。本次更新大小安卓約33MB,iOS約44MB。 為了讓玩家們能享受更平滑,更精美的王者榮耀操作畫面體驗,在設(shè)置…

      2022年11月24日

    聯(lián)系我們

    聯(lián)系郵箱:admin#wlmqw.com
    工作時間:周一至周五,10:30-18:30,節(jié)假日休息