spring-boot 之自动装配机制的原理分析

文章目录

1.入口类@SpringBootApplication

1.1.概述

Spring Boot的项目一般都会有*Application的入口类,入口类中会有main方法,这是一个标准的Java应用程序的入口方法。
@SpringBootApplication注解是Spring Boot的核心注解,它其实是一个组合注解:

在这里插入图片描述

1.2. @SpringBootApplication 该注解主要组合了以下注解:

1.2.1.@SpringBootConfiguration

1.2.1.1.概述
1.	@SpringBootConfiguration:这是Spring Boot项目的配置注解,这也是一个组合注解:
2.	在Spring Boot项目中推荐使用@SpringBootConfiguration替代@Configuration
    所谓的替代,我们可以简单地理解,SpringBoot里面@SpringBootConfiguration就是@Configuration换了一个名字而已

在这里插入图片描述

1.2.1.2.@Configuration(通过案例介绍@Configuration)

源代码
https://gitee.com/gaoxinfu_admin/demo-spring-boot/tree/master/demo-spring-boot-example-04/src/main/java/com/gaoxinfu/demo/springboot/componentscan

1.2.1.2.1.概述
1.Configuration 这个注解大家应该有用过,它是 JavaConfig形式的基于Spring IOC容器的配置类使用的一种注解。
  因为SpringBoot 本质上就是一个spring 应用,所以通过这个注解来加载 IOC 容器的配置是很正常的。所以在启动类
  里面标注了@Configuration,意味着它其实也是一个IoC容器的配置类。
2.传统意义上的 spring 应用都是基于 xml 形式来配置 bean的依赖关系。然后通过 spring 容器在启动的时候,把bean
  进行初始化并且,如果 bean 之间存在依赖关系,则分析这些已经在 IoC 容器中的 bean 根据依赖关系进行组装。
3.直到 Java5 中,引入了 Annotations 这个特性,Spring 框架也紧随大流并且推出了基于Java代码和 Annotation元
  信息的依赖关系绑定描述的方式。也就是JavaConfig。 
  从spring3 开始,spring 就支持了两种 bean 的配置方式,一种是基于 xml 文件方式、另一种就是 JavaConfig
1.2.1.2.2.案例演示
1.2.1.2.2.1.定义一个Bean类:ConfigurationBean
package com.gaoxinfu.demo.springboot.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

/**
 * @Description:
 * @Author: gaoxinfu
 * @Date: 2020-04-18 16:56
 */
public class ConfigurationBean {

    public void say(){
        System.out.println("say() = Hello");
    }
}

1.2.1.2.2.2.定义一个Configuration类:ConfigurationDemo,扫描Bean
package com.gaoxinfu.demo.springboot.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Description:
 * @Author: gaoxinfu
 * @Date: 2020-04-18 16:55
 */
@Configuration
public class ConfigurationDemo {

    @Bean
    public ConfigurationBean configurationBean(){
        return new ConfigurationBean();
    }
}
1.2.1.2.2.3.定义一个测试类:ConfigurationMain
package com.gaoxinfu.demo.springboot.configuration;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;

/**
 * @Description:
 * @Author: gaoxinfu
 * @Date: 2020-04-18 16:56
 */
public class ConfigurationMain {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext AnnotationConfigApplicationContext=new AnnotationConfigApplicationContext(ConfigurationDemo.class);
        ConfigurationBean configurationBean= AnnotationConfigApplicationContext.getBean(ConfigurationBean.class);
        if (configurationBean!=null){
            configurationBean.say();
        }else{
            System.out.println("configurationBean = "+configurationBean);
        }

    }
}

在这里插入图片描述

1.2.1.3.@Configuration本质上是@Component

在这里插入图片描述

1.2.2.@EnableAutoConfiguration

1.2.2.1.概述
1.	@EnableAutoConfiguration:启用自动配置,
2.	该注解会使Spring Boot根据项目中依赖的jar包自动配置项目的配置项:

例如:

我们添加了spring-boot-starter-web的依赖,项目中也就会引入SpringMVC的依赖,
Spring Boot就会自动配置tomcat和SpringMVC

在这里插入图片描述

EnableAutoConfiguration该注解包含两块 @AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})

在这里插入图片描述

1.2.2.2.Import

源代码
https://gitee.com/gaoxinfu_admin/demo-spring-boot/tree/master/demo-spring-boot-example-04/src/main/java/com/gaoxinfu/demo/springboot/enableautoconfiguration/importdemo

1.2.2.2.1.前期准备

在这里插入图片描述

1.2.2.2.1.1.First文件夹

FirstBean.java

package com.gaoxinfu.demo.springboot.enableautoconfiguration.importdemo.first;

/**
 * @Description:
 * @Author: gaoxinfu
 * @Date: 2020-04-18 20:40
 */
public class FirstBean {
}
package com.gaoxinfu.demo.springboot.enableautoconfiguration.importdemo.first;

import com.gaoxinfu.demo.springboot.enableautoconfiguration.importdemo.second.SecondBean;
import com.gaoxinfu.demo.springboot.enableautoconfiguration.importdemo.second.SecondConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;


**FirstConfig.java**
/**
 * @Description:
 * @Author: gaoxinfu
 * @Date: 2020-04-18 20:40
 */
@Configuration
public class FirstConfig {

    @Bean
    public FirstBean firstBean(){
        return new FirstBean();
    }
}

FirstMain.java

package com.gaoxinfu.demo.springboot.enableautoconfiguration.importdemo.first;

import com.gaoxinfu.demo.springboot.configuration.ConfigurationBean;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @Description:
 * @Author: gaoxinfu
 * @Date: 2020-04-18 20:42
 */
public class FirstMain {
    public static void main(String[] args) {

        AnnotationConfigApplicationContext AnnotationConfigApplicationContext=new AnnotationConfigApplicationContext(FirstConfig.class);
        String[] beanDefinitionNames=AnnotationConfigApplicationContext.getBeanDefinitionNames();
        for (String beanDefinitionName:beanDefinitionNames){
            System.out.println("BeanDefinitionName = "+beanDefinitionName);
        }
    }
}
1.2.2.2.1.2.Second文件夹
package com.gaoxinfu.demo.springboot.enableautoconfiguration.importdemo.second;

/**
 * @Description:
 * @Author: gaoxinfu
 * @Date: 2020-04-18 20:42
 */
public class SecondBean {


}

package com.gaoxinfu.demo.springboot.enableautoconfiguration.importdemo.second;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Description:
 * @Author: gaoxinfu
 * @Date: 2020-04-18 20:42
 */
@Configuration
public class SecondConfig {

    @Bean
    public SecondBean secondBean(){
        return  new SecondBean();
    }
}
1.2.2.2.2.案例一:不使用import,只能引用当前包下的Bean

直接运行上面的FirstMain.main方法,只会出现first文件夹下的,second下面的bean不会加载进来
在这里插入图片描述

1.2.2.2.2.案例二:使用import,引用别的包下面的bean

FirstConfig.java

package com.gaoxinfu.demo.springboot.enableautoconfiguration.importdemo.first;

import com.gaoxinfu.demo.springboot.enableautoconfiguration.importdemo.second.SecondBean;
import com.gaoxinfu.demo.springboot.enableautoconfiguration.importdemo.second.SecondConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

/**
 * @Description:
 * @Author: gaoxinfu
 * @Date: 2020-04-18 20:40
 */
@Configuration
@Import(SecondConfig.class)
public class FirstConfig {

    @Bean
    public FirstBean firstBean(){
        return new FirstBean();
    }
}

在这里插入图片描述

再次运行FirstMain.main()方法,second文件夹bean引入
在这里插入图片描述

1.2.2.3.ImportSelector

源代码
https://gitee.com/gaoxinfu_admin/demo-spring-boot/tree/master/demo-spring-boot-example-04/src/main/java/com/gaoxinfu/demo/springboot/enableautoconfiguration/importselectordemo

1.2.2.3.1.UserLoginService和UserBusinessService:创建两个Bean
package com.gaoxinfu.demo.springboot.enableautoconfiguration.importselectordemo;

/**
 * @Description:
 * @Author: gaoxinfu
 * @Date: 2020-04-18 22:05
 */
public class UserLoginService {
}

package com.gaoxinfu.demo.springboot.enableautoconfiguration.importselectordemo;

/**
 * @Description:
 * @Author: gaoxinfu
 * @Date: 2020-04-18 22:06
 */
public class UserBusinessService {
}

1.2.2.3.2.EnableUserDefineService【自定义一个注解】
package com.gaoxinfu.demo.springboot.enableautoconfiguration.importselectordemo;

import org.springframework.context.annotation.Import;

import java.lang.annotation.*;

/**
 * @Description: 启动用户定义的Service
 * @Author: gaoxinfu
 * @Date: 2020-04-18 22:07
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({UserImportSelector.class})
public @interface EnableUserDefineService {

    Class<?>[] exclude() default {};

}
1.2.2.3.3.UserImportSelector
package com.gaoxinfu.demo.springboot.enableautoconfiguration.importselectordemo;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import java.util.Map;

/**
 * @Description:
 * @Author: gaoxinfu
 * @Date: 2020-04-18 22:06
 */
public class UserImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        Map<String,Object> attributes=
                annotationMetadata.getAnnotationAttributes(EnableUserDefineService.class.getName());
        //动态注入bean :自己去实现判断逻辑实现动态配置
        Class<?>[] excludeCLass= (Class<?>[]) attributes.get("exclude");

        //TODO 这里可以做判断,拉出UserBusinessService和UserLoginService等所有的(匹配到)
        //根据exclude 然后删除UserLoginService

        return new String[]{UserBusinessService.class.getName()}; //返回的是一个固定的UserBusinessService
    }
}

1.2.2.3.5.UserMain
package com.gaoxinfu.demo.springboot.enableautoconfiguration.importselectordemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

/**
 * @Description:
 * @Author: gaoxinfu
 * @Date: 2020-04-18 22:11
 */
@SpringBootApplication
@EnableUserDefineService(exclude = {UserLoginService.class})
public class UserMain {

    public static void main(String[] args) {
        ConfigurableApplicationContext configurableApplicationContext
                =SpringApplication.run(UserMain.class,args);

        System.out.println(configurableApplicationContext.getBean(UserBusinessService.class));
    }
}

1.2.2.4.ImportBeanDefinitionRegistrar
1.2.2.4.1.UserLogginDefinitionRegistrar:定义注册Registrar,实现ImportBeanDefinitionRegistrar
package com.gaoxinfu.demo.springboot.enableautoconfiguration.ImportBeanDefinitionRegistrarDemo;

import com.gaoxinfu.demo.springboot.enableautoconfiguration.importselectordemo.UserLoginService;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.StringUtils;

/**
 * @Description:
 * @Author: gaoxinfu
 * @Date: 2020-04-19 11:16
 */
public class UserLogginDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        Class beanClass= UserLoginService.class;
        RootBeanDefinition beanDefinition=new RootBeanDefinition(beanClass);
        String beanName= StringUtils.uncapitalize(beanClass.getSimpleName());
        registry.registerBeanDefinition(beanName,beanDefinition);
    }
}
1.2.2.4.2.UserMain:导入UserLogginDefinitionRegistrar,进行测试,看是否注入UserLoginService的bean
package com.gaoxinfu.demo.springboot.enableautoconfiguration.ImportBeanDefinitionRegistrarDemo;

import com.gaoxinfu.demo.springboot.enableautoconfiguration.importselectordemo.EnableUserDefineService;
import com.gaoxinfu.demo.springboot.enableautoconfiguration.importselectordemo.UserBusinessService;
import com.gaoxinfu.demo.springboot.enableautoconfiguration.importselectordemo.UserLoginService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;

/**
 * @Description:
 * @Author: gaoxinfu
 * @Date: 2020-04-18 22:11
 */
@SpringBootApplication
@Import(UserLogginDefinitionRegistrar.class)
public class UserMain {

    public static void main(String[] args) {
        ConfigurableApplicationContext configurableApplicationContext
                =SpringApplication.run(UserMain.class,args);
        System.out.println(configurableApplicationContext.getBean(UserLoginService.class));
    }
}

在这里插入图片描述

1.2.2.5.SPI:回到@EnableAutoConfiguration的AutoConfigurationImportSelector
1.2.2.5.1.源码

在这里插入图片描述

1.2.2.5.1.1.AutoConfigurationImportSelector.selectImports()方法

在这里插入图片描述

1.2.2.5.1.2.AutoConfigurationMetadataLoader.loadMetadata()加载spring-autoconfigure-metadata.properties

在这里插入图片描述
在这里插入图片描述

**对应类所在的包下面的 spring-autoconfigure-metadata.properties文件 主要是一些spring中的大部分的Configuration,先扫到,后面用那个再说
**
在这里插入图片描述

1.2.2.5.1.3.加载上面Configuration中的Bean(最终在 META-INF/spring.factories)

在这里插入图片描述

   protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.filter(configurations, autoConfigurationMetadata);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }

在这里插入图片描述

 protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

在这里插入图片描述
在这里插入图片描述

META-INF/spring.factories样例
在这里插入图片描述

1.2.2.5.2.案例:自定义META-INF/spring.factories

这里注意下,上面的key是可以进行扩展的,在别的地方定义的同样的key,其value也会被增加扩展加载进来

1.2.2.5.2.1.创建新项目demo-spring-boot-example-05

源码地址
https://gitee.com/gaoxinfu_admin/demo-spring-boot/tree/master/demo-spring-boot-example-05

1.2.2.5.2.2.创建MyBean和MyConfig类
package com.gaoxinfu.demo.spring.boot.example05.bean;

/**
 * @Description:
 * @Author: gaoxinfu
 * @Date: 2020-04-19 14:19
 */
public class MyBean {
}

package com.gaoxinfu.demo.spring.boot.example05.bean;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Description:
 * @Author: gaoxinfu
 * @Date: 2020-04-19 14:19
 */
@Configuration
public class MyConfig {

    @Bean
    public MyBean myBean(){
        return new MyBean();
    }
}
1.2.2.5.2.3.创建spring.fatories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.gaoxinfu.demo.spring.boot.example05.bean.MyConfig

在这里插入图片描述

1.2.2.5.2.4.demo-spring-boot-example-04项目中引入demo-spring-boot-example-05

在这里插入图片描述

1.2.2.5.2.5.demo-spring-boot-example-04项目创建SpiMain进行测试
package com.gaoxinfu.demo.springboot.enableautoconfiguration.spi;

import com.gaoxinfu.demo.spring.boot.example05.bean.MyBean;
import com.gaoxinfu.demo.spring.boot.example05.bean.MyConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

/**
 * @Description:
 * @Author: gaoxinfu
 * @Date: 2020-04-19 14:24
 */
@SpringBootApplication
public class SpiMain {

    public static void main(String[] args) {
        ConfigurableApplicationContext ConfigurableApplicationContext
                =SpringApplication.run(SpiMain.class,args);
        System.out.println(ConfigurableApplicationContext.getBean(MyBean.class));
    }
}

在这里插入图片描述

1.2.2.5.2.6.总结
1.可以看得出来,demo-spring-boot-example-05中的spring.factories中我们配置的MyBean这个类已经被加载IOC容器中了
1.2.2.6.Spring条件注入
1.2.2.6.1.demo-spring-boot-example-05项目中新增spring-autoconfigure-metadata.properties

在这里插入图片描述

spring-autoconfigure-metadata.properties配置内容
即当com.gaoxinfu.demo.springboot.enableautoconfiguration.spi.MyTest这个类存在的时候,
才会加载MyConfig中的相关bean到IOC中

ConditionalOnClass

com.gaoxinfu.demo.spring.boot.example05.bean.MyConfig.ConditionalOnClass=com.gaoxinfu.demo.springboot.enableautoconfiguration.spi.MyTest
1.2.2.6.2.demo-spring-boot-example-05项目版本升级0.0.2

在这里插入图片描述

1.2.2.6.3.demo-spring-boot-example-04重新引用demo-spring-boot-example-05版本的0.0.2版本

在这里插入图片描述

1.2.2.6.4.demo-spring-boot-example-04 类SpiMain开始测试
package com.gaoxinfu.demo.springboot.enableautoconfiguration.spi;

import com.gaoxinfu.demo.spring.boot.example05.bean.MyConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

/**
 * @Description:
 * @Author: gaoxinfu
 * @Date: 2020-04-19 14:24
 */
@SpringBootApplication
public class SpiMain {

    public static void main(String[] args) {
        ConfigurableApplicationContext ConfigurableApplicationContext
                =SpringApplication.run(SpiMain.class,args);
        System.out.println(ConfigurableApplicationContext.getBean(MyConfig.class));
    }
}

在这里插入图片描述
因为spi文件夹下面没有MyTest类,导致MyConfig将Bean注入的IOC容器中在这里插入图片描述

1.2.2.6.5.demo-spring-boot-example-04 创建MyTest类,再次进行测试
package com.gaoxinfu.demo.springboot.enableautoconfiguration.spi;

/**
 * @Description:
 * @Author: gaoxinfu
 * @Date: 2020-04-19 16:48
 */
public class MyTest {
}

再次运行SpiMain中的Main方法,成功在这里插入图片描述

1.2.3.@ComponentScan

@ComponentScan:默认扫描@SpringBootApplication所在类的同级目录以及它的子目录。
1.2.3.2.案例
1.2.3.2.1.ComponentScanBean
package com.gaoxinfu.demo.springboot.componentscan;

import org.springframework.stereotype.Service;

/**
 * @Description:
 * @Author: gaoxinfu
 * @Date: 2020-04-18 16:56
 */
@Service
public class ComponentScanBean {

    public void say(){
        System.out.println("say() = Hello");
    }
}

1.2.3.2.2.ComponentScanMain
package com.gaoxinfu.demo.springboot.componentscan;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;

/**
 * @Description:
 * @Author: gaoxinfu
 * @Date: 2020-04-18 16:56
 */
@ComponentScan
public class ComponentScanMain {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext AnnotationConfigApplicationContext=new AnnotationConfigApplicationContext(ComponentScanMain.class);
        String[] definitionNames= AnnotationConfigApplicationContext.getBeanDefinitionNames();
        for(int i=0;i< definitionNames.length;i++){
            System.out.println("definitionName = "+definitionNames[i]);
        }

    }
}

会自动扫面到@Service的类ComponentScanBean
在这里插入图片描述

1.2.3.3.设置扫描的路径
1.也可以通过
@ComponentScan(basePackages = "com.gaoxinfu.demo.springboot.componentscan")
设置扫描的路径

在这里插入图片描述

多个路径的话,可以用逗号分隔开

@ComponentScan(basePackages = "com.gaoxinfu.demo.springboot.componentscan,com.gaoxinfu.demo.springboot.configuration")

在这里插入图片描述

2. 关闭自动配置

通过上述,我们得知,Spring Boot会根据项目中的jar包依赖,自动做出配置,
Spring Boot支持的自动配置如下(非常多):

在这里插入图片描述

如果我们不需要Spring Boot自动配置,想关闭某一项的自动配置,该如何设置呢?
比如:我们不想自动配置Redis,想手动配置。

在这里插入图片描述

3.自定义Banner

启动Spring Boot项目后会看到这样的图案:
在这里插入图片描述

这个图片其实是可以自定义的:
http://patorjk.com/software/taag/#p=display&f=Graffiti&t=Type%20Something%20
在这里插入图片描述

拷贝生成的字符到一个文本文件中,并且将该文件命名为banner.txt
将banner.txt拷贝到项目的resources目录中:

在这里插入图片描述

重新启动程序,查看效果,会有新的图案显示

如果不想看到任何的banner,也是可以将其关闭的:
在这里插入图片描述

4.全局配置文件

4.1.spring-boot 默认配置文件路径

xxxx-autoconfigure-xxx.jar包下
有可能多个,下面是其中一个 spring-boot-autoconfigure-xxx.jar
在这里插入图片描述

5.Starter pom

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.Xml配置文件

在这里插入图片描述

7.日志

Spring Boot对各种日志框架都做了支持,我们可以通过配置来修改默认的日志的配置:

#设置日志级别
logging.level.org.springframework=DEBUG
格式:
logging.level.*= # Log levels severity mapping. For instance `logging.level.org.springframework=DEBUG`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东山富哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值