1、Spring简介与基于XML的IoC装配

一、Sping简介

1、概述

1、Spring是一个轻量级Java开发框架,最早有Rod Johnson创建,目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。
2、Spring是一个面向对象设计层面的开发框架(基本上都是运行在后台),其本身提供有一个完善的设计容器,利用此容器可以帮助开发者实现对象的管理、线程同步处理、依赖关系的配置等。
3、Spring官网
4、官方项目和文档
5、Spring的归档文档
6、 Spring的官方Github

2、Spring框架特点

1、非侵入式:基于Spring开发的应用中的对象可以不依赖与Spring的API
2、控制反转:IoC(Inversion of control),指的是将对象的创建权交由Spring框架来管理,并由容器根据配置文件去创建实例和管理各个实例之间的依赖关系。在使用Spring之前,对象的创建都是在代码中new创建。
3、依赖注入:DI(Dependency Injection),应用程序在运行时依赖IoC容器来动态注入对象需要的外部资源。依赖的对象不需要手动调用setXXX方法去设置值,通过配置自动赋值。
4、容器:Spring负责创建和管理对象(Bean)的生命周期和配置。
5、面向切面编程:AOP(Aspect Oriented Programming),能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可扩展性和可维护性。
6、组件化:Spring实现了使用简单的组件配置组合成一个复杂的应用。在Spring中可以使用XML和Java注解组合这些对象。

3、Spring体系结构

在这里插入图片描述

1、Code Container(Spring的核心容器):
  • Beans:提供了框架的基础部分,包括控制反转和依赖注入。
  • Core:封装了Spring框架的底层部分,包括资源访问、类型转换及一些常用工具类。
  • Context:上下文模块,建立在Core和Beans模块的基础上,它以一种类似于JNDI注册的方式访问对象。集成Beans模块功能并添加资源绑定、数据验证、国际化、Java EE支持、容器生命周期、事件传播等,ApplicationContext接口是上下文模块的焦点。
  • Expression:提供了强大的表达式语言支持,支持访问和修改属性值,方法调用,支持访问及修改数组、容器和索引器,命名变量,支持算数和逻辑运算,支持从Spring容器获取Bean,它也支持列表投影、选择和一般的列表聚合等。
2、Data Access/Integration(数据访问/集成):
  • JDBC:提供了一个JDBC的样例模板,使用这些模板能消除传统冗长的JDBC编码还有必须的事务控制,而且能享受到Spring管理事务的好处。
  • ORM:提供与流行的“对象-关系”映射框架无缝集成的API,如:JPA、Hibernate和MyBatis等。而且还可以使用Spring事务管理,无需额外控制事务。
  • OXM:提供了一个支持Object/XML映射的抽象层实现,如JAXB、Castor、XMLBeans、JiBX和XStream。将Java对象映射成XML数据,或者将XML数据映射成Java对象。
  • JMS:消息模块,提供一套 “消息生产者、消息消费者”模板用于更加简单的使用JMS,JMS用于用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。
  • Transactions:事务模块,支持编程和声明式事务管理。
3、Web模块:
  • Web:为Spring提供了最基础的Web支持,主要建立在核心容器之上,通过Servlet或者Listeners来初始化IoC容器,也包含一些与Web相关的支持。
  • WebMVC:是一个Web-Servlet模块,实现了Spring MVC(Model-View- Controller)的Web应用。
  • WebSocket:与Web前端进行全双工通信的协议。
  • WebFlux:是一个新的非堵塞函数式Reactive Web框架,可以用来建立异步的、非阻塞的、事件驱动的服务,并且扩展性非常好。
4、AOP、Aspects、Instrumentation和Messaging模块:
  • AOP:提供了面向切面编程实现,提供比如日志记录、权限控制、性能统计等通用功能和业务逻辑分离的技术,并且能动态的把这些功能添加到需要的代码中,这样各司其职,降低业务逻辑和通用功能的耦合。
  • Aspects:提供与AspectJ的集成,是一个功能强大且成熟的面向切面编程(AOP)框架。
  • Instrumentation:提供了类工具的支持和类加载器的实现,可以在特定的应用服务器中使用。
  • Messaging:Spring 4.0以后新增了消息(Spring-messaging)模块,该模块提供了对消息传递体系结构和协议的支持。
5、Test模块:Spring支持Junit和TestNG测试框架,而且还额外提供了一些基于Spring的测试功能,比如在测试Web框架时,模拟Http请求的功能。

二、Spring简单示例搭建

1、创建一个Maven工程

1、新建一个Project

在这里插入图片描述

2、填写好信息,点击结束

在这里插入图片描述

2、引入Spring的POM依赖

<?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">
    <parent>
        <artifactId>Learning</artifactId>
        <groupId>com.itan</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>spring-core</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <spring-framework.version>5.2.7.RELEASE</spring-framework.version>
    </properties>

    <dependencies>
        <!--spring 核心组件所需依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring-framework.version}</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

</project>

3、新建一个实体类(Bean)类

public class User {
    private String name;

    private String sex;

    private Integer age;

    public String getName() {
        return name;
    }

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

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

4、Spring配置文件

1、在resources目录下新增一个配置文件,命名建议spring-config.xml或者applicationContext.xml,这就是Spring的核心配置文件。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--添加bean标签-->
    <!--id属性:名字(唯一)-->
    <!--class属性:类的全限定名-->
    <bean id="user" class="com.itan.beans.User"/>
</beans>

5、测试

public class Test1 {
    public static void main(String[] args) {
        // 通过配置文件创建容器对象
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        /**
         * 通过容器获取对象,返回的是object类型,可以强转,bean的名称一定要和xml中id属性值对应,
         * 否则报错:org.springframework.beans.factory.NoSuchBeanDefinitionException
         */
        User user = (User) context.getBean("user");
        // 此方法直接返回对应的类型的对象
        User user1 = context.getBean("user", User.class);
        /**
         * 注意:使用这种方式获取bean时,必须保证当前spring的配置文件中,只能有一个<bean/>的class是要获取的类型,
         * 否则报错:org.springframework.beans.factory.NoUniqueBeanDefinitionException
         */
        User user2 = context.getBean(User.class);
        System.out.println(user);
        System.out.println(user1);
        System.out.println(user2);
    }
}
/**
 * 运行结果如下:
 * com.itan.beans.User@57baeedf
 * com.itan.beans.User@57baeedf
 * com.itan.beans.User@57baeedf
 */

三、IoC容器

1、IoC概述

1、Spring的核心机制就是IoC(Inversion of Control),即“控制反转”或“反转控制”,就是将原本在程序中手动创建对象的权力,交由Spring框架来管理,并由容器根据配置文件去创建实例和管理各个实例之间的依赖关系,在需要的时候进行依赖注入。对象与对象之间松散耦合。
2、交给Spring管理的对象被称为Bean,Bean对象存放的地方被称为IoC容器,或者Bean工厂
3、最直观的意思:IoC让对象的创建不用去new了,可以由Spring自动生产,使用Java的反射机制,根据配置文件在运行时动态的创建对象和管理对象,并调用对象的方法。

2、Spring Bean

1、当配置好需要交给IoC管理的对象,并启动IoC容器,就可以从IoC容器中获取我们需要的对象了;其中,交给IoC容器管理的对象称为Bean,或者说Bean是由IoC容器进行实例化、组装和管理的对象。
2、Spring IoC容器管理一个或多个Bean。这些Bean是用我们提供给容器的配置元数据创建的,配置元数据可以通过以下几种形式来表示:
  • XML:最原始的Spring配置方式,现在很少用,被下面两种方式取代。
  • 注解:从Spring 2.5开始引入了对配置元数据的注解,比如:@Autowired、@PostConstruct、@PreDestroy等。
  • Java配置:可以通过Java代码配合注解使用,比如@Configuration、@Bean、@Import、@DependsOn等注解
3、在Spring IoC容器本身中,我们定义的配置元数据创建的Bean对象会转为BeanDefinition对象,BeanDefinition会记录下所有解析到的Bean定义,将解析结果保存起来的好处就是此后就不至于每次用到配置信息的时候都去解析一遍配置元数据。BeanDefinition中包含以下数据:
  • 包限定类名:通常是被定义的Bean的实际实现类。
  • Bean行为的配置元素:声明Bean在容器中的作用范围、生命周期回调函数等。
  • 对其他Bean的引用:Bean可能需要引用其他的Bean来完成它的工作,这些引用也称为协作者或依赖关系。
  • 在新创建的对象中设置的其他配置设置:例如,在管理连接池的Bean中使用的连接数,或连接池的大小限制。

3、通过XML装配Bean

1、将Bean的信息配置.xml文件里,通过Spring加载文件为我们创建Bean。这种方式出现很多早前的SSM项目中,将第三方类库或者一些配置工具类都以这种方式进行配置,主要原因是由于第三方类不支持Spring注解。
2、优缺点说明:
  • 优点:可以使用于任何场景,结构清晰,通俗易懂
  • 缺点:配置繁琐,不易维护,枯燥无味,扩展性差
3、通过<bean />标签在xml文件中配置Bean,常用的属性配置说明如下:
  • id:Bean在spring容器的唯一标识,spring容器中不可能同时存在两个相同的id。
  • class:类的全限定名(包名+类名),用“.”号连接
  • name:设置Bean的别名,可以有多个,用“,”或“;”或空格来区分多个name,不同Bean的name和id都不能一样
  • scope:Bean的作用范围,BeanFactory作为容器时取值单例(singleton)和原型(prototype),默认是单例。
  • lazy-init:Bean的实例化时机,是否延迟加载,设置只对scope属性为singleton的Bean起作用,非单例不实例化;false表示立即加载,在spring启动时,立刻进行实例化;true表示延迟加载,设置为true的Bean不会在ApplicationContext启动时提前被实例化,而是在第一次向容器通过getBean获取Bean时实例化的。
  • init-method:在实例化一个Bean之后调用的方法,初始化方法必须是一个无参方法
  • destory-method:在Bean实例销毁前调用的方法,只对scope=“singleton”的Bean起作用,并且方法的参数个数不能超过1,并且参数类型只能为boolean。
  • autowire:设置自动注入模式,常用的有按照类型byType,按照名字byName
  • factory-beanfactory-method:指定由哪个工厂哪个方法完成Bean的创建。
4、上面目录二中的示例代码中就是基于XML装配Bean。

4、通过Java配置装配Bean

1、将类的创建交给我们配置的JavcConfig类来完成,Spring只负责维护和管理,采用纯Java创建方式。其本质上就是把在XML上的配置声明转移到Java配置类中。
2、优缺点说明:
  • 优点:适用于任何场景,配置方便,因为是纯Java代码,扩展性高,十分灵活
  • 缺点:由于是采用Java类的方式,声明不明显,如果大量配置,可读性比较差
3、实现步骤:
  • 创建一个配置类, 添加@Configuration注解声明为配置类。
  • 创建方法,方法上加上@Bean,该方法用于创建实例并返回,该实例创建后会交给spring管理,方法名建议与实例名相同(首字母小写)
  • 注意:实例类不需要加任何注解。
/**
 * @Date: 2022/10/28
 * bean配置类
 */
@Configuration
public class BeanConfig {
    @Bean
    public User user() {
        return new User();
    }
}
public class Test2 {
    public static void main(String[] args) {
        // 通过配置类创建容器对象
        ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
        /**
         * 获取容器中的bean,name值要与配置中的方法名保持一致,
         * 否则报错:org.springframework.beans.factory.NoSuchBeanDefinitionException
         */
        User user = context.getBean("user", User.class);
        System.out.println(user);
    }
}

5、通过注解装配Bean

1、通过在类上加注解的方式,来声明一个类交给Spring管理,Spring会自动扫描带有@Component,@Controller,@Service,@Repository这四个注解的类,然后帮我们创建并管理,前提是需要先配置Spring的注解扫描器。
2、优缺点说明:
  • 优点:开发便捷,通俗易懂,方便维护。
  • 缺点:具有局限性,对于一些第三方资源,无法添加注解。只能采用XML或JavaConfig的方式配置。
3、实现步骤:
  • 对类添加@Component相关的注解,比如@Controller、@Service、@Repository
  • 设置ComponentScan的basePackage扫描包,有如下三种方式:
  • XML配置扫描的包:<context:component-scan base-package='com.itan.beans'>
  • 注解设置扫描的包:@ComponentScan("com.itan.beans")
  • 代码设置扫描的包:new AnnotationConfigApplicationContext("com.itan.beans")
package com.itan.beans;

import org.springframework.stereotype.Component;

/**
 * @Date: 2022/10/23
 */
@Component
public class User {
    private String name;

    private String sex;

    private Integer age;

    public String getName() {
        return name;
    }

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

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
public class Test3 {
    public static void main(String[] args) {
        // 通过配置类创建容器对象,basePackages配置要扫描的包
        ApplicationContext context = new AnnotationConfigApplicationContext("com.itan.beans");
        // 获取容器中的bean
        User user = context.getBean("user", User.class);
        System.out.println(user);
    }
}

四、依赖注入(DI)

1、概述

1、在实际应用中,Bean对象不可能是单一的,往往一个Bean对象,还会依赖很多其他的Bean的属性。这就有了依赖注入的概念,由DI来维护Bean之间的依赖关系,并且自动注入需要的依赖项,使用DI之后,具有依赖关系的对象之间没有了强耦合关系,对象不需要主动查找、获取其依赖项,甚至都不用知道依赖项的具体位置,它们都在容器中,都交给DI就行。
2、DI(Dependency Injection)即依赖注入,可以说是IoC的其中一个内容,在容器实例化对象的时候主动的将被调用者(或者说它的依赖对象)注入给调用对象。也可以理解为:应用程序在运行时依赖IoC容器来动态注入对象需要的外部资源
3、对象可以通过构造函数参数、工厂方法的参数或从工厂方法构造或返回对象实例后在其上设置属性来定义其依赖(要使用)的其他对象。随后,IoC容器在创建Bean时会自动注入这个Bean的依赖项。这个过程基本上和Bean主动通过类的构造器和setter方法来设置其依赖项的过程是相反的,因此DI也称为控制反转(Inversion of Control、IoC)
4、常用的注入方式主要有三种:
  • 构造方法注入(Construct注入)
  • setter方法注入
  • 基于注解的注入(接口注入)

2、构造方法注入

1、构造器依赖注入是由IoC容器调用带有许多参数的构造器来完成的,每个参数表示一个依赖项。
2、使用构造器依赖注入方式,需要依赖<bean />标签的子标签<constructor-arg />,一个<constructor-arg />标签表示一个属性。
3、<constructor-arg />标签参数介绍:<constructor-arg value="" index="" name="" ref="" type="" />
属性说明
value设置参数列表参数对应的值,用于设定基本数据类型和String类型的数据
index通过参数在参数列表中的索引找到参数列表中对应参数,index从0开始
name通过参数名找到参数列表中对应参数
ref如果参数值为非基本数据类型,则可通过ref为参数注入值,其值为另一个bean标签id或name属性的属性值
type通过参数数据类型找到参数列表中对应参数
public class Subject {
    private String name;

    private String teacher;

    public Subject(String name, String teacher) {
        this.name = name;
        this.teacher = teacher;
        System.out.println("构造器依赖注入");
    }

    @Override
    public String toString() {
        return "Subject{" +
                "科目='" + name + '\'' +
                ", 老师='" + teacher + '\'' +
                '}';
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--Subject类配置-->
    <bean id="subject" class="com.itan.beans.Subject">
        <!--构造函数属性注入,constructor-arg表示一个属性,value表示值-->
        <constructor-arg value="Java" />
        <constructor-arg value="张三" />
    </bean>
</beans>
public class Test4 {
    public static void main(String[] args) {
        // 通过配置文件创建容器对象
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        // 获取bean对象
        Subject bean = context.getBean(Subject.class);
        System.out.println(bean);
    }
}
/**
 * 运行结果:
 * 构造器依赖注入
 * Subject{科目='张三', 老师='Java'}
 */

3、setter方法注入

1、setter依赖注入是由IoC容器调用参数的setter方法完成,setter方法是在调用构造器以实例化bean之后完成的。
2、setter方法实际上就是常说的get、set方法中的set方法,因此在使用setter注入的时候需要提供setXXX方法,这个XXX一般就是表示属性名,方法名应该按照Java方法名的规定来定义,属性的第一个字母大写,通常我们可以使用idea来自动生成set方法。
3、使用setter方法注入方式,需要依赖<bean />标签的子标签<property />,一个<property />标签表示一个setter方法。
4、<property />标签参数介绍:<property name="" value="" ref="" />
属性说明
name属性名称
value设置属性对应的值
ref如果参数值为非基本数据类型,则可通过ref为参数注入值,其值为另一个bean标签id或name属性的属性值
public class Teacher {
    private String name;

    private int age;

    public void setName(String name) {
        System.out.println("setter属性注入name");
        this.name = name;
    }

    public void setAge(int age) {
        System.out.println("setter属性注入age");
        this.age = age;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "姓名='" + name + '\'' +
                ", 年龄=" + age +
                '}';
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--Teacher类配置-->
    <bean id="teacher" class="com.itan.beans.Teacher">
        <!--setter方法属性注入,property表示一个属性,name表示属性名,value表示对应的值-->
        <property name="name" value="张三" />
        <property name="age" value="25" />
    </bean>
</beans>
public class Test4 {
    public static void main(String[] args) {
        // 通过配置文件创建容器对象
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        // 获取bean对象
        Teacher teacher = context.getBean(Teacher.class);
        System.out.println(teacher);
    }
}
/**
 * 运行结果:
 * setter属性注入name
 * setter属性注入age
 * Teacher{姓名='张三', 年龄=25}
 */
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值