Spring学习(一)Spring简介、SpringIOC

一、什么是Spring?
1、简介

Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。

Spring依赖导入:

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>
    </dependencies>
2、Spring的优点
  • Spring是一个开源免费的框架(容器)!
  • Spring是一个轻量级、非入侵式的框架。
  • Spring的两大特点:IOC(控制翻转)和AOP(面向切面编程)!
3、Spring的七大模块

Spring有七大功能模块,分别是:

Spring Core,AOP,ORM,DAO,MVC,WEB,Content。
二、Spring IOC 推导

准备工作:写好各层的接口、实体等

pojo:User

package com.zyh.pojo;

public class User {
    private String name;

    public User() {
        System.out.println("无参构造器");
    }

    public User(String name) {
        this.name = name;
    }

    public void setName(String name) {
        this.name = name;
    }
    public void show(){
        System.out.println("name=" + this.name);
    }
}

dao:UserMapper、UserMapperImp

//UserMapper
package com.zyh.dao;
public interface UserMapper {
    public void getUser();
}

//UserMapperImp
package com.zyh.dao.daoimp;

import com.zyh.dao.UserMapper;

public class UserMapperImp implements UserMapper {
    public void getUser() {
        System.out.println("获取用户数据");
    }
}

Service:UserService、UserServiceImp

//UserService
package com.zyh.service;

public interface UserService {
    public void getUser();
}
//UserServiceImp
package com.zyh.service.serviceimp;

import com.zyh.dao.UserMapper;
import com.zyh.dao.daoimp.UserMapperImp;
import com.zyh.service.UserService;

public class UserServiceImp implements UserService {
    private UserMapper userMapper = new UserMapperImp();
    public void getUser() {
        userMapper.getUser();
    }
}

测试:

package com.zyh.dao;

import com.zyh.dao.daoimp.UserMapperImp;
import com.zyh.dao.daoimp.UserMapperMysqlImp;
import com.zyh.service.UserService;
import com.zyh.service.serviceimp.UserServiceImp;
import org.junit.Test;

public class TestIOC {
    @Test
    public void test1(){
        UserService userService = new UserServiceImp();
        userService.getUser();
    }
}

执行结果:
在这里插入图片描述

这是我们的原生代码,可以获取到当前数据,但是现在如果用户的需求发生了改变,不需要获取当前的数据,而是在接口层新增了一个新的接口UserMapperMysqlIml ,那么我们就需要再去service
层再去修改代码呢,这就不符合我们现在Spring开发的思想,在Spring中我们说怎么去做呢?

  • 修改Service层的代码,增加一个setUserMapper()方法,让用户可以自己设置要获取的数据源。
package com.zyh.service.serviceimp;

import com.zyh.dao.UserMapper;
import com.zyh.dao.daoimp.UserMapperImp;
import com.zyh.service.UserService;

public class UserServiceImp implements UserService {
    private UserMapper userMapper = new UserMapperImp();
    public void getUser() {
        userMapper.getUser();
    }
    //用户自己设置要获取的数据来源
    public void setUserMapper(UserMapper userMapper) {
        this.userMapper = userMapper;
    }
}

  • 用户只需要在测试中直接调用这个setUserMapper的方法就可以了。

测试:

package com.zyh.dao;

import com.zyh.dao.daoimp.UserMapperMysqlImp;
import com.zyh.service.UserService;
import com.zyh.service.serviceimp.UserServiceImp;
import org.junit.Test;

public class TestIOC {
    @Test
    public void test1(){
        UserMapperMysqlImp userMapperMysqlImp = new UserMapperMysqlImp();
        UserService userService = new UserServiceImp();
        //用户自己修改参数改变数据的来源,利用set实现了注入
        ((UserServiceImp) userService).setUserMapper(userMapperMysqlImp);
        userService.getUser();
    }
}

执行结果:
在这里插入图片描述

这两种做法的区别:

  • 在最开始,程序中的是主动创建的,控制权在程序人员的手里。
  • 使用set注入后,程序不在是主动的了,而是变成被动的接收。
什么是IOC?

IOC—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。 如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来分析一下:

  • 谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。
  • 控制反转是一种通过描述(XML配置或注解)并通过第三方去生产或获取对象的方式。在Spring中实现控制反转的是IOC容器,它的实现方法是依赖注入(Dependency Injection)DI。
三、Spring hello,world程序

Spring 中的配置:

通过Maven仓库导入Spring依赖和单元测试依赖

        <!-- https://mvnrepository.com/artifact/junit/junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>

实体类Hello程序,记得编写set方法。

package com.zyh.pojo;

public class Hello {
    private String name;
    public Hello(){
       Stste.out.println("无参构造器");
    }
    public void setName(String name) {
       this.name = name;
    }
    public void show(){
        System.out.println("hello,"+name);
    }
}

编写Spring配置文件:applicationContext.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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>

编写applicationContext.xml配置文件,实现对象注入

    <!--一个bean就是一个对象
    id对应的就是对象的名字
    class就是要注入对象的模板类
    property就是给对象的属性设值-->
    <bean id="hello" class="com.zyh.pojo.Hello">
        <property name="name" value="Spring"/>
    </bean>

测试:

package com.zyh.pojo;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestHelloSpring {
    @Test
    public void test1(){
        //读取Spring的配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取注入对象
        Hello hello = (Hello) context.getBean("hello");
        hello.show();
    }
}

执行结果:
在这里插入图片描述

四、IOC创建对象的方式
1、IOC默认使用无参构造器进行创建对象。
2、IOC使用有参构造创建对象

实体类:

package com.zyh.pojo;

public class User {
    private String name;

    public User() {
        System.out.println("User的无参构造");
    }

    public User(String name) {
        System.out.println("User有参构造");
        this.name = name;
    }

    public void setName(String name) {
        System.out.println("使用了User setName()");
        this.name = name;
    }

    public String getName() {
        return name;
    }
    public void show(){
        System.out.println("name=" + name);
    }
}
方式一:通过下标constructor-arg index=" "给参数赋值
        <bean id="user" class="com.zyh.pojo.User">
            <!--index是参数的下标,0为第一个参数,value为给参数的赋值-->
            <constructor-arg index="0" value="hang"/>
        </bean>
方式二:通过参数类型type给参数赋值,但这种方式存在缺陷,若两个参数类型一样,就不能这样用了。
       <bean id="user" class="com.zyh.pojo.User">
            <constructor-arg type="java.lang.String" value="JoJo"/>
       </bean>
方式三:使用参数名给参数赋值
        <bean id="user" class="com.zyh.pojo.User">
            <constructor-arg name="name" value="Fofo"/>
        </bean>

总结:在Spring.xml配置文件被加载的时候,bean中的对象就已经被创建好了。

3、bean 中的 ref
bean 中的对象初始化创建对象的话,参数为基本类型,可以像下面这个例子一样出给参数赋值。
    <bean id="Mysql" class="com.zyh.mapper.MysqlUserMapperImp">
          <property name="" value=""/>
    </bean>
但如果参数不是基本类型,就要通过ref来给参数赋值。
     <bean id="SqlServer" class="com.zyh.mapper.SqlServerUserMapperImp"/>
     <bean id="UserService" class="com.zyh.service.UserServiceImp">
         <property name="userMapper" ref="SqlServer"/>
     </bean>
五、Spring 配置

Spring的配置参数只有四个,在上面我们已经见过了bean,其他参数为:alias、description、import
在这里插入图片描述

1、bean
<bean id="" class="" name=""/>
id 是 bean 的唯一表示符,class是这个对象模板的全限定名,name可以给这个对象取一个别名。
2、alias 给bean中的对象取别名
<alias name="Mysql" alias="gg" />
name的值为bean的id值,alias的值就是别名。
3、import
这个参数一般用于团队开发,可以导入多个配置文件,并合并到一起。

在总的配置文件中导入.xml配置

    <import resource="beans1.xml"/>
    <import resource="beans2.xml"/>
六、依赖注入(DI)
依赖:所有的bean对象的创建都依赖于Spring容器
注入:bean对象中的所有属性,都通过Spring容器来注入

依赖注入的方式:

  • 方式一:构造器注入

    Spring 中默认使用无参构造创建对象并注入。

  • 方式二:通过 set 方法注入

测试环境搭建:

两个实体类

//地址
package com.zyh.pojo;

public class Address {
    private String  address;

    public String getAddress() {
        return this.address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
}

//学生
package com.zyh.pojo;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;

public class Student {
    private String name;
    private Address address;
    private String[] book;
    private List<String> hobby;
    private Map<String,String> card;
    private Properties info;
    private String scholarship;

    public String getName() {
        return name;
    }

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

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public String[] getBook() {
        return book;
    }

    public void setBook(String[] book) {
        this.book = book;
    }

    public List<String> getHobby() {
        return hobby;
    }

    public void setHobby(List<String> hobby) {
        this.hobby = hobby;
    }

    public Map<String, String> getCard() {
        return card;
    }

    public void setCard(Map<String, String> card) {
        this.card = card;
    }

    public Properties getInfo() {
        return info;
    }

    public void setInfo(Properties info) {
        this.info = info;
    }

    public String getScholarship() {
        return scholarship;
    }

    public void setScholarship(String scholarship) {
        this.scholarship = scholarship;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", address=" + address +
                ", book=" + Arrays.toString(book) +
                ", hobby=" + hobby +
                ", card=" + card +
                ", info=" + info +
                ", scholarship='" + scholarship + '\'' +
                '}';
    }
}

resources目录下创建Spring配置文件applicationContext.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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>

注入信息:

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <beans>
        <bean id="address" class="com.zyh.pojo.Address">
            <property name="address" value="xian"/>
         </bean>
        <bean id="student" class="com.zyh.pojo.Student">
            <!--1、普通值value注入-->
            <property name="name" value="zyh"/>
            <!--2、应用值ref注入-->
            <property name="address" ref="address"/>
            <!--3、数组注入-->
            <property name="book">
                <array>
                    <value>斗破苍穹</value>
                    <value>武动乾坤</value>
                    <value>大主宰</value>
                    <value>元尊</value>
                </array>
            </property>
            <!--4、List-->
            <property name="hobby">
                <list>
                    <value>打篮球</value>
                    <value>跑步</value>
                    <value>俯卧撑</value>
                </list>
            </property>
            <!--5、Map-->
            <property name="card">
                <map>
                    <entry key="xiaohu" value="001"/>
                    <entry key="xiaoming" value="002"/>
                    <entry key="xiaojian" value="003"/>
                </map>
            </property>
            <!--6、Properties-->
            <property name="info">
                <props>
                    <prop key="account">20200226</prop>
                    <prop key="petName">jojo</prop>
                </props>
            </property>
            <!--7、Null-->
            <property name="scholarShip">
                <null/>
            </property>
        </bean>
    </beans>
</beans>

测试:

import com.zyh.pojo.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void test1(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = (Student) context.getBean("student");
//        System.out.println(student.getName());
//        System.out.println(student.getAddress());
        System.out.println(student);
    }
}

执行结果:
在这里插入图片描述

  • 方式三:拓展方式注入:p命名空间和c命名空间注入

需要导入的xml约束:

       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"

实体类:User

package com.zyh.pojo;

public class User {
    private int age;
    private String name;

    public User() {

    }
    public User(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

注入:

    <beans>
        <!--p命名空间注入-->
        <bean id="user" class="com.zyh.pojo.User" p:name="zyh" p:age="20"/>
        <!--c命名空间注入-->
        <bean id="user2" class="com.zyh.pojo.User" c:name="hang" c:age="18"/>
    </beans>

测试:

import com.zyh.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest2 {
    @Test
    public void test1() {
        ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
        User user = context.getBean("user", User.class);
        System.out.println(user);
        User user2 = context.getBean("user2", User.class);
        System.out.println(user2);
    }
}

执行结果:
在这里插入图片描述

七、bean 的作用域

在这里插入图片描述

  • 单例模式:singleton(默认)
<bean id="user2" class="com.zyh.pojo.User" c:name="hang" c:age="18" scope="singleton"/>
  • 原型模式:prototype
<bean id="user2" class="com.zyh.pojo.User" c:name="hang" c:age="18" scope="prototype"/>
八、bean 的自动装配
  • 自动装配是Spring满足bean依赖一种方式!
  • Spring会在上下文中自动寻找,并自动给bean装配属性!

测试环境:

实体类:

//猫
package com.zyh.pojo;

public class Cat {
    public void shut(){
        System.out.println("miao ...");
    }
}

//狗
package com.zyh.pojo;

public class Dog {
    public void shut(){
        System.out.println("wang ...");
    }
}
//人 
package com.zyh.pojo;

public class Person {
    private String name;
    private Dog dog;
    private Cat cat;

    public String getName() {
        return name;
    }

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

    public Dog getDog() {
        return dog;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", dog=" + dog +
                ", cat=" + cat +
                '}';
    }
}

1、byName 自动装配
通过 byName 自动装配的时候,要保证装配的bean id和自动注入的set方法的值一致
    <bean id="dog" class="com.zyh.pojo.Dog"/>
    <bean id="cat" class="com.zyh.pojo.Cat"/>
    <bean id="person" class="com.zyh.pojo.Person" autowire="byName">
        <property name="name" value="hang"/>
    </bean>

测试 :

import com.zyh.pojo.Person;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void test1() {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Person person = context.getBean("person", Person.class);
        person.getDog().shut();
        person.getCat().shut();
    }
}

执行结果:
在这里插入图片描述

2、 byType 自动装配
通过byType自动装配时,要保证这个被自动注入的bean的class唯一,类型要和被装配的一致。
    <bean id="dog111" class="com.zyh.pojo.Dog"/>
    <bean id="cat111" class="com.zyh.pojo.Cat"/>
    <bean id="person" class="com.zyh.pojo.Person" autowire="byType">
        <property name="name" value="hang"/>
    </bean>
3、使用注解进行自动装配

使用注解进行自动装配需要导入约束和注解支持:

导入约束并开启注解支持

<?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
        https://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/>

@Autowired

在实体类中加注解:

    @Autowired
    private Dog dog;
    @Autowired
    private Cat cat;

测试:

import com.zyh.pojo.Person;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void test1() {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Person person = context.getBean("person", Person.class);
        person.getDog().shut();
        person.getCat().shut();
    }
}

执行结果:
在这里插入图片描述

如果 @Autowired 自动装配的环境较为复杂,就配合 @Qualifier(value = "dog2")注解进行选定Bean对象。
    <bean id="dog" class="com.zyh.pojo.Dog"/>
    <bean id="dog3" class="com.zyh.pojo.Dog"/>
    <bean id="dog2" class="com.zyh.pojo.Dog"/>
    @Autowired
    @Qualifier(value = "dog2")
    private Dog dog;
@Resource
    @Resource
    private Dog dog;
    @Resource
    private Cat cat;

如果xml配置中没有默认名的bean对象,就会报错。

<!--    <bean id="dog" class="com.zyh.pojo.Dog"/>-->
    <bean id="dog3" class="com.zyh.pojo.Dog"/>
    <bean id="dog2" class="com.zyh.pojo.Dog"/>
    <bean id="cat" class="com.zyh.pojo.Cat"/>
    <bean id="cat2" class="com.zyh.pojo.Cat"/>
    <bean id="cat3" class="com.zyh.pojo.Cat"/>
    <bean id="person" class="com.zyh.pojo.Person">
    </bean>

这时我们可以在 @Resource 中加一个属性name 让它去找到指定的 Bean 对象自动装配。

    @Resource(name = "dog2")
    private Dog dog;
小结:@Resource和@Autowired 的区别
  • @Autowired 是通过byType的方式实现的,要求这个 bean 对象必须存在
  • @Resource 默认是通过byName的方式实现的,如果找不到这个bean对象,就使用byType。
九、使用注解开发

在 Spring4 之后,使用注解进行开发,就需要导入 aop 的依赖,并且需要保证导入 context 支持。

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    <!--开启注解支持-->
    <context:annotation-config/>
    <!--开启扫描包-->
    <context:component-scan base-package="com.zyh"/>
</beans>

在 web 开发中,我们会按照MVC三层架构分层dao、service、controller
这几个层的衍生注解:

  • dao :@Repository
  • service:@Service
  • controller:@Controller
十、使用Java配置Spring
使用Java配置Spring后,我们就不再需要applicationContext.xml配合文件了。

实体类: @Component

package com.zyh.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

//这个注解的意思就是这个类被Spring接管了,被注册到容器中
@Component
public class User {
    private String name;

    public String getName() {
        return name;
    }
    @Value("hang")
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}

配置类: @Configuration

package com.zyh.config;

import com.zyh.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

//这个注解意思也是这个类被Spring接管,注册到Spring容器中。
//代表这个类只一个配置类,就像之前之前的applicationContext.xml一样
@Configuration
@ComponentScan("com.zyh.pojo")
public class MyConfig {

    //注册一个Bean,相当于我们之前在applicationContext.xml中写的<bean></bean>一样
    @Bean
    public User getUser(){
        return new User();
    }
}

测试: AnnotationConfigApplicationContext

import com.zyh.config.MyConfig;
import com.zyh.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MyTest {
    @Test
    public void test1(){
        //通过AnnotationConfigApplicationContext去加载容器
        ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        //配置类中的方法名就相当于bean的id,返回值就相当于bean的class
        User user = context.getBean("getUser", User.class);
        System.out.println(user.getName());
    }
}

执行结果:
在这里插入图片描述
在以后学习SpringBoot的过程中,这种使用Java配置Spring的方式很常见。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值