扩展Spring Context能力:bp-support-spring-context项目解析

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:"bp-support-spring-context"项目为Spring Context框架提供增强支持,聚焦企业级应用开发。Spring Context是Spring框架核心,涉及配置管理、bean生命周期管理、依赖注入、AOP、资源访问、事件传播和国际化支持。该项目可能提供了扩展功能,如自定义ApplicationContext子类、新的bean后处理器、增强的AOP切面、自定义配置元数据解析,以及框架间集成适配器。了解Spring Context的工作原理对于使用此项目至关重要。 bp-support-spring-context:支持使用Spring Context

1. Spring Context核心功能概述

Spring Context是Spring框架中的一个关键模块,提供了对应用上下文(ApplicationContext)的支持,是其他许多Spring模块的依赖基础。作为整个应用的容器,Spring Context负责实例化、配置和组装Bean,同时也提供了对Bean生命周期的管理、事件传播、资源加载以及国际化支持等功能。

在这一章中,我们将简要探讨Spring Context的核心功能,从基础概念讲起,逐步深入到各种功能的细节,以及在实际应用开发中的重要性。通过介绍和分析,希望读者能在完成本章学习后对Spring Context有一个全面和深入的理解。

1.1 Spring Context在现代Java应用中的角色

Spring Context在现代Java应用中承担着中央控制单元的角色。通过定义和管理应用内的Bean,它为开发者提供了一种方便的方式来组织和优化代码结构。无论是在单体应用还是微服务架构中,Spring Context都能提供一致的方式来处理常见的企业级开发问题。

1.2 Spring Context的主要组件

为了实现其功能,Spring Context引入了多个组件,比如BeanFactory用于对象的创建和管理,ResourceLoader用于资源的抽象和访问,而ApplicationContext则是BeanFactory的扩展,增加了消息源(MessageSource)、事件发布(ApplicationEventPublisher)等高级功能。理解这些组件及其相互协作,对于掌握Spring Context至关重要。

1.3 Spring Context与Spring Boot的结合

随着Spring Boot的流行,Spring Context的使用变得更为简单和高效。Spring Boot通过约定优于配置的原则,大量减少了应用上下文的配置工作。但深入理解Spring Context的工作机制,仍然对于优化和定制Spring Boot应用具有重要的意义。

通过本章内容的介绍,读者应具备了开始深入学习Spring Context各项细节的基础,并能够理解其在整个Spring生态系统中的重要性。接下来,我们将逐步展开Spring Context各个方面的深入探讨。

2. ```

第二章:配置管理方法与优势

2.1 Spring Context配置概览

2.1.1 配置文件的类型和选择

在Spring框架中,配置文件是应用配置管理的核心。它允许开发者以声明式的方式将对象的配置信息从应用程序代码中分离出来。常见的配置文件类型有XML、Java配置类以及Properties文件。

XML配置文件 ,传统的Spring XML配置文件定义了bean及其相互之间的依赖关系。开发者可以通过 <bean> 标签来声明一个对象,并通过 <property> 来注入依赖, <constructor-arg> 标签来注入构造函数的参数。

<bean id="myService" class="com.example.MyServiceImpl">
    <property name="dependencyService" ref="dependencyService"/>
</bean>

Java配置类 提供了另一种更为现代的配置方式,通过使用 @Configuration 注解标注一个类,并用 @Bean 注解标注在方法上声明bean。

@Configuration
public class AppConfig {
    @Bean
    public MyService myService() {
        MyServiceImpl myService = new MyServiceImpl();
        myService.setDependencyService(dependencyService());
        return myService;
    }
}

Properties文件 则通常用于配置非对象的配置信息,如数据库连接信息、服务端口等。在Spring中,可以通过 @PropertySource 注解来引入Properties文件。

选择哪种配置方式,取决于项目的需求和个人偏好。通常, Java配置类 因其类型安全和面向对象的特性,使得重构变得更加容易,而 XML 则对于复杂配置的清晰性和可维护性有着优势。 Properties文件 则适合那些简单的、键值对形式的配置数据。

2.1.2 基于注解的配置方法

基于注解的配置是一种更为简洁且易于管理的配置方式。Spring通过扫描带有特定注解的类来自动配置它们,无需显式地在配置文件中声明。

  • @Component :用于标注普通的Spring管理类。
  • @Service :用于标注业务逻辑层(Service层)的类。
  • @Repository :用于标注数据访问层(DAO层)的类。
  • @Controller :用于标注控制层(如Spring MVC中的Controller)的类。
@Repository
public class MyDao {
    public void saveData(DataObject data) {
        // 数据保存逻辑
    }
}

利用 @ComponentScan 注解,Spring将自动扫描指定包下的带有上述注解的类,并注册为Spring的bean。

@ComponentScan(basePackages = "com.example")
public class AppConfig {
}

这种方法不仅减少了XML配置文件的体积,而且使代码更加清晰。在大型项目中,合理使用注解可以大幅度提升开发效率和项目的可维护性。

2.2 配置管理的实践技巧

2.2.1 Spring Profiles的使用

Spring Profiles 提供了根据不同的部署环境选择不同的bean定义的机制。这对于在开发、测试和生产等不同环境中使用不同配置非常有用。

通过 @Profile 注解可以为bean定义环境特定的配置。例如,你可能需要在开发环境中使用嵌入式数据库,而在生产环境中使用外部数据库。

@Configuration
public class AppConfig {
    @Bean
    @Profile("dev")
    public DataSource developmentDataSource() {
        EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
        return builder.setType(EmbeddedDatabaseType.H2).build();
    }

    @Bean
    @Profile("prod")
    public DataSource productionDataSource() {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName("org.h2.Driver");
        dataSource.setUrl("jdbc:h2:tcp://localhost/~/test");
        dataSource.setUsername("sa");
        return dataSource;
    }
}

然后,你可以通过设置 spring.profiles.active 属性来激活特定的配置文件:

  • 在命令行中: java -jar app.jar --spring.profiles.active=dev
  • 在应用属性文件中: spring.profiles.active=dev

Spring Profiles机制提高了配置的灵活性和项目的适应性,使得开发团队能够方便地管理和切换不同环境下的配置。

2.2.2 外部化配置的实现方式

外部化配置允许将配置信息从应用程序代码中分离出来,通常存储在外部文件中,如properties或YAML文件。这使得应用程序能够更容易地适应不同的部署环境,不需要改动代码。

在Spring Boot项目中,可以通过 application.properties application.yml 文件来实现外部化配置。Spring Boot会自动加载这些配置文件,并将其属性值注入到Spring的环境中。

# application.properties
server.port=8080
spring.datasource.url=jdbc:mysql://localhost/test

或者使用YAML格式:

# application.yml
server:
  port: 8080
spring:
  datasource:
    url: jdbc:mysql://localhost/test

在Java代码中,可以使用 @Value 注解将这些外部配置的值注入到bean的属性中。

@Component
public class MyBean {
    @Value("${server.port}")
    private int port;

    @Value("${spring.datasource.url}")
    private String datasourceUrl;

    // 使用port和datasourceUrl的逻辑
}

通过外部化配置,开发者能够更加灵活地管理应用程序的行为,比如数据库连接、日志级别、第三方服务API密钥等敏感信息,从而在不同的部署环境中安全地部署应用程序。

2.3 配置管理的优势分析

2.3.1 提高应用的可配置性

通过配置管理,应用程序可以从硬编码的配置信息中解放出来,变得更加灵活和可配置。开发者可以在不修改代码的情况下,通过修改配置文件来改变应用的行为,这使得应用更容易适应不同的环境。

例如,数据库连接信息可以单独存储在配置文件中,而不是直接在代码中硬编码。这不仅有助于保护敏感信息,而且在数据库迁移或更换时,只需修改配置文件即可。

// 使用配置文件中的数据库连接信息
String url = env.getProperty("spring.datasource.url");
String username = env.getProperty("spring.datasource.username");
String password = env.getProperty("spring.datasource.password");

2.3.2 环境适配与版本管理

在开发多环境的应用程序时,配置管理至关重要。例如,开发、测试、预发布和生产环境可能各自有不同的配置需求,比如不同的数据库连接字符串、日志级别或缓存配置等。

配置文件或外部化配置可以根据不同的环境变量来加载相应的配置信息。这样,开发人员可以针对每个环境创建不同的配置文件,如 application-dev.properties application-prod.properties 等,并通过环境变量指定当前环境的配置文件。

export SPRING_PROFILES_ACTIVE=dev

或者在启动应用时指定:

java -jar myapp.jar --spring.profiles.active=dev

这使得应用能够自动加载适合当前环境的配置,从而实现环境适配。此外,版本控制系统可以帮助管理不同版本的配置文件,进一步提高了配置管理的可追踪性和版本控制能力。

通过以上方式,配置管理不仅仅提高了应用的可配置性和灵活性,还增强了应用对不同环境的适应能力,并且方便了版本控制和管理,使得应用部署更加高效和安全。


# 3. Bean生命周期管理细节

在Spring框架中,Bean的生命周期管理是一项核心功能,它确保了应用中对象的正确创建、初始化、使用和销毁。理解并掌握Bean的生命周期,能够帮助开发者更有效地控制对象的行为,优化资源的使用,以及更好地处理复杂的业务场景。

## 3.1 Bean生命周期概览

### 3.1.1 Bean的定义和作用域

在Spring容器中,Bean是被实例化、组装和管理的对象。Bean的定义包含了其创建的类信息以及如何创建和初始化Bean的指令。

Bean的作用域(Scope)定义了Bean的生命周期以及在Spring应用上下文中的可见性。常见的作用域包括:

- `singleton`:在整个Spring容器中,每个Bean定义只有一个实例。
- `prototype`:每次请求Bean时,都会创建一个新的实例。
- `request`:在Web应用中,对于每个HTTP请求,都会产生一个新的Bean实例。
- `session`:在一个HTTP Session中,会保持Bean的单一实例。
- `application`:在一个ServletContext生命周期内,Bean实例是唯一的。
- `websocket`:在一个WebSocket生命周期内,Bean实例是唯一的。

### 3.1.2 Bean的初始化和销毁过程

Bean的初始化和销毁过程可以分为以下几个步骤:

- **实例化Bean**:容器创建Bean实例。
- **依赖注入**:通过属性、构造函数等向Bean中注入依赖项。
- **BeanNameAware & BeanFactoryAware**:如果Bean实现了相应的Aware接口,Spring容器会调用`setBeanName`或`setBeanFactory`方法,传递Bean的名字或Bean工厂实例。
- **BeanPostProcessor的前置处理**:在Bean实例化之后、初始化之前,Spring容器会遍历并调用所有BeanPostProcessor的`postProcessBeforeInitialization`方法。
- **初始化方法**:调用Bean的初始化方法,比如带有`@PostConstruct`注解的方法,或者配置文件中指定的init-method。
- **BeanPostProcessor的后置处理**:初始化方法执行后,Spring容器会遍历并调用所有BeanPostProcessor的`postProcessAfterInitialization`方法。
- **销毁方法**:在容器关闭时,会调用Bean的销毁方法,如带有`@PreDestroy`注解的方法,或配置文件中指定的destroy-method。

## 3.2 生命周期回调接口的应用

### 3.2.1 使用InitializingBean和DisposableBean

`InitializingBean`和`DisposableBean`是Spring提供的两个接口,它们允许开发者以编程方式控制Bean的初始化和销毁行为。

```java
public class MyBean implements InitializingBean, DisposableBean {
    public void afterPropertiesSet() throws Exception {
        // 初始化逻辑
    }

    public void destroy() throws Exception {
        // 销毁逻辑
    }
}

通过实现这些接口,开发者可以在初始化和销毁Bean时执行自定义的逻辑。但需要注意的是,这种方法和XML配置中的init-method和destroy-method相似,因此在使用时应避免混淆。

3.2.2 @PostConstruct和@PreDestroy注解的使用

Java的 javax.annotation.PostConstruct javax.annotation.PreDestroy 注解提供了一种更为标准的方式来标记初始化和销毁方法。

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

public class MyBean {
    @PostConstruct
    public void init() {
        // 初始化逻辑
    }

    @PreDestroy
    public void cleanup() {
        // 清理逻辑
    }
}

这种方式更符合Java的注解驱动开发范式,也被Spring广泛支持。它不需要实现任何接口,可以直接在Bean的方法上使用这两个注解来指定初始化和销毁逻辑。

3.3 自定义Bean生命周期行为

3.3.1 BeanPostProcessor的作用

BeanPostProcessor 接口是扩展Spring Bean生命周期的另一种方式。开发者可以实现此接口来修改容器中Bean的定义,或者在Bean的初始化前后进行干预。

import org.springframework.beans.factory.config.BeanPostProcessor;

public class CustomBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // 在初始化前修改Bean
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // 在初始化后修改Bean
        return bean;
    }
}

通过实现 BeanPostProcessor ,开发者可以控制Bean的某些行为,比如进行AOP代理增强。

3.3.2 实现自定义的BeanFactoryPostProcessor

BeanFactoryPostProcessor 允许在容器加载了Bean的定义之后,但还未实例化Bean之前进行干预。开发者可以使用它来修改Bean的定义属性。

import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 修改Bean定义的属性
    }
}

这个接口对于开发者来说非常有用,特别是在需要根据应用的配置情况动态调整Bean属性时。

总结本章内容,我们已经从宏观到微观详细探讨了Spring中Bean的生命周期管理细节。理解并应用这些生命周期管理机制,能够极大地提升我们对Spring框架的理解和应用能力,同时也能够更好地设计和优化Spring应用的结构。在下一章中,我们将深入探讨依赖注入的实现与重要性,为读者揭示Spring中另一种核心功能的内幕。

4. 依赖注入的实现与重要性

4.1 依赖注入的基本原理

4.1.1 DI的类型和选择

依赖注入(Dependency Injection,简称DI)是Spring框架的核心特性之一,它是一种实现控制反转(Inversion of Control,IoC)的设计模式,用于降低组件之间的耦合度。在Spring中,依赖注入主要通过以下几种类型实现:

  • 构造器注入(Constructor Injection) :通过构造函数为依赖项提供值。这种方式可以在对象构造时立即验证依赖关系,保证注入的参数不为null,但不够灵活,因为每个依赖都需要对应一个构造函数。
  • 设值注入(Setter Injection) :通过setter方法为依赖项提供值。这种方式较为灵活,可以注入的属性更多,但不能保证依赖项一定被注入,可能会有null值。
  • 字段注入(Field Injection) :通过@Autowired注解直接在字段上注入依赖。这种方式代码简洁,但降低了代码的可测试性,同时不直观显示依赖关系。

选择合适的注入方式时,应考虑以下几个因素: - 封装性 :构造器注入可以保证依赖项在对象创建时已经被初始化,提高了封装性。 - 灵活性 :设值注入提供了更高的灵活性,允许依赖项在不同的时间被注入。 - 代码可测试性 :构造器注入和设值注入都比较容易编写单元测试,而字段注入会使得单元测试依赖于Spring框架,增加了测试的复杂性。

4.1.2 使用@Autowired和@Resource注解

@Autowired和@Resource是Spring提供的依赖注入注解,用于自动注入所需的依赖项。

  • @Autowired注解 :根据类型自动装配依赖项。当Spring容器中存在多个相同类型的bean时,@Autowired会使用byType方式注入;如果类型匹配的bean不存在,则会报错。为了处理这种情况,可以通过@Qualifier注解来指定具体的bean名称。
@Autowired
public void setDependency(Dependency dependency) {
    this.dependency = dependency;
}
  • @Resource注解 :默认按名称注入依赖项,如果没有明确指定name属性,则按类型注入。@Resource的注入方式更加灵活,同时也可以解决@Qualifier无法解决的问题。
@Resource(name = "dependency")
public void setDependency(Dependency dependency) {
    this.dependency = dependency;
}

4.2 注入策略与实践

4.2.1 构造器注入与设值注入的比较

构造器注入和设值注入各有优劣,下面进行比较:

| 特性 | 构造器注入 | 设值注入 | | --- | --- | --- | | 优点 | 1. 对象创建立即验证依赖关系。
2. 不可变对象。
3. 支持依赖关系的验证。 | 1. 可以注入更多类型的依赖。
2. 可以接受null值。
3. 灵活性高,可根据需要配置不同值。 | | 缺点 | 1. 对于很多依赖项,可能会导致构造函数过于复杂。
2. 不够灵活。 | 1. 依赖项可能不会被注入。
2. 对象创建后,可以更改依赖项。 | | 适用场景 | 适用于依赖项不会改变的场景。 | 适用于依赖项可能在不同时间点注入的场景。 |

4.2.2 注入时机和依赖的加载顺序

注入时机和依赖的加载顺序通常与应用上下文的加载过程有关。在Spring中,当ApplicationContext启动时,会根据bean的定义和配置,初始化和注入依赖项。

  • 预初始化(Pre-Injection) :在bean实际使用之前进行依赖注入,确保所有依赖项在bean使用前都已经注入完成。
  • 懒加载(Lazy Initialization) :通过设置bean的 lazy-init 属性为true,可以延迟bean的初始化。这适用于不急迫需要的bean,可以有效提高应用启动速度。
  • 依赖的加载顺序 :依赖项的加载顺序可以通过实现 Ordered 接口或者使用 @Order 注解来控制。

4.3 依赖注入的重要性

4.3.1 降低模块间的耦合度

依赖注入通过控制反转的方式,使得组件间的耦合度降到最低。当组件需要使用其他组件的服务时,不再直接创建依赖对象,而是通过Spring容器来注入。这种方式有以下几个优点:

  • 灵活性高 :可以在运行时动态地替换依赖实现,易于在不同的环境下使用不同的依赖实现。
  • 可重用性强 :组件不再依赖于特定的依赖实现,便于重用。
  • 便于测试 :可以轻松地为组件提供模拟的依赖实现,便于进行单元测试。
4.3.2 提高代码的可测试性和可维护性

依赖注入能够提高代码的可测试性和可维护性,因为:

  • 便于单元测试 :依赖注入使得我们可以为被测试的组件提供模拟依赖,而不需要依赖实际的实现。
  • 代码结构清晰 :组件的依赖关系在配置文件或注解中明确指出,阅读代码时一目了然。
  • 易于重构 :修改组件的依赖关系时,只需要修改配置文件或注解,而无需改动代码逻辑。
// 示例代码:使用模拟依赖进行单元测试
public class MyServiceTest {

    @Test
    public void testDependencyUsage() {
        Dependency mockDependency = mock(Dependency.class);
        when(mockDependency.someMethod()).thenReturn("Mocked Result");
        MyService service = new MyService();
        service.setDependency(mockDependency);
        assertEquals("Mocked Result", service.useDependency());
    }
}

在该测试案例中,我们使用了Mockito框架来模拟 Dependency 类的行为,使得 MyService 类在测试中可以使用模拟的依赖,而不需要真实的依赖实现。

依赖注入不仅简化了组件的配置和管理工作,而且提高了代码的灵活性和可维护性,是现代企业级应用中不可或缺的重要组成部分。

5. 面向切面编程(AOP)在Spring Context中的应用

面向切面编程(Aspect-Oriented Programming,AOP)是Spring框架中一个重要的特性,它能够将系统中散落各处的交叉关注点(如日志、安全等)从业务逻辑中分离出来,以增加模块的内聚性,简化企业级应用的开发和维护。

5.1 AOP基础理论

5.1.1 AOP的概念和术语

在深入Spring AOP之前,让我们先了解一下AOP的核心概念和术语:

  • 切面(Aspect) :一个关注点的模块化,这些关注点可能会横切多个对象。事务管理是Spring AOP的一个很好的例子。
  • 通知(Advice) :切面在特定连接点采取的动作。不同类型的通知包括前置通知(@Before)、后置通知(@After)、返回通知(@AfterReturning)、异常通知(@AfterThrowing)和环绕通知(@Around)。
  • 连接点(Join Point) :在程序执行过程中某个特定的点,如方法调用或异常抛出。在Spring AOP中,连接点总是表示方法的执行。
  • 切点(Pointcut) :匹配连接点的谓词。通知与切点表达式关联,并在匹配的连接点上运行(例如,指定带有特定注解的方法)。
  • 引入(Introduction) :允许我们向现有的类添加新方法或属性。

5.1.2 Spring AOP的工作原理

Spring AOP使用代理模式来实现AOP。这意味着当创建一个代理对象来包装目标对象时,代理将拦截目标对象的方法调用,并将调用转发给相应的通知。对于方法拦截,Spring AOP默认使用JDK动态代理或CGLIB代理来创建代理实例。

5.2 AOP编程实践

5.2.1 AspectJ和@Aspect注解的使用

在Spring框架中,我们可以使用AspectJ的注解来定义切面。首先,需要在配置中启用注解驱动的AOP支持。这里是一个简单的例子:

@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
    // 配置类内容
}

然后创建切面:

@Aspect
@Component
public class LoggingAspect {

    @Before("execution(* com.example.service.*.*(..))")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Before method: " + joinPoint.getSignature().getName());
    }
    // 其他通知的实现...
}

5.2.2 创建和管理切面

创建切面后,我们还需要在应用程序上下文中声明它们,以便Spring容器管理。上面的例子已经通过 @Component 注解实现了这一点。此外,我们还可以使用 @Aspect 注解来表示一个类是一个切面。

切面的创建和管理包括定义切点,指定通知,并控制它们的执行顺序。可以利用 @Order 注解来控制切面的优先级。优先级更高的切面会先于其他切面执行。

5.3 AOP在企业级应用中的优势

5.3.1 横切关注点的解耦

通过使用AOP,横切关注点(cross-cutting concerns)如日志记录、事务管理和安全控制可以与业务逻辑分离。这不仅提高了代码的可读性,还增强了模块的独立性。

5.3.2 事务管理和安全控制的实例分析

在企业级应用中,事务管理和安全控制是常见横切关注点。使用AOP,开发者可以在不修改业务逻辑代码的前提下,集中管理这些关注点。

以事务管理为例:

@Aspect
@Component
public class TransactionAspect {

    @Around("execution(* com.example.service.*.*(..))")
    public Object manageTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
        // 开启事务逻辑
        Object result = joinPoint.proceed(); // 执行业务逻辑
        // 提交事务逻辑
        return result;
    }
}

上面的代码段展示了如何创建一个切面来管理事务,它包围了目标对象的所有业务方法。通过 @Around 通知,我们可以控制方法的执行前后进行事务的开启和提交操作。

AOP的这一特性使得企业应用易于维护和扩展,因为将业务逻辑与事务控制代码分离,使得代码结构更清晰,也容易理解和测试。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:"bp-support-spring-context"项目为Spring Context框架提供增强支持,聚焦企业级应用开发。Spring Context是Spring框架核心,涉及配置管理、bean生命周期管理、依赖注入、AOP、资源访问、事件传播和国际化支持。该项目可能提供了扩展功能,如自定义ApplicationContext子类、新的bean后处理器、增强的AOP切面、自定义配置元数据解析,以及框架间集成适配器。了解Spring Context的工作原理对于使用此项目至关重要。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值