框架:Spring使用

目录

Spring框架概述

IOC/DI概述

IOC 容器:

一. 基于xml配置:

DI 依赖注入:

1.   构造函数注入

2.  set方法注入

3 其他写法

注入集合

4. 自动装配 

二. 使用注解实现

案例开发步骤:

AOP编程

XML方式实现AOP编程:

注解方式实现AOP编程



Spring框架概述

可以解决对象创建以及对象之间依赖关系的一种框架。

且可以和其他框架一起使用:Spring与Struts, Spring与hibernate(起到整合/粘合作用的一个框架)


Spring 架构

1) Spring Core          Spring的核心功能: IOC容器, 解决对象创建及依赖关系
2) Spring AOP           切面编程
3) Spring DAO          Spring 对jdbc操作的支持 【JdbcTemplate模板工具类】
4) Spring ORM          Spring对orm的支持:
    -->  既可以与hibernate整合,【session】
    -->  也可以使用spring的对hibernate操作的封装
5) Spring Web          Spring对web模块的支持:建立在上下文模块之上
    --> 可以与struts整合,让struts的action创建交给spring
    --> spring mvc模式
6) Spring EE            Spring 对javaEE其他模块的支持


组件/框架设计
侵入式设计

引入了框架,对现有的类的结构有影响;即需要实现或继承某些特定类。
例如: Struts框架

非侵入式设计

引入了框架,对现有的类结构没有影响。
例如:Hibernate框架 / Spring框架

1.控制反转 IOC Inversion on Control ):把对象的创建交给外部容器完成,这个就做控制反转

控制反转是一种设计思想,不是技术  .  控制了对象对外部资源的获取

2.依赖注入 DI (dependency injection):处理对象的依赖关系

控制反转, 解决对象创建的问题 【对象创建交给别人】

依赖注入:在创建完对象后, 对象的关系的处理就是依赖注入 【通过set方法依赖注入】

传统的项目: A类依赖B类, 就会自己去创建B类对象, 

spring IOC把对象的创建都交给容器, B对象由IOC容器创建,然后DI依赖注入给A类

3.AOP 切面

面向切面编程。简单来说来可以理解为把很多重复代码抽出来形成的类。

切面举例:事务、日志、权限;

 

 



IOC/DI概述

使用场景:

A对象依赖B对象,B对象依赖C对象,C对象依赖D对象

要创建A对象就得new B,要new B就得new C,要new C就得new D......

  • 问题1:依赖对象要多次创建

使用反射 单例模式 或者 工厂模式 统一创建对象,统一管理 (控制反转)

  • 问题2:每个对象内部都要创建它的依赖对象,依赖关系复杂,

不用内部自己创建依赖对象,从外部传入依赖对象(依赖注入)

 

 

spring核心内容。作用: 创建对象 & 处理对象的依赖关系

原理: 把对象的信息写在配置文件, 读取配置文件,然后用反射创建对象


IOC 容器

BeanFactory 容器

流程: 开发一个javabean, 然后配置bean的参数,  就可以在类中通过spring获取bean的对象,调用bean的方法

配置bean元数据三种方法: 

  1. 基于 XML 的配置文件
  2. 基于注解的配置
    1. @Component @ComponentScan
    2. @Bean
    3. @Import
  3. 基于 Java 的配置

FactoryBean, 创建bean的工厂

一. 基于xml配置:

创建一个xml配置文件,

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="......" xmlns:xsi="......" xsi:schemaLocation="......">
<!-- 一个简单的bean定义 -->
   <bean id="bean的别名" class="javabean文件的包名.类名">
       <!-- bean的配置 -->
   </bean>
<!-- 设置了lazy init的bean定义 -->
   <bean id="..." class="..." lazy-init="true">
       <!-- bean的配置 -->
   </bean>

<!-- 生命周期 -->
    <!-- 具有初始化方法的bean定义 -->
   <bean id="..." class="..." init-method="方法名">
       <!-- bean的配置 -->
   </bean>
    <!-- 具有销毁方法的bean定义 -->
   <bean id="..." class="..." destroy-method="方法名">
       <!-- bean的配置 -->
   </bean>

<!-- 作用域 -->
    <!-- 单例作用域的bean定义 -->
   <bean id="..." class="..." scope="singleton">
       <!-- bean的配置 -->
   </bean>
    <!-- 原型作用域的bean定义 -->
   <bean id="..." class="..." scope="prototype">
       <!-- bean的配置 -->
   </bean>

<!-- 继承 父定义的配置数据 -->
   <bean id="..." class="..." parent="父bean">
       <!-- bean的配置 -->
   </bean>
</beans>
属性描述
class必输,指定用来创建 bean 的 java类。
id / name别名, 指定唯一的 bean 标识符。id是唯一的,, name可以不唯一
scope指定由特定的 bean 定义创建的对象的作用域,
constructor-arg注入依赖关系,通过构造函数注入
properties注入依赖关系,通过setxx方法注入
autowiring mode

注入依赖关系,自动装配

lazy-initialization mode懒汉单例模式, 延迟初始化 bean , IoC 容器在它第一次被请求时而不是在启动时去创建bean 实例。
initialization 方法在 bean 的所有必需的属性被容器设置之后,调用回调方法。
destruction 方法当包含该 bean 的容器被销毁时,使用回调方法。
作用域描述
singleton

单例模式: 在spring IoC容器仅存在一个Bean实例,默认值

prototype原型模式: 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean() 
requestWEB应用: 每次HTTP请求都会创建一个新的Bean
sessionWEB应用: 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean
global-sessionWEB应用: 一般用于Portlet应用环境

在方法中读取配置文件,获取bean 

public class Main {
   public static void main(String[] args) {
      ApplicationContext context = new ClassPathXmlApplicationContext("xml配置文件");
      A a = (A) context.getBean("bean的id");
   }
}

 

工厂类对象spring管理

// 创建工厂类ObjectFactory ,作用创建对象
public class ObjectFactory {
	// 实例方法创建对象
	public User getInstance() {
		return new User(100,"工厂:调用实例方法");
	}
	// 静态方法创建对象
	public static User getStaticInstance() {
		return new User(101,"工厂:调用静态方法");
	}
}
<!-- 工厂类的xml配置 -->
	<!-- 先创建工厂 -->
	<bean id="factory" class="包名.ObjectFactory"></bean>
	<!-- 再创建user对象 -->
	<!-- # 3.1 工厂类,实例方法 -->
	<bean id="user3" factory-bean="factory" factory-method="getInstance"></bean>
	<!-- # 3.2 工厂类: 静态方法 -->
	<bean id="user4" class="包名.ObjectFactory" factory-method="getStaticInstance"></bean>

DI 依赖注入:

给对象的属性赋值

在xml文件中配置 各个bean的依赖关系, 然后在java代码中用ClassPathXmlApplicationContext直接获取对象

依赖注入3/4种方式: 其实原理都是通过 赋值方法 或 属性反射 赋值

  1. 通过构造函数传参  (依赖关系无变化的场景,尽量采用)
  2. 通过set方法给属性传参  (简单直观,大量使用)
  3. 属性反射 field.set()
  1. 静态工厂方法
  2. 实例工厂方法

例子: 有3个类, 用spring处理他们的依赖关系

  • 主函数:需要创建A类对象
  • A类:依赖B类对象
  • B类:普通类,无其他依赖关系

1.   构造函数注入

A类 通过构造函数获取 B类对象 与其他参数
/*-----------------------------------------------------------------------*/
public class A {
   private B b;
   private int year;
   private String name;
   //A类构造函数 获取B对象及year和name参数
   public A(B b, int year, String name) {
      this.b = b;
      this.year = year;
      this.name = name;
   }
   public void b() {
      b.Method();
   }
}

基于构造函数注入的xml配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="..." xmlns:xsi="..." xsi:schemaLocation="...">
   <!-- a bean 传参数 -->
   <bean id="a" class="A类路径">
      <constructor-arg index="0" ref="b"/>
      <constructor-arg index="1" type="int" value="2019"/>
      <constructor-arg index="2" type="java.lang.String" value="Allen"/>
   </bean>
   <!-- b bean 无参数 -->
   <bean id="b" class="B类路径">
   </bean>
</beans>

把一个引用传递给一个对象要使用标签的 ref 属性,而如果你要直接传递一个值使用 value 属性。  

2.  set方法注入

A类 通过get,set方法获取 B类对象 与其他参数
/*-----------------------------------------------------------------------*/
public class A {
   private B b;
   private int year;
   private String name;
   public void setB(B b) {
      this.b = b;
   }
   public SpellChecker getB() {
      return b;
   }
   ......year,name,set(),get()方法
   public void b() {
      b.Method();
   }
}

于set函数注入的xml配置

只改〈constructor-arg〉标签就行
   <bean id="a" class="A类路径">
      <property name="b" ref="b"/>
      <property name="year" value="2019"/>
      <property name="name" value="Allen"/>
   </bean>
   <bean id="b" class="B类路径">
   </bean>

基于构造函数注入和基于set函数注入配置文件 唯一的区别就是 用的是〈bean〉标签中的〈constructor-arg〉元素还是〈property〉元素

3 其他写法

P名称空间

如果有许多set方法, 那么在 XML 配置文件中使用 p-namespace 是非常方便

   <bean id="a" class="A类路径"
      p:b-ref="b"
      p:name="Allen"
      p:year="2019"/>
   </bean>

内部bean的注入

使用内部bean,相当于局部变量  就不用单独创建一个B类的bean标签了
   <bean id="a" class="A类路径">
      <property name="b">
         <bean id="b" class="B类路径"/>
       </property>
   </bean>

注入集合

public class JavaCollection {
   List list;
   Set  set;
   Map  map;
   Properties prop;

   set...
   get...
   ......
}
   <bean id="javaCollection" class="JavaCollection类路径">
      <property name="list">
         <list>
            <value>1</value>
            <ref bean="beanName"/>
            <property name="email" value=""/> 空字符串
            <property name="email"><null/></property> NULL值
         </list>
      </property>
      <property name="set">
         <set>
            <value>1</value>
            <ref bean="beanName"/>
        </set>
      </property>
      <property name="map">
         <map>
            <entry key="1" value="1"/>
            <entry key="2" value-ref="beanName"/>
         </map>
      </property>
      <property name="prop">
         <props>
            <prop key="one">1</prop>
            <prop key="two">2</prop>
         </props>
      </property>
   </bean>

 

4. 自动装配 

Spring 容器可以在不使用<constructor-arg><property> 元素的情况下自动装配相互协作的 bean 之间的关系

依赖查找:byType byName

Spring提供的自动装配主要是为了简化配置; 但是不利于后期的维护。(一般不推荐使用)

限制描述
重写的可能性你可以使用总是重写自动装配的 <constructor-arg>和 <property> 设置来指定依赖关系。
原始数据类型你不能自动装配所谓的简单类型包括基本类型,字符串和类。
混乱的本质自动装配不如显式装配精确,所以如果可能的话尽可能使用显式装配。

使用<bean>元素的 autowire 属性

模式描述
no这是默认的设置,它意味着没有自动装配,你应该使用显式的bean引用来连线。你不用为了连线做特殊的事
byName由属性名自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byName。然后尝试匹配,并且将它的属性与在配置文件中被定义为相同名称的 beans 的属性进行连接。
byType由属性数据类型自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byType。然后如果它的类型匹配配置文件中的一个确切的 bean 名称,它将尝试匹配和连接属性的类型。如果存在不止一个这样的 bean,则一个致命的异常将会被抛出。
constructor类似于 byType,但该类型适用于构造函数参数类型。如果在容器中没有一个构造函数参数类型的 bean,则一个致命错误将会发生。
autodetectSpring首先尝试通过 constructor 使用自动装配来连接,如果它不执行,Spring 尝试通过 byType 来自动装配。

 

4.1)根据名称自动装配:【autowire="byName"】(自动去IOC容器中找与属性名同名的引用的对象,并自动注入)

将A的属性与配置文件中定义为相同名称的 beans 进行匹配和连接。如果找到匹配项,它将注入这些 beans,否则,它将抛出异常
   <bean id="a" class="A类对象" autowire="byName">
      <!-- 不用写b类的bean了,根据名字自动装配 -->
      <property name="year" value="2019"/>
      <property name="name" value="Allen"/>
   </bean>

      也可以定义到全局beans中【default-autowire="byName"】,就不用在每个bean节点中配置了

4.2)根据类型自动装配:【autowire="byType"】和属性名没关系,但必须确保改类型在IOC容器中只有一个对象;否则报错。

   <bean id="a" class="A类地址" autowire="byType">
      <!-- 不用写b类的bean了,根据TYPE自动装配 -->
      <property name="year" value="2019"/>
      <property name="name" value="Allen"/>
   </bean>

4.3)构造函数自动装配:【autowire="constructor"】与byType非常相似, 把它构造函数的参数与配置文件中 beans 名称中的一个进行匹配和连线。如果找到匹配项它会注入这些 bean,否则它会抛出异常。

   <bean id="a" class="A类地址" autowire="constructor">
      <!-- 不用写b类的bean了,根据构造函数去自动装配 -->
      <property name="year" value="2019"/>
      <property name="name" value="Allen"/>
   </bean>

Java代码使用

主方法 通过spring获取A对象,用A对象调用B类方法
/*-----------------------------------------------------------------------*/
public class Main {
   public static void main(String[] args) {
      ApplicationContext context = new ClassPathXmlApplicationContext("xml配置文件");
      A a = (A) context.getBean("a");
      a.b();
   }
}
B类
/*-----------------------------------------------------------------------*/
public class B{
   public B(){
      //B类构造函数
   }
   public void Method() {
      //B类方法
   } 
}

二. 使用注解实现

使用注解,可以简化配置,使用相关类方法或字段声明的注解,将 bean 配置移动到组件类本身。

使用注解步骤:

5.1)注解连线在默认情况下在 Spring 容器中不打开 .  在xml配置中:引入context名称空间;开启注解扫描;

<beans ......>
   <!-- 需要先配置Xml注册Bean, 让已经注册的bean开始工作 -->
   <context:annotation-config/>

   <!-- 不需要注册其, 作用是告诉Spring,bean都放在这个包下, 优先级大于annotation-config -->
   <context:component-scan base-package="包名(扫描此包名下的所有类)"></context:component-scan>

</beans>

5.2)在java文件中使用注解通过注解的方式:可把对象加入ioc容器,或从ioc容器中取出对象注入。

@Required  用于 bean 属性的 setter 方法,

@Autowired   可以应用到 bean 属性的 setter 方法,非 setter 方法,构造函数和属性。

@Qualifier   通过指定确切的将被连线的 bean,

@Autowired 和 @Qualifier 注解可以用来删除混乱。

基于java相关的注解:
@Component      指定把一个类加入IOC容器表明此类是bean的来源

@Bean   将带有 @Bean 注解的方法名称作为 bean 的 ID, 创建并返回实际的 bean

@import  从另一个配置类中加载 @Bean 定义

@Resource      从IOC容器中找出属性注入


案例开发步骤:

spring各个版本中:
在3.0以下的版本,源码有spring中相关的所有包【spring功能 + 依赖包】
在3.0以上的版本,源码中只有spring的核心功能包【没有依赖包】(如果要用依赖包,需要单独下载!)

例:创建javabean:User(id,name)

1) 导入源码, jar文件

spring-framework-3.2.5.RELEASE
commons-logging-1.1.3.jar                      日志
spring-beans-3.2.5.RELEASE.jar                bean节点
spring-context-3.2.5.RELEASE.jar              spring上下文节点
spring-core-3.2.5.RELEASE.jar                   spring核心功能
spring-expression-3.2.5.RELEASE.jar         spring表达式相关表
注:以上是必须引入的5个jar文件,在项目中可以用用户库管理!
 

2) 创建核心xml配置文件
Spring配置文件:name.xml
通过bean的方式把对象加入IOC

name.xml 

<beans xmlns=...>
<!-- IOC容器的配置: 要创建的所有的对象都配置在这里 -->
  <bean id="user" class="javabean文件的包名.类名" init-method="init_user" destroy-method="destroy_user" scope="singleton" lazy-init="false">
    <property name="name" value="张三" />
  </bean>
</beans>

3)调用API

// 1. 通过工厂类得到IOC容器创建的对象
	public void testIOC() throws Exception {
		// 不自己创建对象
		// 把对象的创建交给spring的IOC容器
		Resource resource = new ClassPathResource("配置文件路径/name.xml");
		// 创建容器对象(Bean的工厂), IOC容器 = 工厂类 + name.xml
		BeanFactory factory = new XmlBeanFactory(resource);
		// 得到容器创建的对象
		User user = (User) factory.getBean("user");
	}
	
// *2. (推荐)直接得到IOC容器对象 
	public void testAc() throws Exception {
		// 得到IOC容器对象
		ApplicationContext ac = new ClassPathXmlApplicationContext("配置文件路径/name.xml");
		// 根据id属性从容器中获取bean
		User user = (User) ac.getBean("user");
                    //对象的name值为 "张三"
                    System.out.println(user.getName());
		// 销毁容器对象 
		ac.destroy();
	}

AOP编程

Spring AOP 模块提供拦截器来拦截一个应用程序,当执行一个方法时,你可以在方法执行之前或之后添加额外的功能。

在Spring的面向切面编程AOP思想里面,把功能分为核心业务功能,和周边功能。 
核心业务:比如登陆,增加数据,删除数据都叫核心业务 
周边功能切面】:比如性能统计,日志,事务管理等等 


AOP( aspect object programming)面向切面编程
         
核心业务功能和切面功能分别独立进行开发, 然后把切面功能和核心业务功能 "编织" 在一起,这就叫AOP。
切入点:执行目标对象方法,动态植入切面代码。
         可以通过切入点表达式,指定拦截哪些类的哪些方法; 给指定的类在运行的时候植入切面类代码。

通知描述
前置通知在一个方法执行之前,执行通知。
后置通知在一个方法执行之后,不考虑其结果,执行通知。
返回后通知在一个方法执行之后,只有在方法成功完成时,才能执行通知。
抛出异常后通知在一个方法执行之后,只有在方法退出抛出异常时,才能执行通知。
环绕通知在建议方法调用之前和之后,执行通知。

先引入aop相关jar文件 (aspectj aop优秀组件) 
spring-aop-3.2.5.RELEASE.jar           【spring3.2源码】
aopalliance.jar             【spring2.5源码/lib/aopalliance】
aspectjweaver.jar        【spring2.5源码/lib/aspectj】或【aspectj-1.8.2\lib】
aspectjrt.jar                  【spring2.5源码/lib/aspectj】或【aspectj-1.8.2\lib】

注意:用到spring2.5版本的jar文件,如果用jdk1.7可能会有问题。需要升级aspectj组件,即使用aspectj-1.8.2版本中提供jar文件提供。


XML方式实现AOP编程:

1).核心业务功能  ProductService 

public class ProductService {
    public int doSomeService(){         
        System.out.println("---执行核心业务功能"); 
        return 1;
    }
}

2).日志切面 LoggerAspect

public class Logging {
   //在选定的方法执行之前
   public void beforeAdvice(){
      System.out.println("方法执行前");
   }
   //在选定的方法执行之后
   public void afterAdvice(){
      System.out.println("方法执行后");
   }
   //当任何方法返回时
   public void afterReturningAdvice(Object retVal){
      System.out.println("返回值:" + retVal.toString() );
   }
   //出现异常
   public void AfterThrowingAdvice(IllegalArgumentException ex){
      System.out.println("出现异常: " + ex.toString());   
   }  
   //环绕通知 相当于 前+后
   public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
      System.out.println("环绕执行前:" + joinPoint.getSignature().getName());
      //AOP环绕通知,用来执行核心代码
      Object object = joinPoint.proceed();
      System.out.println("环绕执行后:" + joinPoint.getSignature().getName());
      return object;
   }
}

3). 配置文件 applicationContext.xml

<beans xmlns=...>
    <!-- 业务bean  -->     
    <bean id="service" class="业务类路径"/>

    <!-- 切面aspect  -->     
    <bean id="loggerAspect" class="日志类路径"/>

<!-- aop:config把业务对象与辅助功能编织在一起 -->   
   <aop:config>
      <aop:aspect id="log" ref="loggerAspect">
         <!-- 声明一个切点, 表示对满足以com包开头任意类 任意方法 任意参数 进行切面操作 -->
         <aop:pointcut id="serviceCutpoint" expression="execution(* com.*.*(..))"/>

         <!-- before定义 切点执行前 调用log切面的方法 -->
         <aop:before pointcut-ref="serviceCutpoint" method="beforeAdvice"/>
         <!-- after定义 切点执行后 调用方法-->
         <aop:after pointcut-ref="serviceCutpoint" method="afterAdvice"/>
         <!-- after-returning定义 切点有返回 调用方法-->
         <aop:after-returning pointcut-ref="serviceCutpoint" 
                              returning="retVal"
                              method="afterReturningAdvice"/>
         <!-- after-throwing定义 切点异常 调用方法-->
         <aop:after-throwing pointcut-ref="serviceCutpoint" 
                             throwing="ex"
                             method="AfterThrowingAdvice"/>
         <!-- around定义 环绕通知 前+后 -->
         <aop:around pointcut-ref="serviceCutpoint"  method="doAround"/>
      </aop:aspect>
   </aop:config>
</beans>

 执行顺序是    @Around→@Before→@After→@Around→@AfterRunning (如果有异常→@AfterThrowing)

测试:

public class TestSpring {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"xml配置文件路径"});
        ProductService s = (ProductService) context.getBean("service");
        s.doSomeService();
    }
}

控制台打印:
环绕执行前::doSomeService
方法执行前
---执行核心业务功能
方法执行后
环绕执行后:doSomeService
返回值:1

 


 

注解方式实现AOP编程

1)注解配置业务类

使用@Component("s") 注解ProductService 类

package com.service;
import org.springframework.stereotype.Component;
 
@Component("s")
public class ProductService {
    public void doSomeService(){
        System.out.println("核心业务功能");
    }
}

2)注解配置切面
@Aspect    表示这是一个切面
@Component    表示这是一个bean,由Spring进行管理
@Around(value = "execution(* com.service.ProductService.*(..))") 表示对com.service.ProductService 这个类中的所有方法进行切面操作

package com.aspect;
import ······;
 
@Aspect
@Component
public class LoggerAspect {
     
    @Around(value = "execution(* com.service.ProductService.*(..))")
    public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("start 切面:" + joinPoint.getSignature().getName());
        Object object = joinPoint.proceed();
        System.out.println("end 切面:" + joinPoint.getSignature().getName());
        return object;
    }
}

3)applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans ······>
   <context:component-scan base-package="com.aspect"/>
   <context:component-scan base-package="com.service"/>
<!-- 扫描包com.aspect和com.service,定位业务类和切面类 -->
   <aop:aspectj-autoproxy/>  
<!-- 找到被注解了的切面类,进行切面配置 -->
</beans>

4)测试

public class TestSpring {
  
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext(
                new String[] { "applicationContext.xml" });
        ProductService s = (ProductService) context.getBean("s");
        s.doSomeService();
    }
}
控制台打印:
start 切面:doSomeService
核心业务功能
end 切面:doSomeService

注解详解
@Aspect 指定一个类为切面类
@Pointcut("execution(* cn.itcast.e_aop_anno.*.*(..))") 指定切入点表达式

@Before("pointCut_()") 前置通知: 目标方法之前执行
@After("pointCut_()") 后置通知:目标方法之后执行(始终执行)
@AfterReturning("pointCut_()") 返回后通知: 执行方法结束前执行(异常不执行)
@AfterThrowing("pointCut_()") 异常通知: 出现异常时候执行
@Around("pointCut_()") 环绕通知: 环绕目标方法执行


切入点表达式

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值