当前位置: 代码网 > it编程>编程语言>Java > Springboot关于@Configuration的用法解读

Springboot关于@Configuration的用法解读

2026年04月02日 Java 我要评论
@configuration 全部用法现在大部分的项目都采用了基于注解的配置,采用了@configuration 替换 标签的做法。但是最近在翻看spring 官方文档时,发现@configurati

@configuration 全部用法

现在大部分的项目都采用了基于注解的配置,采用了@configuration 替换 标签的做法。但是最近在翻看spring 官方文档时,发现@configuration 声明为基础标签之外,还和大量的其他注解产生化学反应。

@configuration 基本说明

​ **定义:指示一个类声明一个或者多个@bean 声明的方法并且由spring容器统一管理,以便在运行时为这些bean生成bean的定义和服务请求的类。**例如:

@configuration
public class appconfig {
  
  @bean
  public mybean mybean(){
    return new mybean();
  }
}

上述appconfig 加入@configuration 注解,表明这就是一个配置类。

有一个mybean()的方法,返回一个mybean()的实例,并用@bean 进行注释,表明这个方法是需要被spring进行管理的bean。

@bean 如果不指定名称的话,默认使用mybean名称,也就是小写的名称。

通过注解启动

通过启动一个annotationconfigapplicationcontext 来引导这个@configuration 注解的类,比如:

annotationconfigapplicationcontext ctx = new annotationconfigapplicationcontext();
ctx.register(appconfig.class);
ctx.refresh();

在web项目中,也可以使用annotationcontextwebapplicationcontext 或者其他变体来启动。

新建一个springboot项目(别问我为什么,因为这样创建项目比较快)。

  • 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>
    <parent>
        <groupid>org.springframework.boot</groupid>
        <artifactid>spring-boot-starter-parent</artifactid>
        <version>2.1.5.release</version>
        <relativepath/> <!-- lookup parent from repository -->
    </parent>
    <groupid>com.spring.configuration</groupid>
    <artifactid>spring-configuration</artifactid>
    <version>0.0.1-snapshot</version>
    <name>spring-configuration</name>
    <description>demo project for spring boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-test</artifactid>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupid>org.springframework</groupid>
            <artifactid>spring-context</artifactid>
            <version>5.0.6.release</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupid>org.springframework.boot</groupid>
                <artifactid>spring-boot-maven-plugin</artifactid>
            </plugin>
        </plugins>
    </build>
</project>
  • 在config 包下新建一个myconfiguration环境配置,和上面的示例代码相似,完整的代码如下:
@configuration
public class myconfiguration {

    @bean
    public mybean mybean(){
        system.out.println("mybean initialized");
        return new mybean();
    }
}

说明myconfiguration 是一个配置类,能够在此类下面声明管理多个bean,我们声明了一个mybean 的bean,希望它被容器加载和管理。

  • 在pojo包下新建一个mybean的类,具体代码如下
public class mybean {

    public mybean(){
        system.out.println("generate mybean instance");
    }

    public void init(){
        system.out.println("mybean resources initialized");
    }
}

  • 新建一个springconfigurationapplication类,用来测试myconfiguration类,具体代码如下:
public class springconfigurationapplication {

    public static void main(string[] args) {
        
//        annotationconfigapplicationcontext context = = new annotationconfigapplicationcontext(myconfiguration.class)
      	// 因为我们加载的@configuration 是基于注解形式的,所以需要创建annotationconfigapplicationcontext
        annotationconfigapplicationcontext context = new annotationconfigapplicationcontext();
        // 注册myconfiguration 类并刷新bean 容器。
        context.register(myconfiguration.class);
        context.refresh();

    }

}

输出:

mybean initialized
generate mybean instance

从输出的结果可以看到,默认名称为mybean 的bean随着容器的加载而加载,因为mybean方法返回一个mybean的构造方法,所以mybean被初始化了。

通过xml 的方式来启动

  • 可以通过使用xml方式定义的<context:annotation-config />开启基于注解的启动,然后再定义一个myconfiguration的bean,在/resources 目录下新建 application-context.xml 代码如下:
<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xmlns:context="http://www.springframework.org/schema/context"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemalocation="
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
      http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd
      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd"
>
    <!-- 相当于基于注解的启动类 annotationconfigapplicationcontext-->
    <context:annotation-config />
    <bean class="com.spring.configuration.config.myconfiguration"/>
</beans>
  • 需要引入applicationcontext.xml ,在springconfigurationapplication 需要进行引入,修改后的springconfigurationapplication如下:
public class springconfigurationapplication {

    public static void main(string[] args) {

        applicationcontext context = 
          new classpathxmlapplicationcontext("applicationcontext.xml");
    }
}

输出:

mybean initialized
generate mybean instance

基于componentscan() 来获取bean的定义

@configuration 使用@component 进行原注解,因此@configuration 类也可以被组件扫描到(特别是使用xml context:component-scan 元素)。

在这里认识几个注解: @controller, @service, @repository, @component

  • @controller: 表明一个注解的类是一个"controller",也就是控制器,可以把它理解为mvc 模式的controller 这个角色。这个注解是一个特殊的@component,允许实现类通过类路径的扫描扫描到。它通常与@requestmapping 注解一起使用。
  • @service: 表明这个带注解的类是一个"service",也就是服务层,可以把它理解为mvc 模式中的service层这个角色,这个注解也是一个特殊的@component,允许实现类通过类路径的扫描扫描到
  • @repository: 表明这个注解的类是一个"repository",团队实现了javaee 模式中像是作为"data access object" 可能作为dao来使用,当与 persistenceexceptiontranslationpostprocessor 结合使用时,这样注释的类有资格获得spring转换的目的。这个注解也是@component 的一个特殊实现,允许实现类能够被自动扫描到
  • @component: 表明这个注释的类是一个组件,当使用基于注释的配置和类路径扫描时,这些类被视为自动检测的候选者。

也就是说,上面四个注解标记的类都能够通过@componentscan 扫描到,上面四个注解最大的区别就是使用的场景和语义不一样,比如你定义一个service类想要被spring进行管理,你应该把它定义为@service 而不是@controller因为我们从语义上讲,@service更像是一个服务的类,而不是一个控制器的类,@component通常被称作组件,它可以标注任何你没有严格予以说明的类,比如说是一个配置类,它不属于mvc模式的任何一层,这个时候你更习惯于把它定义为 @component。@controller,@service,@repository 的注解上都有@component,所以这三个注解都可以用@component进行替换。

来看一下代码进行理解:

  • 定义五个类,类上分别用@controller, @service, @repository, @component, @configuration 进行标注,分别如下
@component
public class userbean {}

@configuration
public class userconfiguration {}

@controller
public class usercontroller {}

@repository
public class userdao {}

@service
public class userservice {}
  • myconfiguration上加上@componentscan 注解,扫描上面5个类所在的包位置。代码如下:
@configuration
@componentscan(basepackages = "com.spring.configuration.pojo")
public class myconfiguration {

    @bean
    public mybean mybean(){
        system.out.println("mybean initialized");
        return new mybean();
    }
}
  • 修改 springconfigurationapplication 中的代码,如下:
public class springconfigurationapplication {

    public static void main(string[] args) {

//        annotationconfigapplicationcontext context = = new annotationconfigapplicationcontext(myconfiguration.class)
//        applicationcontext context = new classpathxmlapplicationcontext("applicationcontext.xml");

        annotationconfigapplicationcontext context = new annotationconfigapplicationcontext();
        context.register(myconfiguration.class);
        context.refresh();

        // 获取启动过程中的bean 定义的名称
        for(string str : context.getbeandefinitionnames()){
            system.out.println("str = " + str);
        }
        context.close();

    }
}

输出:

mybean initialized
generate mybean instance
str = org.springframework.context.annotation.internalconfigurationannotationprocessor
str = org.springframework.context.annotation.internalautowiredannotationprocessor
str = org.springframework.context.annotation.internalrequiredannotationprocessor
str = org.springframework.context.annotation.internalcommonannotationprocessor
str = org.springframework.context.event.internaleventlistenerprocessor
str = org.springframework.context.event.internaleventlistenerfactory
str = myconfiguration
str = userbean
str = userconfiguration
str = usercontroller
str = userdao
str = userservice
str = mybean

由输出可以清楚的看到,上述定义的五个类成功被@componentscan 扫描到,并在程序启动的时候进行加载。

@configuration 和 environment

​ @configuration 通常和environment 一起使用,通过@environment 解析的属性驻留在一个或多个"属性源"对象中,@configuration类可以使用@propertysource,像environment 对象提供属性源

  • 为了便于测试,我们引入junit4和spring-test 的依赖,完整的配置文件如下
<?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>
    <parent>
        <groupid>org.springframework.boot</groupid>
        <artifactid>spring-boot-starter-parent</artifactid>
        <version>2.1.5.release</version>
        <relativepath/> <!-- lookup parent from repository -->
    </parent>
    <groupid>com.spring.configuration</groupid>
    <artifactid>spring-configuration</artifactid>
    <version>0.0.1-snapshot</version>
    <name>spring-configuration</name>
    <description>demo project for spring boot</description>
    <properties>
        <java.version>1.8</java.version>
        <spring.version>5.0.6.release</spring.version>
        <spring.test.version>4.3.13.release</spring.test.version>
        <junit.version>4.12</junit.version>
    </properties>
    <dependencies>
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-test</artifactid>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupid>org.springframework</groupid>
            <artifactid>spring-context</artifactid>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupid>org.springframework</groupid>
            <artifactid>spring-test</artifactid>
            <version>${spring.test.version}</version>
        </dependency>
        <dependency>
            <groupid>junit</groupid>
            <artifactid>junit</artifactid>
            <version>${junit.version}</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupid>org.springframework.boot</groupid>
                <artifactid>spring-boot-maven-plugin</artifactid>
            </plugin>
        </plugins>
    </build>
</project>
  • 在config 包下定义一个 environmentconfig 类,注入environment 属性,完整代码如下:
@runwith(springjunit4classrunner.class)
@contextconfiguration(classes = environmentconfig.class)
@configuration
@propertysource("classpath:beanname.properties")
public class environmentconfig {

    @autowired
    environment env;

    @test
    public void testreadproperty(){
        // 获取bean.name.controller 的属性
        system.out.println(env.getproperty("bean.name.controller"));
        // 判断是否包含bean.name.component
        system.out.println(env.containsproperty("bean.name.component"));
        // 返回与给定键关联的属性值
        system.out.println(env.getrequiredproperty("bean.name.service"));
    }
}
  • 在/resources 目录下新建beanname.properties 文件,如下:
bean.name.configuration=beannameconfiguration
bean.name.controller=beannamecontroller
bean.name.service=beannameservice
bean.name.component=beannamecomponent
bean.name.repository=beannamerepository

启动并进行junit测试,输出如下:

…………

beannamecontroller

true

beannameservice

…………

@autowired 、 @inject、@resource 的区别

@inject: 这是jsr330 的规范,通过autowiredannotationbeanpostprocessor 类实现的依赖注入。位于javax.inject包内,是java自带的注解。

@inject
@named("environment")
environment env;

不加@named注解,需要配置与变量名一致即可。

@autowired: @autowired 是spring提供的注解,通过autowiredannotationbeanpostproessor 类实现注入。位于org.springframework.beans.factory.annotation 包内,是spring 中的注解

@autowired
environment env;

默认是通过bytype 实现注入

@resource: @resource 是jsr250规范的实现,@resource通过commonannotationbeanpostprocessor 类实现注入。@resource 一般会指定一个name属性,如下:

@resource(name = "environment")
environment env;

默认是通过byname 实现注入

区别:

@autowired和@inject基本是一样的,因为两者都是使用autowiredannotationbeanpostprocessor来处理依赖注入。但是@resource是个例外,它使用的是commonannotationbeanpostprocessor来处理依赖注入。当然,两者都是beanpostprocessor。

在介绍完上述三者的区别之后,可以对environment的属性以上述注入方式进行改造

@value、@propertysource 和 @configuration

@configuration 可以和@value 和@propertysource 一起使用读取外部配置文件,具体用法如下:

  • 在config 包下新建一个readvaluefrompropertysource类,代码如下
@propertysource("classpath:beanname.properties")
@configuration
public class readvaluefrompropertysource {

    @value("bean.name.component")
    string beanname;

    @bean("mytestbean")
    public mybean mybean(){
        return new mybean(beanname);
    }

}

通过@propertysource引入的配置文件,使@value 能够获取到属性值,在给mybean()方法指定了一个名称叫做mytestbean。

  • 修改mybean类,增加一个name属性和一个构造器,再生成其tostring() 方法
public class mybean {

    string name;

    public mybean(string name) {
        this.name = name;
    }

    public mybean(){
        system.out.println("generate mybean instance");
    }

    public void init(){
        system.out.println("mybean resources initialized");
    }

    @override
    public string tostring() {
        return "mybean{" +
                "name='" + name + '\'' +
                '}';
    }
}
  • 在springconfigurationapplication中进行测试,如下
public class springconfigurationapplication {

    public static void main(string[] args) {

      // 为了展示配置文件的完整性,之前的代码没有删除。
//        annotationconfigapplicationcontext context = = new annotationconfigapplicationcontext(myconfiguration.class)
//        applicationcontext context = new classpathxmlapplicationcontext("applicationcontext.xml");

//        annotationconfigapplicationcontext context = new annotationconfigapplicationcontext();
//        context.register(myconfiguration.class);
//        context.refresh();
//
//        // 获取启动过程中的bean 定义的名称
//        for(string str : context.getbeandefinitionnames()){
//            system.out.println("str = " + str);
//        }
//        context.close();

        applicationcontext context =
                new annotationconfigapplicationcontext(readvaluefrompropertysource.class);
        mybean mybean = (mybean) context.getbean("mytestbean");
        system.out.println("mybean = " + mybean);

    }
}

使用applicatio@inconntext 就能够获取mytestbean 这个bean,再生成mybean的实例。

输出:mybean = mybean{name=‘bean.name.component’}

@import 和 @configuration

@import的定义(来自于javadoc):表明一个或者多个配置类需要导入,提供与spring xml中相等的功能,允许导入@configuration 、@importselector、@importbeandefinitionregistar的实现,以及常规组件类似于annotationconfigapplicationcontext。可能用于类级别或者是原注解。如果xml或者其他非@configuration标记的bean资源需要被导入的话,使用@importresource。下面是一个示例代码:

  • 在pojo 包下新建两个配置类,分别是customerbo, schedualbo
@configuration
public class customerbo {

    public void printmsg(string msg){
        system.out.println("customerbo : " + msg);
    }

    @bean
    public customerbo testcustomerbo(){
        return new customerbo();
    }
}

@configuration
public class schedulerbo {

    public void printmsg(string msg){
        system.out.println("schedulerbo : " + msg);
    }

    @bean
    public schedulerbo testschedulerbo(){
        return new schedulerbo();
    }
}
  • 在config 包下新建一个appconfig,导入customerbo 和 schedulerbo 。
@configuration
@import(value = {customerbo.class,schedulerbo.class})
public class appconfig {}
  • 在config 包下新建一个importwithconfiguration ,用于测试@import 和 @configuration 的使用
public class importwithconfiguration {

    public static void main(string[] args) {
        applicationcontext context = new annotationconfigapplicationcontext(appconfig.class);
        customerbo customerbo = (customerbo) context.getbean("testcustomerbo");
        customerbo.printmsg("system out println('get from customerbo')");

        schedulerbo schedulerbo = (schedulerbo) context.getbean("testschedulerbo");
        schedulerbo.printmsg("system out println('get from schedulerbo')");
    }
}

输出:

customerbo : system out println(‘get from customerbo’)
schedulerbo : system out println(‘get from schedulerbo’)

@profile

@profile: 表示当一个或多个@value 指定的配置文件处于可用状态时,组件符合注册条件,可以进行注册。

三种设置方式:

  • 可以通过configurableenvironment.setactiveprofiles()以编程的方式激活
  • 可以通过abstractenvironment.active_profiles_property_name (spring.profiles.active )属性设置为
  • jvm属性
  • 作为环境变量,或作为web.xml 应用程序的servlet 上下文参数。也可以通过@activeprofiles 注解在集成测试中以声明方式激活配置文件。

作用域

  • 作为类级别的注释在任意类或者直接与@component 进行关联,包括@configuration 类
  • 作为原注解,可以自定义注解
  • 作为方法的注解作用在任何方法

注意

​ 如果一个配置类使用了profile 标签或者@profile 作用在任何类中都必须进行启用才会生效,如果@profile({“p1”,“!p2”}) 标识两个属性,那么p1 是启用状态 而p2 是非启用状态的。

@importresource 和 @configuration

@importresource: 这个注解提供了与@import 功能相似作用,通常与@configuration 一起使用,通过annotationconfigapplicationcontext 进行启动,下面以一个示例来看一下具体用法:

  • 在config下新建testservice 类,声明一个构造函数,类初始化时调用
public class testservice {

    public testservice(){
        system.out.println("test @importresource success");
    }
}
  • 在/resources 目录下新建 importresources.xml ,为了导入testservice
<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xmlns:context="http://www.springframework.org/schema/context"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemalocation="
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
      http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd
      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd"
>
    <bean id = "testservice" class="com.spring.configuration.config.testservice" />
</beans>
  • 然后在config 下新建一个importresourcewithconfiguration, 用于读取配置文件
@configuration
@importresource("classpath:importresources.xml")
public class importresourcewithconfiguration {

    @autowired
    private testservice service;

    public void getimportresource(){
        new testservice();
    }

    public static void main(string[] args) {
        annotationconfigapplicationcontext context =
                new annotationconfigapplicationcontext(importresourcewithconfiguration.class);
        context.getbean("testservice");

    }
}

输出:test @importresource success

@configuration 嵌套

@configuration注解作用在类上,就和普通类一样能够进行相互嵌套,定义内部类。

// 来自javadoc
@configuration
public class appconfig{
  
  @inject
  datasource datasource;
  
  @bean
  public mybean mybean(){
    return new mybean(datasource);
  }
  
  @configuration
  static class dataconfig(){
    @bean
    datasource datasource(){
      return new embeddeddatabasebuilder().build()
    }
  }
}

在上述代码中,只需要在应用程序的上下文中注册 appconfig 。由于是嵌套的@configuration 类,databaseconfig 将自动注册。当appconfig 、databaseconfig 之间的关系已经隐含清楚时,这就避免了使用@import 注解的需要。

@lazy 延迟初始化

@lazy : 表明一个bean 是否延迟加载,可以作用在方法上,表示这个方法被延迟加载;可以作用在@component (或者由@component 作为原注解) 注释的类上,表明这个类中所有的bean 都被延迟加载。如果没有@lazy注释,或者@lazy 被设置为false,那么该bean 就会急切渴望被加载;除了上面两种作用域,@lazy 还可以作用在@autowired和@inject注释的属性上,在这种情况下,它将为该字段创建一个惰性代理,作为使用objectfactory或provider的默认方法。下面来演示一下:

  • 修改myconfiguration类,在该类上添加@lazy 注解,新增一个iflazyinit()方法,检验是否被初始化。
@lazy
@configuration
@componentscan(basepackages = "com.spring.configuration.pojo")
public class myconfiguration {

    @bean
    public mybean mybean(){
        system.out.println("mybean initialized");
        return new mybean();
    }

    @bean
    public mybean iflazyinit(){
        system.out.println("initialized");
        return new mybean();
    }
}
  • 修改springconfigurationapplication 启动类,放开之前myconfiguration 的启动类
public class springconfigurationapplication {

    public static void main(string[] args) {

        annotationconfigapplicationcontext context = new annotationconfigapplicationcontext(myconfiguration.class);
//        applicationcontext context = new classpathxmlapplicationcontext("applicationcontext.xml");

//        annotationconfigapplicationcontext context = new annotationconfigapplicationcontext();
//        context.register(myconfiguration.class);
//        context.refresh();
//
//        // 获取启动过程中的bean 定义的名称
        for(string str : context.getbeandefinitionnames()){
            system.out.println("str = " + str);
        }
//        context.close();

//        applicationcontext context =
//                new annotationconfigapplicationcontext(readvaluefrompropertysource.class);
//        mybean mybean = (mybean) context.getbean("mytestbean");
//        system.out.println("mybean = " + mybean);

    }
}

输出你会发现没有关于bean的定义信息,但是当吧@lazy 注释拿掉,你会发现输出了关于bean的初始化信息:

mybean initialized
generate mybean instance
initialized
generate mybean instance

@runwith 和 @contextconfiguration

junit4 测试类,用于注解在类上表示通过junit4 进行测试,可以省略编写启动类代码,是applicationcontext 等启动类的替换。一般用@runwith 和 @configuration 进行单元测试,这是软件开发过程中非常必要而且具有专业性的一部分,上面environmentconfig 类证实了这一点:

@runwith(springjunit4classrunner.class)
@contextconfiguration(classes = environmentconfig.class)
@configuration
@propertysource("classpath:beanname.properties")
public class environmentconfig {

//    @autowired
//    environment env;

    @inject
    environment env;

    @test
    public void testreadproperty(){
        // 获取bean.name.controller 的属性
        system.out.println(env.getproperty("bean.name.controller"));
        // 判断是否包含bean.name.component
        system.out.println(env.containsproperty("bean.name.component"));
        // 返回与给定键关联的属性值
        system.out.println(env.getrequiredproperty("bean.name.service"));
    }
}

@enable 启动spring内置功能

详情查阅@enableasync,@enablescheduling,@enabletransactionmanagement,@enableaspectjautoproxy,@enablewebmvc官方文档

@configuration 使用约束

  • 必须以类的方式提供(即不是从工厂方法返回的实例)
  • @configuration 注解的类必须是非final的
  • 配置类必须是非本地的(即可能不在方法中声明),native 标注的方法
  • 任何嵌套的@configuration 都必须是static 的。
  • @bean 方法可能不会反过来创建更多配置类

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com