Spring

Spring

依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.0.RELEASE</version>
</dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.0.RELEASE</version>
        </dependency>

优点

  • 轻量级、非入侵式(不会对已有的项目造成影响)
  • 控制反转(IOC),面向切面编程(AOP)
  • 支持事务的处理 支持多框架的整合

IOC控制反转

image-20200801101114140

由原本把对象的创建和对象间的依赖方式写死,变为把对象的创建交给第三方决定,这就是控制反转

配置

配置文件

<!--在resources文件夹下创建beans.xml文件-->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context-3.1.xsd
                        http://www.springframework.org/schema/mvc
                        http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">

</beans>

对象是Spring创建的,对象的属性值是Spring容器设置的

需求 需要方便满足用户的要求

<!--在resources文件夹下创建beans.xml文件-->
<?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="aUser" class="com.rush.po.User">
        <property name="id" value="1"/>
        <property name="username" value="mc"/>
    </bean>
    <bean id="UserDaoImpl" class="com.rush.dao.UserDaoImpl"/>
    <bean id="UserDaoMysqlImpl" class="com.rush.dao.UserDaoMysqlImpl"/>
    <bean id="UserDaoOracleImpl" class="com.rush.dao.UserDaoOracleImpl"/>
    <!--传入Oracle的实现对象 以后需要修改直接改beans文件即可-->
    <bean id="UserServiceImpl" class="com.rush.service.UserServiceImpl">
        <property name="userDao" ref="UserDaoOracleImpl"/>
    </bean>

</beans>

别名

<alias name="user" alias="userNew"/>
<!--多个别名 1 2 3 4都是user2的别名-->
<alias name="user2" alias="userNew,1 2;3,4"/>

import

​ 把多个beans.xml合并为一个

<?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">

    <import resource="beans.xml"/>
    <import resource="beans1.xml"/>
    <import resource="beans2.xml"/>
</beans>

依赖注入

构造器注入

<bean id="UserServiceImpl" class="com.rush.service.UserServiceImpl">
        <property name="userDao" ref="UserDaoOracleImpl"/>
    </bean>

set注入

<bean id="teachcer" class="com.rush.po.Teacher">
<bean id="student" class="com.rush.po.Student">
	<!--set普通注入-->
    <property name="name" value="rush"/>
    <!--Bean注入引用对象-->
    <property name="teacher" value="teacher"/>
    <!--数组注入-->
    <property name="books">
    <array>
        <value>红楼梦</value>
        <value>水浒传</value>
        </array>
    </property>
    <!--list注入-->
    <property name="hobbys">
    <list>
        <value>歌曲</value>
        <value>游戏</value>
        </list>
    </property>
    <!--Map-->
    <property name="car">
    <map>
        <entry key="" value=""/>
        </map>
    </property>
    <!--Set-->
    <property>
    <set>
        <value>LOL</value>
        <value>WOW</value>
        </set>
    </property>
    <!--空值-->
    <property name="wife">
    		<null/>
    </property>
    <!--properties-->
    <property name="info">
    	<props>
        <prop key="学号">311900xxxx</prop>
            <prop key="姓名">rush</prop>
        </props>
    </property>
</bean>

空间注入

  • p:set注入
  • c:构造器注入
<!--p 先加约束 xmlns:p="http://www.springframework.org/schema/p"-->
<bean id="user" class="com.rush.po.User" p:name="rush" p:age="18"/>

<!--c 先加约束 xmlns:p="http://www.springframework.org/schema/c"-->
<!--需要无参和有参构造器-->
<bean id="user" class="com.rush.po.User" c:name="rush" c:age="18"/>

作用域

单例

Spring默认使用

<bean id="user" class="com.rush.po.User" c:name="rush" c:age="18" scope="singleton"/>

原型

每次都是一个新对象

<bean id="user" class="com.rush.po.User" c:name="rush" c:age="18" scope="prototype"/>

剩余

  • request
  • session
  • application
  • websocket

Bean自动装配

byName

id要和实体类一样才能找到

<bean id="cat" class="com.rush.po.Cat"/>
<bean id="dog" class="com.rush.po.Dog"/>
<!--把cat和dog对象自动放入有dog和cat的People实体类对象中-->
<bean id="People" class="com.rush.po.People" autowire="byName"/>

byType

对应的类型必须全局唯一

<bean id="cat1" class="com.rush.po.Cat"/>
<bean id="dog1" class="com.rush.po.Dog"/>
<!--把cat和dog对象自动放入有dog和cat的People实体类对象中-->
<bean id="People" class="com.rush.po.People" autowire="byType"/>

注解自动装配

@Autowired先找type再找name

@Autowired(required = false)//设置required的值使得属性可以为空 否则为空会报异常
@Qualifier(value="dogRush") //手动设置对应的xml中的name
private Cat cat;

导入约束

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
          	         http://www.springframework.org/schema/beans/spring-beans.xsd 	
                   http://www.springframework.org/schema/context
                   http://www.springframework.org/schema/context/spring-context.xsd"
       <context:annotation-config/> 	
       </beans>				
       	>

@Resource注解

//先找name再找type
@Resource
private Cat cat;
@Resource(name = "dogdog")
private Dog dog;

注解开发

先导入aop

<!--打开包扫描-->
<context:component-scan base-package=""/>

装配类并注入值

@Component
public class User{
 	  @Value("rush")
    public String name;
}

衍生的注解

功能与@Componet相同

  • Dao:@Repository
  • Service:@service
  • controller:Controller

@Scope,类似配置文件中的scope,可设置使用单例模式、原型模式等

注解与xml配合

一般用xml注册类,注解赋值

Java配置类

​ JavaConfig是Spring的一个子项目,在Spring4之后成为了一个核心功能。

//配置类
package com.rush.config;

import com.rush.po.User;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

/**
 * @author:zmc
 * @function:
 * @date: 2020/8/2 11:30
 */
@Component
public class Config {
        @Bean
        public User getUser(){
            return new User();
        }
}
//实体类
package com.rush.po;

import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

/**
 * @author:zmc
 * @function:
 * @date: 2020/8/1 10:35
 */
@Data
public class User {
    @Value("666")
    private int id;
    @Value("弟弟")
    private String username;

}
//测试类
package com.rush;

import com.rush.config.Config;
import com.rush.po.User;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @author:zmc
 * @function:
 * @date: 2020/8/1 10:43
 */
public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext config
                = new AnnotationConfigApplicationContext(Config.class);
        User user = (User) config.getBean("getUser");
        System.out.println(user);
    }
}

这种纯Java,不使用xml的模式就是SpringBoot的模式。

AOP

面试必问AOP和SpringMVC

底层:代理模式

AOP就是横向开发 不改变原有代码

image-20200802131956089

原生态实现AOP

使用原生态的Spring API接口

运行前增强

package com.rush.log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

/**
 * @author:zmc
 * @function:
 * @date: 2020/8/2 14:17
 */
public class Log implements MethodBeforeAdvice {
    /**
     *
     * @param method 要被执行的方法
     * @param args 参数
     * @param target 目标对象
     * @throws Throwable
     */
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
    }
}

运行后增强

package com.rush.log;

import org.springframework.aop.AfterAdvice;
import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

/**
 * @author:zmc
 * @function:
 * @date: 2020/8/2 14:27
 */
public class AfterLog implements AfterReturningAdvice {
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了"+method.getName()+"方法,执行结果为:"+returnValue);
    }
}

原接口

package com.rush.service;

import com.rush.dao.UserDao;
import com.rush.dao.UserDaoImpl;
import com.rush.po.User;
import org.springframework.context.annotation.Scope;

import java.util.Comparator;
import java.util.PriorityQueue;

/**
 * @author:zmc
 * @function:
 * @date: 2020/8/1 11:18
 */
public class UserServiceImpl implements UserService {

    @Override
    public void add() {
        System.out.println("添加了一个用户");
    }
}

切入点配置

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:annotation-config/>
    <bean id="userService" class="com.rush.service.UserServiceImpl"/>
    <bean id="log" class="com.rush.log.Log"/>
    <bean id="after" class="com.rush.log.AfterLog"/>



    <aop:config>
        <!--第一个*表示返回的类型 *表示所有类型-->
        <!--包名-->
        <aop:pointcut id="pointcut" expression="execution(* com.rush.service.UserServiceImpl.*(..))"/>
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="after" pointcut-ref="pointcut"/>
    </aop:config>
</beans>

测试类

package com.rush;

import com.rush.config.Config;
import com.rush.po.User;
import com.rush.service.UserService;
import com.rush.service.UserServiceImpl;
import org.aspectj.apache.bcel.util.ClassPath;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author:zmc
 * @function:
 * @date: 2020/8/1 10:43
 */
public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //动态代理代理的是接口
        UserService userService = (UserService) context.getBean("userService");
        userService.add();
    }
}

image-20200802145201100

自定义类实现AOP

自定义类

package com.rush;

/**
 * @author:zmc
 * @function:
 * @date: 2020/8/2 14:55
 */
public class DiyPointCut {
    public void before(){
        System.out.println("--------------方法执行前--------------");
    }
    public void after(){
        System.out.println("--------------方法执行后--------------");
    }
}

切面配置

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:annotation-config/>
    <bean id="diy" class="com.rush.DiyPointCut"/>
    <bean id="userService" class="com.rush.service.UserServiceImpl"/>


    <aop:config>
        <!--自定义切面-->
        <aop:aspect ref="diy">
        <!--切入点-->
        <aop:pointcut id="point" expression=
                "execution(* com.rush.service.UserServiceImpl.*(..))"/>
        <aop:before method="before" pointcut-ref="point"/>
        <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>
</beans>

测试类

package com.rush;

import com.rush.config.Config;
import com.rush.po.User;
import com.rush.service.UserService;
import com.rush.service.UserServiceImpl;
import org.aspectj.apache.bcel.util.ClassPath;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author:zmc
 * @function:
 * @date: 2020/8/1 10:43
 */
public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //动态代理代理的是接口
        UserService userService = (UserService) context.getBean("userService");
        userService.add();
    }
}

image-20200802150637889

注解实现AOP

切面类

package com.rush;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;


/**
 * @author:zmc
 * @function:
 * @date: 2020/8/2 15:09
 */
@Aspect //标注这个类是一个切面
public class AnnotationPointCut {
    @Before("execution(* com.rush.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("--------方法执行前--------");
    }

    @After("execution(* com.rush.service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("--------方法执行后--------");
    }

    @Around("execution(* com.rush.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp){
        System.out.println("--------环绕前--------");
        Signature signature = jp.getSignature();
        System.out.println(signature);
        try {
            jp.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("--------环绕后--------");
    }
}

配置注解

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:annotation-config/>
    <bean id="annotationPointCut" class="com.rush.AnnotationPointCut"/>
    <bean id="userService" class="com.rush.service.UserServiceImpl"/>
    <!--开启注解支持-->
    <aop:aspectj-autoproxy/>
</beans>

image-20200802152633954

Spring Aop 默认使用JDK实现 可以改为cglib

<!--默认为false 即JDK 改为true即cglib-->
<aop:aspectj-autoproxy proxy-target-class="true"/>

整合Mybatis

SqlSessionTemplate需要使用构造器注入,因为没有set方法。

编写数据源 sqlSession

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--用Spring的数据源代替Mybatis的配置-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost/mybatis?
            useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=GMT%2B8&amp;useSSL=false"/>
            <property name="username" value="root"/>
            <property name="password" value="root"/>
        </bean>
    <!--配置原来Mybatis的sqlSessionFactory-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource"/>
            <!--绑定Mybatis配置文件-->
            <property name="configLocation" value="classpath:mybatis-config.xml"/>
            <property name="mapperLocations" value="classpath:com/rush/mapper/*.xml"/>
        </bean>
        <!--SqlSessionTemplate就是我们使用的sqlSession-->
        <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
            <constructor-arg index="0" ref="sqlSessionFactory"/>
        </bean>
    <bean id="userMapper" class="com.rush.mapper.UserMapperImpl">
        <property name="sqlSessionTemplate" ref="sqlSession"/>
    </bean>
</beans>

加实现类

package com.rush.mapper;

import com.rush.po.User;
import org.mybatis.spring.SqlSessionTemplate;

import java.util.List;

/**
 * @author:zmc
 * @function:
 * @date: 2020/8/2 23:08
 */
public class UserMapperImpl implements UserMapper
{
        private SqlSessionTemplate sqlSession;

    public void setSqlSessionTemplate(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }

    @Override
    public List<User> selectUser() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}

Mybatis扫描包

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <typeAliases>
        <package name="com.rush.po"/>
    </typeAliases>
</configuration>

Mapper配置

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.rush.mapper.UserMapper">
    <select id="selectUser" resultType="user">
        select * from mybatis.user
    </select>
</mapper>

测试类

package com.rush;

import com.rush.mapper.UserMapper;
import com.rush.po.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author:zmc
 * @function:
 * @date: 2020/8/2 22:59
 */
public class test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
        for (User user : userMapper.selectUser()) {
            System.out.println(user);
        }
    }
}

继承SqlSessionDaoSupport

精简了原本的实现类

package com.rush.mapper;

import com.rush.po.User;
import org.mybatis.spring.support.SqlSessionDaoSupport;

import java.util.List;

/**
 * @author:zmc
 * @function:
 * @date: 2020/8/3 11:08
 */
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {

    @Override
    public List<User> selectUser() {
        return getSqlSession().getMapper(UserMapper.class).selectUser();
    }
}

写完记得去Spring配置文件

<!--可省略sqlSessionTemplate-->    
<bean id="userMapper2" class="com.rush.mapper.UserMapperImpl2">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

声明式事务

  • 把一组业务当作一个业务来做,要么都成功,要么都失败
  • 事务在实际开发中非常重要

事务ACID原则

原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)

  • 原子性:要么同时成功,要么同时失败
  • 一致性:事务开始之前和结束之后,关系数据的完整性和业务逻辑的一致性

A给B转账 A和B的存款总数是不会变化的

  • 隔离性:要防止多个事务同时操作一个资源时数据错误
  • 持久性:事务一旦提交,数据就会被持久化地写入存储器,不会再改变

配置

	<!--配置声明式事务-->    
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <constructor-arg ref="dataSource"/>
    </bean>
<!--配置事务通知-->
 <tx:advice id="TestAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!--配置事务传播特性-->
            <tx:method name="add" propagation="REQUIRED"/>
            <tx:method name="delete" propagation="REQUIRED"/>
            <tx:method name="update" propagation="REQUIRED"/>
            <tx:method name="query" read-only="true"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
<!--配置事务切入-->
  <!--配置切入面-->
    <aop:config>
       <aop:pointcut id="txPointCut" expression="execution(* com.rush.mapper.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
    </aop:config>

注意

导入tx依赖时 idea会出错 需要修改

xmlns:tx="http://www.springframework.org/schema/cache"
<!--改为下面这个才行-->
xmlns:tx="http://www.springframework.org/schema/tx"

Spring七大传播属性

  • REQUIRED:支持当前事务,如果没有事务,就新建一个事务。一般使用这个
  • SUPPORTS:支持当前事务,如果没有事务,就以非事务方式执行
  • MANDATORY:支持当前事务,如果没有事务,就抛出异常
  • REQUIRES_NEW:新建事务,如果已存在事务,则将当前事务挂起
  • NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,则将当前事务挂起
  • NEVER:以非事务方式执行,如果已存在事务,则抛出异常
  • NESTED:支持当前事务,如果已存在事务,则执行一个嵌套事务,如果没有事务,则新建事务。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值