Springboot自定义xml文件解析

2022-07-04  本文已影响0人  SpaceCat

有时候,要通过自定义XML配置文件来实现一些特定的功能。这里通过例子来说明。

1、说明

首先,看部分spring加载bean文件的源码:
spring-beans-5.0.6.RELEASE.jar!/org/springframework/beans/factory/xml/PluggableSchemaResolver.class

public class PluggableSchemaResolver implements EntityResolver {  
    public static final String DEFAULT_SCHEMA_MAPPINGS_LOCATION = "META-INF/spring.schemas";  
    private static final Log logger = LogFactory.getLog(PluggableSchemaResolver.class);

spring-beans-5.0.6.RELEASE.jar!/org/springframework/beans/factory/xml/DefaultNamespaceHandlerResolver.class

public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {  
    public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";  
    protected final Log logger;  
    @Nullable

可以看出,spring在加载xml文件的时候,会默认读取配置文件META-INF/spring.schemasMETA-INF/spring.handlers。这样,我们就可以在这两个文件添加我们自定义的xml文件格式和xml文件解析处理器。

2、解析自定义xml配置文件

2.1 创建一个基本的Springboot工程

新建一个Springboot工程,pom如下。
SelfDefineXmlTrial/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.lfqy.springboot</groupId>
    <artifactId>SelfDefineXmlTrial</artifactId>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.2.RELEASE</version>
    </parent>

    <dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    </dependencies>

</project>

然后,新建一个用于测试controller。
com.lfqy.springboot.selfdefxml.controller.SelfDefXmlController

package com.lfqy.springboot.selfdefxml.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * Created by chengxia on 2022/5/7.
 */

@RestController
@RequestMapping("/selfdefxml")
public class SelfDefXmlController {
    @RequestMapping("/hello")
    public String hello(){
        return "Hello world!";
    }
}

最后,创建一个Springboot的启动类。
com.lfqy.springboot.selfdefxml.SelfDefXmlApplication

package com.lfqy.springboot.selfdefxml;

import com.lfqy.springboot.selfdefxml.beans.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

@SpringBootApplication
public class SelfDefXmlApplication {

    public static void main(String[] args) {
        SpringApplication.run(SelfDefXmlApplication.class);
        System.out.println("The application has been started ....");
    }
}

运行启动之后,浏览器访问http://localhost:8080/selfdefxml/hello效果如下:

image.png

2.2 添加自定义的xml并解析

2.2.1 添加自定义xml格式说明和处理器配置

修改前面提到的配置文件META-INF/spring.schemasMETA-INF/spring.handlers,添加xml格式说明。
META-INF/spring.schemas

http\://www.mmpp.com/schema/selfdef.xsd=META-INF/selfdef.xsd

META-INF/spring.handlers

http\://www.mmpp.com/schema/selfdef=com.lfqy.springboot.selfdefxml.selxmlparse.UserNamespaceHandler

添加xml格式说明配置文件。
META-INF/selfdef.xsd

<?xml version="1.0" encoding="UTF-8" standalone="no"?>  
  
<schema xmlns="http://www.w3.org/2001/XMLSchema"  
        xmlns:tns="http://www.mmpp.com/schema/selfdef"  
        targetNamespace="http://www.mmpp.com/schema/selfdef"  
        elementFormDefault="qualified">  
    <element name="user">  
        <complexType>            <attribute name="id" type="string"/>  
            <attribute name="name" type="string"/>  
            <attribute name="address" type="string"/>  
        </complexType>    </element>  
</schema>

2.2.2 添加自定义xml格式处理器

添加自定义xml格式处理器类。
com.lfqy.springboot.selfdefxml.selxmlparse.UserNamespaceHandler

package com.lfqy.springboot.selfdefxml.selxmlparse;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

/**
 * Created by chengxia on 2022/7/3.
 */
public class UserNamespaceHandler extends NamespaceHandlerSupport {

    @Override
    public void init() {
        registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
    }
}

新增xml格式解析类。
com.lfqy.springboot.selfdefxml.selxmlparse.UserBeanDefinitionParser

package com.lfqy.springboot.selfdefxml.selxmlparse;

import com.lfqy.springboot.selfdefxml.beans.User;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;

/**
 * Created by chengxia on 2022/7/3.
 */
public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

    @Override
    protected Class<?> getBeanClass(Element element) {
        return User.class;
    }

    @Override
    protected void doParse(Element element, BeanDefinitionBuilder builder) {
        String name = element.getAttribute("name");
        String address = element.getAttribute("address");

        if (StringUtils.hasText(name)) {
            builder.addPropertyValue("name", name);
        }
        if (StringUtils.hasText(address)) {
            builder.addPropertyValue("address", address);
        }
    }
}

2.2.3 添加自定义的bean类

新增自定义xml对应的bean类。
com.lfqy.springboot.selfdefxml.beans.User

package com.lfqy.springboot.selfdefxml.beans;

/**
 * Created by chengxia on 2022/7/3.
 */
public class User {

    private String name;

    private String address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

2.2.4 修改启动类读取自定义配置文件

添加自定义xml配置文件读取的相关逻辑。
com.lfqy.springboot.selfdefxml.SelfDefXmlApplication

package com.lfqy.springboot.selfdefxml;

import com.lfqy.springboot.selfdefxml.beans.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

@SpringBootApplication
public class SelfDefXmlApplication {

    public static void main(String[] args) {
        SpringApplication.run(SelfDefXmlApplication.class);
        System.out.println("The application has been started ....");
        ApplicationContext ac = new ClassPathXmlApplicationContext("SelfDefBeans.xml");
        User user = (User)ac.getBean("user");
        System.out.println(user.getName());
        System.out.println(user.getAddress());
    }
}

2.3 启动和运行

到这里,编码就完成了,工程的目录结构如下。


image.png

运行之后,控制台输出如下:


  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.0.2.RELEASE)

2022-07-03 23:54:48.457  INFO 3667 --- [           main] c.l.s.selfdefxml.SelfDefXmlApplication   : Starting SelfDefXmlApplication on ChengdeMBP with PID 3667 (/Users/chengxia/Developer/TrialProject/SpringBoot/SelfDefineXmlTrial/target/classes started by chengxia in /Users/chengxia/Developer/TrialProject)
2022-07-03 23:54:48.461  INFO 3667 --- [           main] c.l.s.selfdefxml.SelfDefXmlApplication   : No active profile set, falling back to default profiles: default
2022-07-03 23:54:48.628  INFO 3667 --- [           main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@45f45fa1: startup date [Sun Jul 03 23:54:48 CST 2022]; root of context hierarchy
2022-07-03 23:54:51.084  INFO 3667 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2022-07-03 23:54:51.121  INFO 3667 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2022-07-03 23:54:51.121  INFO 3667 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.31
2022-07-03 23:54:51.140  INFO 3667 --- [ost-startStop-1] o.a.catalina.core.AprLifecycleListener   : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [/Users/chengxia/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.]
2022-07-03 23:54:51.273  INFO 3667 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2022-07-03 23:54:51.273  INFO 3667 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 2656 ms
2022-07-03 23:54:51.492  INFO 3667 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Servlet dispatcherServlet mapped to [/]
2022-07-03 23:54:51.497  INFO 3667 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]
2022-07-03 23:54:51.498  INFO 3667 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2022-07-03 23:54:51.498  INFO 3667 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2022-07-03 23:54:51.498  INFO 3667 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]
2022-07-03 23:54:51.675  INFO 3667 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2022-07-03 23:54:52.034  INFO 3667 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@45f45fa1: startup date [Sun Jul 03 23:54:48 CST 2022]; root of context hierarchy
2022-07-03 23:54:52.153  INFO 3667 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/selfdefxml/hello]}" onto public java.lang.String com.lfqy.springboot.selfdefxml.controller.SelfDefXmlController.hello()
2022-07-03 23:54:52.158  INFO 3667 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2022-07-03 23:54:52.158  INFO 3667 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2022-07-03 23:54:52.190  INFO 3667 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2022-07-03 23:54:52.191  INFO 3667 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2022-07-03 23:54:52.448  INFO 3667 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2022-07-03 23:54:52.519  INFO 3667 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2022-07-03 23:54:52.526  INFO 3667 --- [           main] c.l.s.selfdefxml.SelfDefXmlApplication   : Started SelfDefXmlApplication in 4.696 seconds (JVM running for 5.555)
The application has been started ....
2022-07-03 23:54:52.532  INFO 3667 --- [           main] o.s.c.s.ClassPathXmlApplicationContext   : Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7cd1ac19: startup date [Sun Jul 03 23:54:52 CST 2022]; root of context hierarchy
2022-07-03 23:54:52.537  INFO 3667 --- [           main] o.s.b.f.xml.XmlBeanDefinitionReader      : Loading XML bean definitions from class path resource [SelfDefBeans.xml]
Paopao
Beijing

3、实现xml配置文件启动时自动加载

这里,通过实现一个启动时自动初始化的一个servlet来实现。

3.1 新增一个servlet实现类

com.lfqy.springboot.selfdefxml.servlet.StartupServlet

package com.lfqy.springboot.selfdefxml.servlet;

import com.lfqy.springboot.selfdefxml.beans.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;

/**
 * Created by chengxia on 2022/7/3.
 */
public class StartupServlet extends HttpServlet {
    private static final long serialVersionUID = 8823372937923792739L;
    public StartupServlet(){}
    public void init(ServletConfig sc) throws ServletException{
        super.init(sc);
        //load the self-deine beans
        ApplicationContext ac = new ClassPathXmlApplicationContext("SelfDefBeans.xml");
        User user = (User)ac.getBean("user");
        System.out.println(user.getName());
        System.out.println(user.getAddress());
    }
}

3.2 在启动类中添加servlet加载逻辑

在启动时加载servlet,为了方便区分,这里新写一个启动类。
com.lfqy.springboot.selfdefxml.SelfDefXmlLoadOnStartupApplication

package com.lfqy.springboot.selfdefxml;

import com.lfqy.springboot.selfdefxml.beans.User;
import com.lfqy.springboot.selfdefxml.servlet.StartupServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.support.ClassPathXmlApplicationContext;

@SpringBootApplication
@ServletComponentScan(basePackages = "com.lfqy.springboot.selfdefxml.servlet") //servlet扫描包路径
public class SelfDefXmlLoadOnStartupApplication {
    @Bean
    public ServletRegistrationBean getServletRunOnStartup(){
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StartupServlet());
        //设置Servlet在服务器启动时初始化(执行init方法),如果传入的参数是负数或者没有配置,那么这个Servlet会在第一次被请求时初始化
        servletRegistrationBean.setLoadOnStartup(1);
        //这里随便指定servlet关联度的url,如果不指定,那么默认所有的请求都会到这儿,会有各种奇奇怪怪的问题
        servletRegistrationBean.addUrlMappings("/runOnStartup");
        return servletRegistrationBean;
    }

    public static void main(String[] args) {
        SpringApplication.run(SelfDefXmlLoadOnStartupApplication.class);
        System.out.println("The application has been started ....");
    }
}

3.3 启动和运行

到这里,编码已经完成,工程的目录结构如下:


image.png

运行之后,控制台输出如下:


  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.0.2.RELEASE)

2022-07-04 00:13:45.346  INFO 3789 --- [           main] l.s.s.SelfDefXmlLoadOnStartupApplication : Starting SelfDefXmlLoadOnStartupApplication on ChengdeMBP with PID 3789 (/Users/chengxia/Developer/TrialProject/SpringBoot/SelfDefineXmlTrial/target/classes started by chengxia in /Users/chengxia/Developer/TrialProject)
2022-07-04 00:13:45.350  INFO 3789 --- [           main] l.s.s.SelfDefXmlLoadOnStartupApplication : No active profile set, falling back to default profiles: default
2022-07-04 00:13:45.399  INFO 3789 --- [           main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@18a70f16: startup date [Mon Jul 04 00:13:45 CST 2022]; root of context hierarchy
2022-07-04 00:13:47.116  INFO 3789 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2022-07-04 00:13:47.155  INFO 3789 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2022-07-04 00:13:47.156  INFO 3789 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.31
2022-07-04 00:13:47.165  INFO 3789 --- [ost-startStop-1] o.a.catalina.core.AprLifecycleListener   : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [/Users/chengxia/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.]
2022-07-04 00:13:47.244  INFO 3789 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2022-07-04 00:13:47.244  INFO 3789 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1848 ms
2022-07-04 00:13:47.390  INFO 3789 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Servlet startupServlet mapped to [/runOnStartup]
2022-07-04 00:13:47.391  INFO 3789 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Servlet dispatcherServlet mapped to [/]
2022-07-04 00:13:47.394  INFO 3789 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]
2022-07-04 00:13:47.395  INFO 3789 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2022-07-04 00:13:47.395  INFO 3789 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2022-07-04 00:13:47.395  INFO 3789 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]
2022-07-04 00:13:47.539  INFO 3789 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2022-07-04 00:13:47.765  INFO 3789 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@18a70f16: startup date [Mon Jul 04 00:13:45 CST 2022]; root of context hierarchy
2022-07-04 00:13:47.824  INFO 3789 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/selfdefxml/hello]}" onto public java.lang.String com.lfqy.springboot.selfdefxml.controller.SelfDefXmlController.hello()
2022-07-04 00:13:47.828  INFO 3789 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2022-07-04 00:13:47.830  INFO 3789 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2022-07-04 00:13:47.854  INFO 3789 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2022-07-04 00:13:47.854  INFO 3789 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2022-07-04 00:13:47.991  INFO 3789 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2022-07-04 00:13:48.086  INFO 3789 --- [           main] o.s.c.s.ClassPathXmlApplicationContext   : Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@790174f2: startup date [Mon Jul 04 00:13:48 CST 2022]; root of context hierarchy
2022-07-04 00:13:48.090  INFO 3789 --- [           main] o.s.b.f.xml.XmlBeanDefinitionReader      : Loading XML bean definitions from class path resource [SelfDefBeans.xml]
Paopao
Beijing
2022-07-04 00:13:48.190  INFO 3789 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2022-07-04 00:13:48.193  INFO 3789 --- [           main] l.s.s.SelfDefXmlLoadOnStartupApplication : Started SelfDefXmlLoadOnStartupApplication in 3.506 seconds (JVM running for 4.179)
The application has been started ....

参考资料

上一篇下一篇

猜你喜欢

热点阅读