第一章:SpringBoot基础入门

第一章:SpringBoot基础入门

1.1:SpringSpinrBoot

  1. Spring能做什么

    • Spring的能力
      在这里插入图片描述

    • Spring的生态

      网址:https://spring.io/projects/spring-boot

      覆盖了:Web开发、数据访问、安全控制、分布式、消息服务、移动开发、批处理等。

    • Spring5重大升级

      1. 响应式编程
        在这里插入图片描述

      2. 内部源码设计

        基于Java8的一些新特性。

  2. 为什么用SpringBoot

    Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can "just run"。能快速创建出生产级别的Spring应用。

    • SpringBoot的优点

      1. Create stand-alone Spring applications:创建独立的Spring应用。
      2. Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files):内嵌Web服务器。
      3. Provide opinionated 'starter' dependencies to simplify your build configuration:自动starter依赖,简单构建配置
      4. Automatically configure Spring and 3rd party libraries whenever possible:自动配置Spring以及第三方功能。
      5. Provide production-ready features such as metrics, health checks, and externalized configuration:提供生产级别的监控、健康检查以及外部化配置。
      6. Absolutely no code generation and no requirement for XML configuration:无代码生成、无需编写XML

      SpringBoot是整合Spring技术栈的一站式框架,SpringBoot是简化Spring技术栈的快速开发脚手架。

    • SpringBoot的缺点

      1. 迭代快,需要时刻关注变化。
      2. 封装太深,内部原理复杂,不容易精通。
  3. 时代背景

    • 微服务

      James Lewis and Martin Fowler (2014) 提出微服务完整概念。https://martinfowler.com/microservices/

      ​ 微服务是一种架构风格。一个应用拆分为一组小型服务。每个服务运行在自己的进程内,也就是可独立部署和升级。服务之间使用轻量级HTTP交互。服务围绕业务功能拆,可以由全自动部署机制独立部署,去中心化,服务自治。服务可以使用不同的语言、不同的存储技术。

    • 分布式
      在这里插入图片描述

      1. 分布式的困难

        远程调用、服务发现、负载均衡、服务容错、配置管理、服务监控、链路追踪、日志管理、任务调度。

      2. 分布式的解决

        SpringBoot + SpringCloud
        在这里插入图片描述

  4. 如何学习SpringBoot

    • 官网文档架构
      在这里插入图片描述
      在这里插入图片描述

      查看版本新特性:https://github.com/spring-projects/spring-boot/wiki#release-notes
      在这里插入图片描述

1.2:SpringBoot2入门

需求:浏览器发送/hello请求,响应Hello, Spring Boot 2

  1. 创建maven工程

    IDEA创建一个新的工程boot_helloworld_01

  2. 引入依赖

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
    </parent>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
    
  3. 创建主程序

    package com.wang;
    
    // 主程序类。@SpringBootApplication:这是一个SpringBoot应用
    @SpringBootApplication
    public class MainApplication {
        public static void main(String[] args) {
            SpringApplication.run(MainApplication.class, args);
        }
    }
    
  4. 编写业务

    package com.wang.controller;
    
    @RestController
    public class HelloController {
    
        @RequestMapping("/hello")
        public String handle01() {
            return "Hello, Spring Boot 2!";
        }
    }
    
  5. 测试

    直接运行mian方法,浏览器访问http://localhost:8080/hello
    在这里插入图片描述

  6. 修改配置

    resources文件夹下创建application.properties文件

    server.port=8888
    

    修改了上面配置,重新启动项目,http://localhost:8080/hello访问成功。

  7. 简化部署

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

    重新打成一个jar包在cmd窗口下也能运行。
    在这里插入图片描述
    在这里插入图片描述

1.3:了解自动配置原理

  1. SpringBoot特点

    • 依赖管理

      1. 父项目做依赖管理

        几乎声明了所有开发中常用的依赖的版本号,自动版本仲裁机制。

        <!-- 依赖管理,自己工程引入的依赖 -->
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.3.4.RELEASE</version>
        </parent>
        
        <!-- 它的父项目 -->
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.3.4.RELEASE</version>
        </parent>
        
      2. 开发导入starter场景启动器

        • 见到很多spring-boot-starter-**就是某种场景。

        • 只要引入starter,这个场景的所有常规需要的依赖我们都自动引入。

        • SpringBoot所有支持的场景:

          https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.build-systems.starters

        • 见到*-spring-boot-starter:第三方为我们提供的简化开发的场景启动器。

        • 所有场景启动器最底层的依赖

          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter</artifactId>
              <version>2.3.4.RELEASE</version>
              <scope>compile</scope>
          </dependency>
          
      3. 无需关注版本号,自动版本仲裁

        • 引入依赖默认都可以不写版本。
        • 引入非版本仲裁的jar,要写版本号。
      4. 可以修改默认版本

        查看spring-boot-dependencies里面规定当前依赖的版本用的key

        <!-- 举例:修改mysql的版本依赖 -->
        <properties>
            <mysql.version>5.1.43</mysql.version>
        </properties>
        
    • 自动配置

      1. 自动配好Tomcat

        • 引入Tomcat依赖

          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-tomcat</artifactId>
              <version>2.3.4.RELEASE</version>
              <scope>compile</scope>
          </dependency>
          
        • 配置Tomcat

      2. 自动配置好SpringMVC

        • 引入SpringMVC全套组件。
        • 自动配好SpringmVC常用组件(功能)。
      3. 自动配好Web常见功能

        • SpringBoot帮我们配置好了所有Web开发的场景。
      4. 默认的包结构

        • 主程序所在包及其下面的所有子包里面的组件都会被默认扫描出来。

        • 无序以前的包扫描配置。

        • 想要改变扫描路径:@SpringBootApplication(scanBasePackages = "com")或者@ComponentScan("com.wang")指定扫描路径

          @SpringBootApplication(scanBasePackages = "com.wang")
          // 等同于
          @SpringBootConfiguration
          @EnableAutoConfiguration
          @ComponentScan("com.wang")
          
      5. 各种配置拥有默认值

        • 默认配置最终都是映射到某个类上。
        • 配置文件的值最终会绑定在每个类上,这个类会在容器中创建对象。
      6. 按需加载所有自动配置项

        • 非常多的starter
        • 引入了哪些场景这个场景的自动配置才会开启。
        • SpringBoot所有的自动配置功能都在spring-boot-autoconfigure包里面。
  2. 容器功能

    • 添加组件

      先创建两个实体类:UserPet

      package com.wang.bean;
      
      // 用户
      public class User {
          private String name;
          private Integer age;
          private Pet pet;
      
          // 省略空参、全参构造、get/set方法、toString方法
      }
      
      package com.wang.bean;
      
      // 宠物
      public class Pet {
          private String name;
      
          // 省略空参、全参构造、get/set方法、toString方法
      }
      
      1. @Configuration

        Full模式与Lite模式:

        • 配置类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断。
        • 配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式。
        // Configuration使用示例
        package com.wang.config;
        
        // 配置类里面使用@Bean标注在方法给容器注册组件,默认也是单实例的。
        // 配置类本身也是组件
        // proxyBeanMethods =  false:代理bean的方法
        	// Full(proxyBeanMethods =  true): 【保证每个@Bean方法被调用多少次返回的组件都是单实例的】
        	// Lite(proxyBeanMethods =  false): 【每个@Bean方法被调用多少次返回的组件都是新创建的】
        	// 组件依赖必须使用Full模式默认。其他默认是否Lite模式
        
        // 告诉SpringBoot这一个配置类 == 配置文件
        @Configuration(proxyBeanMethods =  false)
        public class MyConfig {
        
            // 给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例
            @Bean
            public User user01() {
                User zhangsan =  new User("zhangsan", 18);
                // User组件依赖了Pet组件
                zhangsan.setPet(tomcatPet());
                return zhangsan;
            }
        
            @Bean("tom")
            public Pet tomcatPet() {
                return new Pet("tomcat");
            }
        }
        
        // Configuration测试代码
        package com.wang;
        
        @SpringBootApplication
        public class MainApplication {
        
            public static void main(String[] args) {
                // 1. 返回我们IOC容器
                ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
                
                // 2. 查看容器里面的组件
                String[] names = run.getBeanDefinitionNames();
                for (String name: names) {
                    System.out.println(name);
                }
        
                // 3.从容器中获取组件
                Pet tom01 = run.getBean("tom", Pet.class);
                Pet tom02 = run.getBean("tom", Pet.class);
                System.out.println("组件:" + (tom01 == tom02));
                MyConfig bean = run.getBean(MyConfig.class);
                System.out.println(bean);
                
                // 4. 组件是否单例
                User user1 = bean.user01();
                User user2 = bean.user01();
                System.out.println(user1 == user2);
        
                User user01 = run.getBean("user01", User.class);
                Pet tom = run.getBean("tom", Pet.class);
                System.out.println("用户的宠物: " + (user01.getPet() == tom));
            }
        }
        
      2. @Import()

        @Import({User.class, DBHelper.class})给容器中自动创建出这两个类型的组件、默认组件的名字就是全类名。

      3. @Conditional

        条件装配:满足Conditional指定的条件,则进行组件注入。

        // 条件转配示例
        package com.wang.config;
        
        @Configuration
        // 容器中存在tom组件时,才给类中的bean进行组件注入
        @ConditionalOnBean(name = "tom")
        public class MyConfig {
        
            @Bean
            public User user01() {
                User zhangsan =  new User("zhangsan", 18);
                zhangsan.setPet(tomcatPet());
                return zhangsan;
            }
        
            @Bean("tom22")
            public Pet tomcatPet() {
                return new Pet("tomcat");
            }
        }
        
        // 测试条件装配
        package com.wang;
        
        @SpringBootApplication()
        public class MainApplication {
            public static void main(String[] args) {
                ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
        
                boolean tom = run.containsBean("tom");
                System.out.println("容器中Tom组件:" + tom);
                boolean user01 = run.containsBean("user01");
                System.out.println("容器中user01组件:" + user01);
                boolean tom22 = run.containsBean("tom22");
                System.out.println("容器中tom22组件:" + tom22);
            }
        }
        
    • 原生配置文件引入

      1. @ImportResource

        <!--创建beans.xml配置文件-->
        <bean id="haha" class="com.wang.bean.User">
            <property name="name" value="zhangsan"></property>
            <property name="age" value="18"></property>
        </bean>
        
        <bean id="hehe" class="com.wang.bean.Pet">
            <property name="name" value="tomcat"></property>
        </bean>
        
        package com.wang.config;
        
        // 添加@ImportResource注解
        @ImportResource("classpath:beans.xml")
        public class MyConfig { .... }
        
        // 测试IOC容器是否存在haha和hehe组件
        package com.wang;
        
        @SpringBootApplication
        public class MainApplication {
            public static void main(String[] args) {
                ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
        
                boolean haha = run.containsBean("haha");
                System.out.println("haha: :" + haha);
                boolean hehe = run.containsBean("hehe");
                System.out.println("hehe::" + hehe);
            }
        }
        
    • 配置绑定

      如何使用Java读取到properties文件中的内容,并且把它封装到JavaBean中,以供随时使用:

      # application.properties文件中 
      mycar.brand=BYD
      mycar.price=100000
      
      1. @ConfigurationProperties

        package com.wang.bean;
        
        // 只有在容器中的组件,才会拥有SpringBoot提供的强大功能
        @Component
        @ConfigurationProperties(prefix = "mycar")
        public class Car {
            private String brand;
            private Integer price;
        	
            // 省略get/set方法、toString方法
        }
        
      2. @EnableConfigurationProperties+@ConfigurationProperties

        package com.wang.bean;
        
        @ConfigurationProperties(prefix = "mycar")
        public class Car { ..... }
        
        package com.wang.config;
        
        @Configuration
        // 1.开启Car配置绑定功能
        // 2.把这个Car这个组件自动注册到容器中
        @EnableConfigurationProperties(Car.class)
        public class MyConfig { ... }
        
  3. 自动配置原理入门

    • 引导加载类自动配置

      @SpringBootConfiguration
      @EnableAutoConfiguration
      @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
      public @interface SpringBootApplication { ... }
      
      1. @SpringBootConfiguration

        // 代表当前是一个配置类
        @Configuration
        public @interface SpringBootConfiguration {}
        
      2. @ComponentScan

        // 指定扫描那些注解
        @Repeatable(ComponentScans.class)
        public @interface ComponentScan { .... }
        
      3. @EnableAutoConfiguration

        @AutoConfigurationPackage
        @Import(AutoConfigurationImportSelector.class)
        public @interface EnableAutoConfiguration { ... }
        
        • @AutoConfigurationPackage

          // 利用Registrar给容器中导入一系列组件
          // 将指定的一个包下的所有组件导入进来,MainApplication主程序所在的包下
          @Import(AutoConfigurationPackages.Registrar.class)
          public @interface AutoConfigurationPackage { .... }
          
          package org.springframework.boot.autoconfigure;
          
          public abstract class AutoConfigurationPackages {
          
              ......
              
          	/**
          	 * {@link ImportBeanDefinitionRegistrar} to store the base package from the importing
          	 * configuration.
          	 */
          	static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
          
          @Override
          public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
          	register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
           }
                  
          		@Override
          		public Set<Object> determineImports(AnnotationMetadata metadata) {
          			return Collections.singleton(new PackageImports(metadata));
          		}
          	}
              
              .....
          }
          

          在这里插入图片描述

        • @Import(AutoConfigurationImportSelector.class)

          // 1.利用getAutoConfigurationEntry(annotationMetadata)给容器中批量导入一些组件
          // 2.调用List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)
          // 获取到所有需要导入到容器中的配置类
          package org.springframework.boot.autoconfigure;
          
          public class AutoConfigurationImportSelector implements  DeferredImportSelector,
          	BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
                  
                  ......
                  
              @Override
          	public String[] selectImports(AnnotationMetadata annotationMetadata) {
          		if (!isEnabled(annotationMetadata)) {
          			return NO_IMPORTS;
          		}
               AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
          		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
          	}
                  
          protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
          		if (!isEnabled(annotationMetadata)) {
          			return EMPTY_ENTRY;
          		}
          		AnnotationAttributes attributes = getAttributes(annotationMetadata);
          		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
          		configurations = removeDuplicates(configurations);
          		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
          		checkExcludedClasses(configurations, exclusions);
          		configurations.removeAll(exclusions);
          		configurations = getConfigurationClassFilter().filter(configurations);
          		fireAutoConfigurationImportEvents(configurations, exclusions);
          		return new AutoConfigurationEntry(configurations, exclusions);
          	}
                       
                  .......   
          }
          

          在这里插入图片描述

          // 接上面的类
          protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, 
                                                            AnnotationAttributes attributes) {
          		List<String> configurations = 
                      SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), 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;
          }
          
          
          
          
          // 3.利用工厂加载Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader)
          // 得到所有的组件
          
          // 4.从META-INF/spring.factories位置来加载一个文件。
          // 默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件。
          // spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面有META-INF/spring.factories
          package org.springframework.core.io.support;
          
          public final class SpringFactoriesLoader {
              
              ....
              
              // 此方法的返回值
              public static List<String> loadFactoryNames(Class<?> factoryType, 
                                                          @Nullable ClassLoader classLoader) {
          		String factoryTypeName = factoryType.getName();
          	return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
          	}
              
              private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
          		MultiValueMap<String, String> result = cache.get(classLoader);
          		if (result != null) {
          			return result;
          		}
          
          		try {
          			Enumeration<URL> urls = (classLoader != null ?
          					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
          					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
          			result = new LinkedMultiValueMap<>();
          			while (urls.hasMoreElements()) {
          				URL url = urls.nextElement();
          				UrlResource resource = new UrlResource(url);
          				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
          				for (Map.Entry<?, ?> entry : properties.entrySet()) {
          					String factoryTypeName = ((String) entry.getKey()).trim();
          					for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
          						result.add(factoryTypeName, factoryImplementationName.trim());
          					}
          				}
          			}
          			cache.put(classLoader, result);
          			return result;
          		}
          		catch (IOException ex) {
          			throw new IllegalArgumentException("Unable to load factories from location [" +
          					FACTORIES_RESOURCE_LOCATION + "]", ex);
          		}
          	}
                  
              ....
              
          }
          

          在这里插入图片描述

    • 按需开启自动配置项

      # 文件里面写死了springBoot启动就要给容器加载的所有配置类
      ....
      # Auto Configure
      org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
      org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
      org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
      org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
      org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
      ....
      
      # 虽然我们127个场景的所有自动配置启动的时候默认全部加载。XxxAutoConfiguration
      # 按照条件装配规则(@Conditional),最终会按需配置。
      
    • 总结

      // 随便点开spring-boot-autoconfigure-2.3.4.RELEASE.jar包下XxxAutoConfiguration文件看是否配置了此组件
      @Configuration(proxyBeanMethods = false)
      @EnableConfigurationProperties(ServerProperties.class)
      @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
      @ConditionalOnClass(CharacterEncodingFilter.class)
      @ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
      public class HttpEncodingAutoConfiguration { .... }
      
      1. SpringBoot先加载所有的自动配置类XxxAutoConfiguration
      2. 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。XxxProperties里面拿。XxxProperties和配置文件进行了绑定。
      3. 生效的配置类就会给容器中装配很多组件。
      4. 只要容器中有这些组件,就相当于这些功能就有了。
      5. 定制化配置。
        • 用户直接自己@Bean替换底层的组件。
        • 用户去看这个组件是获取的配置文件什么值就去修改。

      XxxAutoConfiguration ——> 加载组件 ——> XxxProperties里面拿值 ——> application.properties修改配置

    • 最佳实践

      1. 引入场景依赖
      2. 查看自动配置了哪些(选做):配置文件中debug=true开启自动配置报告。【Negative(不生效)、Positive(生效)】
      3. 是否需要修改:参照文档修改配置项
      4. 自动加入或者替换组件:@Bean、@Component....
      5. 自定义器XxxCustomizer
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
非常感谢您的提问,以下是我的回答: 1. Spring Boot的自动配置原理: Spring Boot的自动配置是基于条件注解实现的。Spring Boot会根据classpath中的jar包、类、属性等信息,来决定是否需要给容器中添加配置类。如果需要添加,则会根据条件自动配置底层框架,如Tomcat、Redis、MySQL等。如果不需要添加,则不会添加。 2. Spring Boot导入其他的配置文件: Spring Boot可以通过在application.properties或application.yml文件中添加spring.config.import属性来导入其他的配置文件。例如,可以添加以下内容来导入另一个配置文件: ``` spring.config.import=classpath:/another-config.yml ``` 3. Spring Boot的核心注解: Spring Boot的核心注解包括@SpringBootApplication、@Configuration、@EnableAutoConfiguration和@ComponentScan。其中@SpringBootApplication是一个复合注解,包含了@Configuration、@EnableAutoConfiguration和@ComponentScan注解的功能。 4. Spring Boot的实现: Spring Boot的实现基于Spring框架,它使用了大量的注解来简化配置。它还使用了条件注解来根据不同条件自动配置底层框架。另外,Spring Boot还提供了很多starter包,可以让开发人员更加方便快速地搭建应用程序。 5. Spring Boot和Spring MVC的区别: Spring Boot是一个快速构建基于Spring的应用程序的框架,它使用了很多注解来简化配置。而Spring MVC是一个基于MVC架构的Web应用程序框架,它是Spring框架的一部分。Spring Boot可以使用Spring MVC来构建Web应用程序,但是它还可以用于构建其他类型的应用程序。 6. Spring Boot如何跨域请求: Spring Boot可以通过添加一个跨域请求的过滤器来实现跨域请求。具体来说,可以创建一个类实现javax.servlet.Filter接口,然后在类上添加@WebFilter注解,并设置urlPatterns属性来指定需要跨域请求的URL。在过滤器实现的doFilter方法中,设置Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Headers和Access-Control-Max-Age等跨域请求头信息即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值