Spring的学习


零、这个知识重要吗?有什么用?

Spring是一切的基础。


一、Spring概述

(1.1)Spring是什么?

是一个轻量级开源的 JavaEE 框架,用来解决企业应用开发的复杂性。

  • 轻量级:需要依赖的文件少,小,可以单独使用。
  • 开源:源代码开放。
  • 框架:让开发更加方便、简洁。

(1.2)Spring 两个核心:IOC 以及 AOP

  • IOC(控制翻转):将创建对象的过程交给 Spring 进行管理。
  • AOP(面向切面):可以不修改源代码的情况下,对代码功能进行增强。

(1.3)Spring 入门框架

(1)添加依赖

<dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</artifactId>
                <version>4.3.7.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-beans</artifactId>
                <version>4.3.7.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>4.3.7.RELEASE</version>
            </dependency>
            <!-- 2.Spring dao依赖-->
            <!-- spring-jdbc包括了一些如jdbcTemplate的工具类-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>4.3.7.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-tx</artifactId>
                <version>4.3.7.RELEASE</version>
            </dependency>
            <!-- 3.Spring web依赖 -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-web</artifactId>
                <version>4.3.7.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>4.3.7.RELEASE</version>
            </dependency>
            <!-- 4.Spring test依赖:方便做单元测试和集成测试 -->
            <dependency>
	            <groupId>org.springframework</groupId>
	            <artifactId>spring-test</artifactId>
	            <version>4.3.7.RELEASE</version>
            </dependency>
			<!-- lombok -->
			<dependency>
	            <groupId>org.projectlombok</groupId>
	            <artifactId>lombok</artifactId>
	            <version>1.18.20</version>
        	</dependency>
			<!-- junit 依赖-->
		        <dependency>
		            <groupId>junit</groupId>
		            <artifactId>junit</artifactId>
		            <version>4.12</version>
		
		        </dependency>
		        <dependency>
		            <groupId>junit</groupId>
		            <artifactId>junit</artifactId>
		            <version>4.12</version>
		            <scope>test</scope>
		        </dependency>
        </dependencies>

(2)创建对象,创建 spring-config.xml文件,在配置文件中创建 bean 标签,将对象类加载到配置文件中。
(3)创建测试类,通过ClassPathXmlApplicationContext获取context对象,再获取对象。

public class UserTest {
    @Test
    public void test() {
        ClassPathXmlApplicationContext context = 
        new ClassPathXmlApplicationContext("spring-config.xml");
        User user = context.getBean("user", User.class);
        user.doThing();
    }
}

二、IOC

(2.1)什么是IOC

IOC(控制反转)是一种设计原则,在对象创建时,通过一个调控系统将对象的引用传递给它,目的是减少耦合。


(2.2)IOC底层原理

使用到了 XML解析、工厂模式、反射。

IOC实现解耦合:XML配置文件配置对象,创建工厂类,通过反射创建对象。


(2.3)BeanFactory 接口

1、IOC 思想基于 IOC 容器完成,IOC 容器底层就是对象工厂

2、Spring 提供 IOC 容器实现两种方式:(两个接口)

  • BeanFactory:IOC 容器基本实现,是 Spring 内部的使用接口,不提供开发人员进行使用
    特点:加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象

  • ApplicationContext:BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人
    员进行使用
    特点:加载配置文件时候就会把在配置文件对象进行创建

ApplicationContext 实现类

		// 类路径加载
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
		// 磁盘路径加载
        FileSystemXmlApplicationContext context1 = new FileSystemXmlApplicationContext();

(2.4)Bean 作用域

Bean 作用域指的就是在 IOC 容器中,Bean 是单例还是多例的。
单例(默认):所有请求由一个对象处理。
多例:多个请求由多个对象进行处理。

<bean> 标签中,修改 scope = "protorype" 可以将 Bean 修改成多例的,此时不再是加载配置文件时进行加载对象,而是在调用 getBean() 时,创建一个新的对象。


(2.5)FactoryBean

Spring 中有两种类型的 Bean ,普通Bean 以及 工厂Bean

  • 普通Bean

    • 定义的类型与实际返回的类型相同
  • 工厂Bean

    • 配置文件中定义的类型和实际返回的类型可以不同,目的是从Spring的角度实现创建不同的Bean对象。
    • (1)实现 FactoryBean 接口
    • (2)覆写方法
public class UserFactory implements FactoryBean<User> {
    /**
     * 如果是单例实例,那么创建的 Bean 会放入Spring容器的单实例缓存池中
     * @return FactoryBean 创建的 Bean
     * @throws Exception
     */
    @Override
    public User getObject() throws Exception {
        return new User();
    }

    /**
     * @return FactoryBean 创建 Bean 的类型
     */
    @Override
    public Class<?> getObjectType() {
        return User.class;
    }

    /**
     * 确定 FactoryBean 创建的 Bean 实例是否为单例的
     * @return 是否为单例对象
     */
    @Override
    public boolean isSingleton() {
        return false;
    }
}
@Test
    public void test() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        User bean = context.getBean("userFactory", User.class);

        bean.user();
    }

(2.6)Bean 的生命周期

(2.6.1)默认五步生命周期

  • (1)无参构造创建
  • (2)注入属性(set方法)
  • (3)初始化(需要配置方法)
  • (4)使用
  • (5)销毁(手动,需要配置方法)

(2.6.2)加入后置处理器以后

  • (1)无参构造创建
  • (2) 注入属性(set方法)
  • (3)后置处理器
  • (4)初始化(需要配置方法)
  • (5)后置处理器
  • (6)使用
  • (7)销毁(手动,需要配置方法)

(2.6.3)配置初始化、销毁方法

  • <bean>标签中有 init-method 以及 destroy-method
  • 将Bean类中方法名赋值给它们就可以完成初始化、销毁方法的定义
  • 注意的是销毁需要使用手动调用销毁 context.close()

(2.6.4)实现后置处理器

  • 创建类实现 BeanPostProcessor
  • 重新方法before after,添加后置处理器逻辑
  • 在配置文件中进行配置,当配置文件配置了后置处理器后,会为全部的 Bean 对象添加后置处理器。

(2.7)Bean 管理

Bean管理指的是两个操作,Spring 创建对象,Spring 注入属性。

(2.7.1) 基于 XML 文件进行 Bean 管理

(2.7.1.1) 创建对象

在 Spring 配置文件中,通过 <Bean> 标签进行创建以及配置属性,创建时使用无参构造函数进行创建。

  • id:唯一标识
  • name:唯一标识,相比于 ID ,可以添加特殊符号
  • class:对象所在的包类路径
(2.7.1.2)注入属性(DI依赖注入)

核心是 set 方法
<Bean>标签中,添加<property>标签,底层会将属性名首字母大写,添加前缀set调用对应的方法。

  • name :属性名
  • value:属性值

P标签注入
本质还是使用 set 方法进行注入,使用命名空间简化了属性,在<Bean> 标签中,添加p:属性名=属性值


有参构造注入
使用<comstructor-arg>标签调用对象的构造方法进行注入,要求拥有对应的构造方法。

  • name:属性名
  • value:属性值

注入的属性为 null 或者含有特殊符号

  • null
<property name="args">
	<null/>
</property>
  • 特殊符号
<property name="args">
	<value>  <![CDATA[<<属性值>>]]>  </value>
</property>

外部 Bean 的引用注入
—个Bean标签 A 引用了另外—个Bean标签 B,B是在A外进行注册的,那么A就称为引入外部Bean B

<property name="B" ref="对象B在Spring配置文件中的唯一标识">

内部Bean注入以及级联属性

—个Bean标签A引用了另外一个Bean标签B,B是在A内部进行注册的,那么A就称为引入内部 Bean
使用<property>标签,在<property>标签内进行<bean>的注册,同时这个bean也能配置属性,就称为级联属性

集合注入,以及集合添加元素
在这里插入图片描述


XML 自动装配
自动装配指的是根据装配规则,Spring 自动将属性进行匹配注入,在需要自动装配的 Bean 中添加属性 autowire,底层是调用了对应的 Set 方法。

  • ByName:根据属性名匹配对应的 Bean
  • ByType:根据属性类型匹配对应的 Bean

— XML外部属性文件
需要引入 Context 命名空间,使用<context:property-placeholder>标签,它是一个单标签,引入外部文件,使用 ${key} 进行取值。

xmlns:context="http://www.springframework.org/schema/context"
  • location:声明文件地址
<context:property-placeholder location="classpath:database.properties" />
    <bean name="user" class="com.wuqiyong.pojo.User">
        <property name="" value="${jdbc.username}"/>
    </bean>

(2.7.2) 基于注解进行 Bean 管理

(2.7.2.1)创建对象

1、在对象上进行Bean的声明,告诉 Spring 哪些类需要进行创建,提供了多个注解帮助对象的创建,是规范。

  • @Component 普通 Bean
  • @Service Service 层
  • @Controller Controller 层
  • @Repository Dao 层

2、开启组件扫描

XML形式:
<context:component-scan base-package="com.wuqiyong"></context:component-scan>

配置扫描范围

设置扫描的范围

<context:component-scan base-package="com.wuqiyong" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

排除扫描的范围

<context:component-scan base-package="com.wuqiyong">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
(2.7.2.2)注入属性
  • @Autowired 用于注释属性,根据属性联系进行自动装配,内部封装了 Set 方法。
  • @Qualifier 配合@Autowired使用,当注入属性有多个实现类时,使用@Qualifier(value = "类名")指定实现类进行注入。
  • @Resource 根据类型或者名称进行注入,当使用 name 属性时,就根据名称进行注入,否则根据联系,是 JavaX 提供的注解。
  • @Value 注入普通联系的属性,使用 value 属性进行赋值,会将它转换为注入属性的联系,如果不能转换,将抛出异常。

(2.8)注解类,获取外部文件,开启扫描

  • @Configuration 表示当前类为注解类
  • @PropertySource 引入外部文件,使用 ${key} 进行取值
  • @ComponentScan 开启组件扫描,使用basePackages 声明扫描位置
@PropertySource(value = "classpath:database.properties")
@Configuration
@ComponentScan(basePackages = {"com.wuqiyong"})
public class MyConfig {
	使用 key 来获取外部文件的值
}

使用 AnnotationConfigApplicationContext(ConfigClass.class) 加载配置文件


三、AOP

(3.1)什么是AOP,起到什么作用,怎么实现的?

  • AOP是面向切面编程,利用AOP可以使业务逻辑各个部分进行分离,使得耦合性降低。
  • 将不属于业务逻辑的代码从业务中划分出来,即在不修改源代码的情况下,在主干功能里面添加新的功能。
  • 是通过创建一个代理对象,在代理对象中添加逻辑代码从而起到增强的作用。

(3.2)AOP术语

  • 连接点 在一个类中,哪些方法可以被增强,那么这个方法就称为连接点。
  • 切入点 在一个类中,实际被增强的方法称为切入点
  • 通知(增强) 对主体代码进行补充的逻辑代码部分称为通知(增强)
    在这里插入图片描述
  • 切面指的是一个动作,把通知应用到切入点的过程称为切面

(3.3)AOP 动态代理的两种情况

  • 当有接口时,使用 JDK 动态代理 创建接口实现类的代理对象,来进行增强方法。
  • 没有接口时,使用 CGLIB 动态代理 创建当前类的子类,通过子类的代理对象,来进行增强方法。

(3.4)使用 JDK 代理实现 AOP 操作

  • Proxy.newProxyInstance(arg0, arg1, arg2) 得到一个代理对象
    方法参数解释:
    • 类加载器
    • 需要增强方法所在类实现的接口
    • 代理对象类,需要实现 InvocationHandler里面写增强方法
public interface User {
    void user();
}

class UserImpl implements User {
    @Override
    public void user() {
        System.out.println("userImpl");
    }
}

class UserPlus implements InvocationHandler {
    private Object object;

    public UserPlus(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("before");

        Object invoke = method.invoke(object, args);

        System.out.println("after");

        return invoke;
    }
}

class JDKProxy {
    public static void main(String[] args) {
        Class[] interfaces = new Class[]{User.class};

        UserImpl user = new UserImpl();

        User userPlus = (User) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserPlus(user));

        userPlus.user();
    }
}

(3.4)使用 Spring & AspectJ 实现 AOP操作

Spring 框架一般都是基于 AspectJ 实现 AOP 操作

AspectJ 是独立的AOP框架,将 spring 和 AspectJ 一起实现AOP操作

(3.4.1)切入表达式

切入表达式就是声明类中哪个方法需要进行增强
语法结构 execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]))

  • 权限修饰符:* 代表全部权限
  • 返回联系:可以省略
  • 类全路径 . 方法名:代表增强当前方法,* 代表全部方法
  • 参数列表:可以使用 .. 省略

(3.4.2)实现 AOP 操作

1、添加依赖

	<dependency>
	    <groupId>org.springframework</groupId>
	    <artifactId>spring-aspects</artifactId>
	    <version>5.2.7.RELEASE</version>
	</dependency>
	
	<dependency>
	    <groupId>org.aspectj</groupId>
	    <artifactId>aspectjtools</artifactId>
	    <version>1.9.5</version>
	</dependency>
	
	<dependency>
	    <groupId>aopalliance</groupId>
	    <artifactId>aopalliance</artifactId>
	    <version>1.0</version>
	</dependency>
	
	<dependency>
	    <groupId>org.aspectj</groupId>
	    <artifactId>aspectjweaver</artifactId>
	    <version>1.9.0</version>
	</dependency>
	
	<dependency>
	    <groupId>cglib</groupId>
	    <artifactId>cglib</artifactId>
	    <version>3.3.0</version>
	</dependency>

2、代码实现

添加到配置文件中,开启 aspectj 代理

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
public class AspectJ {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");

        Emp emp = context.getBean("emp", Emp.class);

        emp.emp();

    }
}

@Component
class Emp {
    public void emp() {
        System.out.println("Emp !!!");
    }
}

@Component
@Aspect
class EmpPlus {

    // 提取相同的切入点

    @Pointcut(value = "execution(* com.wuqiyong.Review.Emp.emp(..))")
    public void pointCut() {
    }

    //前置通知
    //@Before 注解表示作为前置通知
    @Before(value = "pointCut()")
    public void before() {
        System.out.println("before.........");
    }
    //后置通知(返回通知)
    @AfterReturning(value = "pointCut()")
    public void afterReturning() {
        System.out.println("afterReturning.........");
    }
    //最终通知
    @After(value = "pointCut()")
    public void after() {
        System.out.println("after.........");
    }
    //异常通知
    @AfterThrowing(value = "pointCut()")
    public void afterThrowing() {
        System.out.println("afterThrowing.........");
    }
    //环绕通知
    @Around(value = "pointCut()")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws
            Throwable {
        System.out.println("环绕之前.........");
        //被增强的方法执行
        proceedingJoinPoint.proceed();
        System.out.println("环绕之后.........");
    }
}

3、使用注解类实现

public class AspectJ {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AspectJConfig.class);
        Emp emp = context.getBean("emp", Emp.class);
        emp.emp();
    }
    
}

@Component
class Emp {
    public void emp() {
        System.out.println("Emp !!!");
    }
}

@ComponentScan(basePackages = {"com.wuqiyong.Review"})
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
class AspectJConfig {
}

@Component
@Aspect
class EmpPlus {

    // 提取相同的切入点

    @Pointcut(value = "execution(* com.wuqiyong.Review.Emp.emp(..))")
    public void pointCut() {
    }

    //前置通知
    //@Before 注解表示作为前置通知
    @Before(value = "pointCut()")
    public void before() {
        System.out.println("before.........");
    }
    //后置通知(返回通知)
    @AfterReturning(value = "pointCut()")
    public void afterReturning() {
        System.out.println("afterReturning.........");
    }
    //最终通知
    @After(value = "pointCut()")
    public void after() {
        System.out.println("after.........");
    }
    //异常通知
    @AfterThrowing(value = "pointCut()")
    public void afterThrowing() {
        System.out.println("afterThrowing.........");
    }
    //环绕通知
    @Around(value = "pointCut()")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws
            Throwable {
        System.out.println("环绕之前.........");
        //被增强的方法执行
        proceedingJoinPoint.proceed();
        System.out.println("环绕之后.........");
    }
}

四、JDBCtemplate

JDBCemplate 是 Spring 框架对 JDBC 进行的封装
1、导入依赖

		<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.3</version>
        </dependency>
		<dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.37</version>
        </dependency>

2、配置数据库连接池

<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="driverClassName" value="${jdbc.driver}" />
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="druidDataSource"/>
    </bean>

3、编写代码

  • update 增删改
  • queryForObject 查询一个对象,包括特殊值查询,使用 BeanPropertyRowMapper 封装自定义类。
  • query 查询多个对象,使用 BeanPropertyRowMapper 封装自定义类。

五、事务管理

事务是添加到 Service 层的,在 Spring 中有编程型事务(通过编程)以及声明型事务(通过配置文件),声明型事务又可以通过注解以及 XML 文件实现,底层使用了 AOP。

1、创建事务管理器

<bean id="transactionManager" 
	class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	 <!--注入数据源-->
	 <property name="dataSource" ref="dataSource"></property>
</bean>

2、开启事务注解

  • 引入名称空间 tx
  • 开启事务注解
<!--开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager">
</tx:annotation-driven>

3、在 Service 类或方法上添加 @Transactional

  • @Transactional 添加到类上,该类所有方法开启事务,添加到方法上,当前方法开启事务。

    @Transactional 中有多个重要属性

    • propagation 事务传播行为,即当多个方法进行调用时,有普通方法以及事务方法,那么应该怎么处理。

      • REQUIRED 如果有事务在运行,那么当前方法就在这个事务内运行,否则,就创建一个新的事务,并在自己的事务内运行。
      方法 A 调用 方法 BA 有事务时,B 方法使用 A 方法中的事务
      	当 A 没有事务时,创建一个新的事务
      	AB任意⼀个⽅法异常(默认是RuntimeExceptionError)都会导致AB的操作被回滚。
      
      • REQUIRED_NEW 当前方法必须启动新事务,并在自己的事务中内运行,如果有事务正在运行,应该将它挂起。
      	方法 A 调用 方法 B
      		如果 A 存在一个事务,那么会将 A 的事务挂起
      		新建一个属于 B 的事务,等 B 的事务执行完成以后才会执行 A 的事务
      	如果 A 发生异常,只会回滚 A 不会回滚 B 
      	如果 B 发生异常 B 一定会进行回滚,抛出异常给 A ,如果 A catch 住了,那么 A 不会回滚,否则 A 也会回滚。
      

在这里插入图片描述

ioslation 事务隔离级别

  • Isolation.READ_COMMITTED
  • Isolation.REPEATABLE_READ

timeout 超时时间

  • 设置事务需要在一定的时间内进行提交,否则进行回滚,默认值为 -1 单位是秒。

readOnly 是否只读

  • false 增删查改
  • true 只能查询

rollbackFor noRollbackFor 当出现哪些异常时回滚/不回滚

使用注解类实现银行转账功能

public class Transaction {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class);
        MyService myService = context.getBean("myService", MyService.class);
        myService.transfer();
    }
}

@Service
@Transactional
class MyService {

    @Autowired
    Dao dao;

    public void transfer() {
        dao.transfer1();

        System.out.println(1 / 0);

        dao.transfer2();
    }
}

@Repository
class Dao {

    @Autowired
    JdbcTemplate jdbcTemplate;

    public void transfer1() {
        String sql = "update user_table set balance = balance - 100 where user = ?";

        jdbcTemplate.update(sql, "AA");

    }

    public void transfer2() {
        String sql = "update user_table set balance = balance + 100 where user = ?";
        jdbcTemplate.update(sql, "BB");
    }


}

@Configuration
@ComponentScan(basePackages = {"com.wuqiyong"})
@EnableTransactionManagement
@PropertySource(value = "classpath:database.properties")
class TxConfig {
    
    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.driver}")
    private String driver;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;



    @Bean
    public DruidDataSource getDruidDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl(url);
        dataSource.setDriverClassName(driver);
        dataSource.setUsername(username);
        dataSource.setPassword(password);

        return dataSource;
    }


    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);

        return jdbcTemplate;
    }


    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource);

        return dataSourceTransactionManager;
    }
}

六、整合 log4j

1、引入依赖
2、创建 log4j2.xml


指导复习的问题


  • 使用 JDK 代理实现 AOP 操作
  • 使用 AspectJ 实现 AOP 操作
  • 使用 JDBCtemplate 对数据库进行增删查(一个对象,多个对象,记录条数)改
  • 使用事务管理实现转账功能

容易犯的错误

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值