4、Spring核心-bean

为ioc容器中的bean正确赋值的各种方式

1、setter方式注入bean

导入的依赖:

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>

		<!--这里为测试方便我就直接用了lombok插件-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
        </dependency>

bean配置

<bean id="user01" class="com.springleaning.entity.User">
    <property name="uid" value="01"/>
    <property name="uname" value="张三"/>
    <property name="pwd" value="zs123"/>
    <property name="age" value="20"/>
</bean>

test01

private ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml");

    @Test
    public void test01() {
        //直接使用ioc容器实例的getBean方法获取id为useer01的bean
        User user = (User) ioc.getBean("user01");
        //下面这种,如果容器中有多个类型是User的bean,则会出错
        User user2 = (User) ioc.getBean(User.class);
        System.out.println(user+"\n"+user2);
    }

结果

User(uid=1, uname=张三, pwd=zs123, age=20)

User(uid=1, uname=张三, pwd=zs123, age=20)

2、使用构造器

2.1 指定name属性

bean配置

 <bean id="user02" class="com.springleaning.entity.User">
        <constructor-arg name="uid" value="02"/>
        <constructor-arg name="uname" value="李四"/>
        <constructor-arg name="pwd" value="ls123"/>
        <constructor-arg name="age" value="19"/>
    </bean>

test02

	@Test
    public void test02() {
        User user = (User) ioc.getBean("user02");
        System.out.println(user);
    }

结果

User(uid=2, uname=李四, pwd=ls123, age=19)

同样能够正确为bean赋值

2.2 不指定name,直接赋value值

bean配置

	<bean id="user03" class="com.springleaning.entity.User">
        <constructor-arg value="03"/>
        <constructor-arg value="王五"/>
        <constructor-arg value="ww123"/>
        <constructor-arg value="18"/>
    </bean>

test03

	@Test
    public void test03() {
        User user = (User) ioc.getBean("user03");
        System.out.println(user);
    }

结果

User(uid=3, uname=王五, pwd=ww123, age=18)

这仍是没问题的,但是要注意一点,这种不指定name的方式需要按照构造器形参的顺序来进行赋值,否则会错

2.3 不指定name,但是指定index索引

bean配置

	<!--这里我故意把顺序打乱,使用指定正确索引来帮助bean正确赋值-->
	<bean id="user04" class="com.springleaning.entity.User">
        <constructor-arg value="赵七" index="1"/>
        <constructor-arg value="04" index="0"/>
        <constructor-arg value="21" index="3"/>
        <constructor-arg value="zq123" index="2"/>
    </bean>

test04

	@Test
    public void test04() {
        User user = (User) ioc.getBean("user04");
        System.out.println(user);
    }

结果

User(uid=4, uname=赵七, pwd=zq123, age=21)

可以看到这种方式也能正确赋值

3、通过P名称空间来为bean赋值

P名称空间是用来防止标签重复的,在beans标签引入头部文件

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

在引入P名称空间头部文件后,新创建一个bean

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iLuQdjl3-1589457939176)(I:\作业\IOC容器\QQ截图20200514143119.png)]

可以看到在打出p的时候提示就已经出来了,这就意味着不用在bean标签里面写property标签了

bean配置

	<bean id="user05" class="com.springleaning.entity.User" p:uid="05"
    p:uname="老六" p:pwd="ll123" p:age="22"/>

结果

User(uid=5, uname=老六, pwd=ll123, age=22)

可以看到这也能正确的赋值

4、内外部bean

xml称可扩展的标记语言,但始终在xml文件里编写的东西还是文本内容,但是却能把参数自动转换对应的类型

但如果,我一个类里面还有其他类的对象属性呢,比如
在这里插入图片描述

4.1 给属性赋null值

在这里插入图片描述

首先我在User实体类中给了uname一个初始值

bean配置

	 <bean id="user01" class="com.springleaning.entity.User">
        <property name="uname" value="null"/>
    </bean>

test01

	@Test
    public void test01() {
        User user = (User) ioc.getBean("user01");
        System.out.println(user.getUname()==null);
    }

结果

false

可以看到并不是真正为null,用value方式其实都是赋的字符串,但null不是字符串

那么下面这种才是正确的赋null值的方法

bean配置

	<bean id="user02" class="com.springleaning.entity.User">
        <property name="uname">
            <null/>
        </property>
    </bean>

test01

	@Test
    public void test01() {
        User user = (User) ioc.getBean("user02");
        System.out.println(user.getUname()==null);
    }

结果

true

赋null值的方法仅此一种,在需要赋null值的property标签内加标签就ok

4.2 引用外部bean

bean配置

		<bean id="user02" class="com.springleaning.entity.User">
        <property name="uname">
            <null/>
        </property>
        <property name="car" ref="car01"/>
        <property name="book" ref="book01"/>
    </bean>

    <bean id="car01" class="com.springleaning.entity.Car">
        <property name="cname" value="宝马"/>
        <property name="coler" value="黑色"/>
        <property name="price" value="500000"/>
    </bean>

    <bean id="book01" class="com.springleaning.entity.Book">
        <property name="bname" value="鲁宾逊漂流记"/>
        <property name="btype" value="故事"/>
        <property name="bprice" value="35"/>
    </bean>

test03

 	@Test
    public void test03() {
        User user = (User) ioc.getBean("user02");
        System.out.println(user.getCar() + "\n" + user.getBook());
    }

结果

Car(cname=宝马, coler=黑色, price=500000.0) Book(bname=鲁宾逊漂流记, btype=故事, bprice=35.0)

引用外部bean成功

要注意一点的是,引用外部bean只能引用beans根标签下的bean,否则会报错提示找不到该bean

4.3 引用内部bean

在这里插入图片描述

通过这张图,你知道了什么?没错,内部bean类型必须父级property的name属性的类型一样

来做个测试

bean配置

		<property name="car">
            <bean class="com.springleaning.entity.Car">
                <property name="cname" value="奥迪"/>
                <property name="coler" value="黑色"/>
                <property name="price" value="600000"/>
            </bean>
        </property>

test02

 	@Test
    public void test02() {
        User user = (User) ioc.getBean("user02");
        System.out.println(user.getCar() + "\n" + user.getBook());
    }

结果

Car(cname=奥迪, coler=黑色, price=600000.0)

没问题,这里还需要注意一点,内部bean是不需要写id或者别名的,因为只能在这个property里面使用,写了id没任何意义,而且每个bean也引用不了其他bean里的内部bean。

4.4 给List赋值

bean配置

	<bean id="user03" class="com.springleaning.entity.User">
        <property name="list">
            <list>
                <bean class="com.springleaning.entity.Book"
                      p:bname="三国演义" p:btype="故事书" p:bprice="50"/>
                <bean class="com.springleaning.entity.Book"
                      p:bname="java" p:btype="编程" p:bprice="78"/>
                <bean class="com.springleaning.entity.Book"
                      p:bname="c++" p:btype="编程" p:bprice="68"/>
                <bean class="com.springleaning.entity.Book"
                      p:bname="mysql" p:btype="编程" p:bprice="60"/>
            </list>
        </property>
    </bean>

test03

 	@Test
    public void test03() {
        User user = (User) ioc.getBean("user03");
        System.out.println(user.getCar() + "\n" + user.getBook());
    }

结果

[Book(bname=三国演义, btype=故事书, bprice=50.0), Book(bname=java, btype=编程, bprice=78.0), Book(bname=c++, btype=编程, bprice=68.0), Book(bname=mysql, btype=编程, bprice=60.0)]t

好没问题,同样list里的bean不需要写id或者别名,因为也是内部bean。

4.5 给Map赋值

bean配置

	<bean id="user04" class="com.springleaning.entity.User">
        <property name="map">
            <map>
                <entry key="key01" value="哈哈"/>
                <entry key="key02" value="嘿嘿"/>
                <entry key="car" value-ref="car01"/>
                <entry key="book" value-ref="book01"/>
            </map>
        </property>
    </bean>

test04

 	@Test
    public void test04() {
        User user = (User) ioc.getBean("user04");
        System.out.println("全部:"+user.getMap()+"\ncar:"+user.getMap().get("car"));
    }

结果

全部:{key01=哈哈, key02=嘿嘿, car=Car(cname=宝马, coler=黑色, price=500000.0), book=Book(bname=鲁宾逊漂流记, btype=故事, bprice=35.0)} car:Car(cname=宝马, coler=黑色, price=500000.0)

ok这也是没问题的

4.6 给Properties赋值

bean配置

	 <bean id="user05" class="com.springleaning.entity.User">
        <property name="properties">
            <props >
                <prop key="p1">配置1</prop>
                <prop key="p2">配置2</prop>
                <prop key="p3">配置3</prop>
            </props>
        </property>
    </bean>

test05

    @Test
    public void test05() {
        User user = (User) ioc.getBean("user05");
        System.out.println(user.getProperties());
    }

结果

{p1=配置1, p2=配置2, p3=配置3}

这个也是没问题的

4.7 级联属性

什么叫级联属性呢,最简单的例子就是,一个List,List对象装进去的是Book类的对象,这个book对象就是List对象的属性,属性中的属性,就是级联属性,这个我不就不多说了,参考下拉框级联选择城市

4.8 配置继承

bean配置

	<bean id="user06" parent="user05"/>

test06

    @Test
    public void test06() {
        User user = (User) ioc.getBean("user06");
        System.out.println(user.getProperties());
    }

结果

{p1=配置1, p2=配置2, p3=配置3}

使用parent指定继承的bean的id,继承了并不表示两个bean就是同一个bean了,只是配置信息被继承了而已

4.9 抽象bean

抽象bean是只能当做模板被继承,而不用直接获取这个抽象bean的信息

bean配置

	<bean id="user07" class="com.springleaning.entity.User" abstract="true">
        <property name="map">
            <map>
                <entry key="key01" value="哈哈"/>
                <entry key="key02" value="嘿嘿"/>
                <entry key="car" value-ref="car01"/>
                <entry key="book" value-ref="book01"/>
            </map>
        </property>
    </bean>

test07

    @Test
    public void test07() {
        User user = (User) ioc.getBean("user07");
        System.out.println(user.getMap());
    }

结果

直接抛出异常org.springframework.beans.factory.BeanIsAbstractException: Error creating bean with name 'user07': Bean definition is abstract

意思是这个bean是抽象的,不能创建

4.9 继承抽象bean

抽象bean是只能当做模板被继承,而不用直接获取这个抽象bean的信息

bean配置

    <bean id="user08" parent="user07"/>

test08

    @Test
    public void test08() {
        User user = (User) ioc.getBean("user08");
        System.out.println(user.getMap());
    }

结果

{key01=哈哈, key02=嘿嘿, car=Car(cname=宝马, coler=黑色, price=500000.0), book=Book(bname=鲁宾逊漂流记, btype=故事, bprice=35.0)}

继承抽象bean后继承了它的配置信息,没毛病

5、bean的依赖

首先做个测试,看看b一个ioc容器内bean的创建的先后顺序

抽象bean是只能当做模板被继承,而不用直接获取这个抽象bean的信息

bean配置

    <bean id="user" class="com.springleaning.entity.User"/>
    <bean id="car" class="com.springleaning.entity.Car"/>
    <bean id="book" class="com.springleaning.entity.Book"/>

test01

    private ApplicationContext ioc= new ClassPathXmlApplicationContext("ioc3.xml");
    @Test
    public void test01() {
        System.out.println("ioc容器启动完毕。。。");
    }

结果

User被创建了。。。 Car被创建了。。。 Book被创建了。。。 ioc容器启动完毕。。。

然后我把bean的位置换一下,在测试

bean配置

    <bean id="user" class="com.springleaning.entity.User"/>
    <bean id="car" class="com.springleaning.entity.Car"/>
    <bean id="book" class="com.springleaning.entity.Book"/>

结果

Book被创建了。。。 Car被创建了。。。 User被创建了。。。 ioc容器启动完毕。。。

可以看到,一个ioc容器内bean的创建顺序是按照配置的先后顺序来创建的,而且所有的bean都是在ioc容器启动之前就已经创建好了。

那有没有什么方法在不改变配置顺序的情况下改变bean的创建顺序呢,答案是肯定是有的
那就是bean的依赖,这是什么个情况呢,那就来做个测试

bean配置

    <bean id="book" class="com.springleaning.entity.Book" depends-on="car"/>
    <bean id="car" class="com.springleaning.entity.Car"/>
    <bean id="user" class="com.springleaning.entity.User"/>

结果

Car被创建了。。。 Book被创建了。。。 User被创建了。。。 ioc容器启动完毕。。。

可以看到已经不是按照配置顺序来创建bean了,Car是第一个创建

我给第一个bean加上了 depends-on=“car” 这就表示,id为book的bean依赖于id为car这个bean,

所谓依赖,就是只能等被依赖的bean创建好之后,这个依赖于它的bean才能创建,所以能看到如上的结果

6、bean的作用域,单实例,多实例

​ 首先bean的作用域是什么?简单来说其实就是单实例和多实例,单实例上面的案例以及接触过了,就是同一个类型的实体类的实例只有一个,也就是说只会创建一次,而且是在ioc容器创建之前就已经创建好了。这个单实例就不多讲了。

​ 那么什么是多实例呢?我们先来做个小测试

bean配置

    <bean id="book" class="com.springleaning.entity.Book"/>
    <bean id="car" class="com.springleaning.entity.Car"/>

    <!--prototype:多实例-->
    <!--singleton:单实例-->
    <bean id="user" class="com.springleaning.entity.User" scope="prototype"/>

test01

   @Test
    public void test01() {
        Book book= (Book) ioc.getBean("book");
        Book book2= (Book) ioc.getBean("book");
        System.out.println("ioc容器启动完毕。。。");
    }

结果

Book被创建了。。。 Car被创建了。。。 ioc容器启动完毕。。。

可以看到,我创建了两个book对象,但是构造器却只调用了一次,这就是单实例。

第三个bean并没有创建,因为我加了scope=“prototype” 也就是这它改成了多实例的。

那多实例的并是什么时候创建呢?很简单再来做个测试

test01

    @Test
    public void test01() {
        User user= (User) ioc.getBean("user");
        User user2= (User) ioc.getBean("user");
        System.out.println("ioc容器启动完毕。。。");
    }

结果

Book被创建了。。。 Car被创建了。。。 User被创建了。。。 User被创建了。。。 ioc容器启动完毕。。。

可以看到,多实例bean每创建一个对象就调用一次构造器,但是上面那个例子就已经知道了,ioc启动完成了,并没有多实例bean被创建 ,因为多实例bean是使用bean的时候才去创建

7、工厂模式

什么是工厂模式?

​ 其实就是一个类,这个类专门帮我们去创建对象,这个类就是工厂
而工厂又分为两种:

​ 静态工厂:就是静态类,本身不需要被创建,可以直接通过类名点出方法,工厂类.工厂方法名();

​ 实例工厂:就是本身需要创建的类,先new实例再通过实例去调用工厂方法。

那么静态工厂和实例工厂具体怎么实现呢?以下分别做个案例

7.1 静态工厂

AirPlane类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class AirPlane {
    private String jzName;
    private String fjsName;
    private String fdj;
    private String yc;
    private Integer zks;
}

StaticFactroy(静态工厂类)

public class StaticFactroy {
    public static AirPlane getAirPlane() {
        return new AirPlane();
    }
}

bean配置

    <bean id="airplane" class="com.springleaning.entity.StaticFactroy"
    factory-method="getAirPlane">
        <property name="fdj" value="太行"/>
        <property name="fjsName" value="lmz"/>
        <property name="jzName" value="xhh"/>
        <property name="yc" value="50m"/>
        <property name="zks" value="150"/>
    </bean>

test01

   @Test
    public void test01() {
        AirPlane airplane = (AirPlane) ioc.getBean("airplane");
        System.out.println(airplane);
    }

结果

Book被创建了。。。 Car被创建了。。。 AirPlane(jzName=xhh, fjsName=lmz, fdj=太行, yc=50m, zks=150)

7.2 实例工厂

InstanseFactroy(静态工厂类)

public class InstanseFactroy {
    public AirPlane getAirPlane() {
        return new AirPlane();
    }
}

bean配置

    <bean id="instanseFactroy" class="com.springleaning.entity.InstanseFactroy"/>

    <bean id="airplane2" factory-bean="instanseFactroy" factory-method="getAirPlane">
        <property name="fdj" value="太行"/>
        <property name="fjsName" value="lmz"/>
        <property name="jzName" value="xhh"/>
        <property name="yc" value="50m"/>
        <property name="zks" value="150"/>
    </bean>

test02

    @Test
    public void test02() {
        AirPlane airplane = (AirPlane) ioc.getBean("airplane2");
        System.out.println(airplane);
    }

结果

Book被创建了。。。 Car被创建了。。。 AirPlane(jzName=xhh, fjsName=lmz, fdj=太行, yc=50m, zks=150)

好了,两种工厂模式就这样了

类的区别

静态的工厂方法加了static,实例的工厂方法没有加,就这么简单

xml配置的区别

静态工厂:

​ 配置bean需要class指定静态工的全类名以及指定一个factory-method工厂方法。

实例工厂:
先配置一个实例工厂的bean,这个bean指定实例工厂的全类名,实则就是为了创建出实例工厂的一个实例。
再配置一个用来获取信息的bean,这个bean不用指定class类型,但是需要指定factory-bean=这个实例工厂 bean的id,然后指定一个factory-method工厂方法即可

7.3 FactoryBean

FactoryBean是一个Spring规定的一个接口,只要实现了这个接口的类,Spring都识别为这是一个工厂

做个小测试

BookFactoryBean

public class BookFactoryBean implements FactoryBean<Book> {
    /**
     * @return 返回创建的对象
     */
    @Override
    public Book getObject() throws Exception {
        System.out.println("Book由工厂创建完成...");
        return new Book();
    }

    /**
     * @return 返回对象的类型
     */
    @Override
    public Class<?> getObjectType() {
        return Book.class;
    }

    /**
     * isSingleton:是不是单例?
     * @return true:是单例,false:不是单例
     */
    @Override
    public boolean isSingleton() {
        return false;
    }
}

bean配置

 <bean id="bookFactoryBean"  class="com.springleaning.entity.BookFactoryBean">

    </bean>

test03

    @Test
    public void test03() {
        Object bean=ioc.getBean("bookFactoryBean");
        System.out.println(bean);
        System.out.println("bean的类型:"+bean.getClass());
    }

结果

Book由工厂创建完成... Book(bname=null, btype=null, bprice=null) bean的类型:class com.springleaning.entity.Book

那么这个FactoryBean工厂是在何时创建bean的实例呢?

	/**
     * isSingleton:是不是单例?
     * @return true:是单例,false:不是单例
     */
    @Override
    public boolean isSingleton() {
        return false;
    }

这个方法也知道了设置是不是单例的方法,刚才我们设置的false就是代表要创建的bean不是单实例,所以就是多实例。前面知道了多实例是在使用bean的时候才创建。所以我们这次把测试类中的获取对象去掉看看。

test04

    @Test
    public void test04() {
       System.out.println("ioc容器启动完成。。。'");
    }

结果

ioc容器启动完成。。。

可以看到并没有创建,所以证明这个设置为false的确是创建多实例的bean,那么如果设置为true单实例呢,

是否跟之前一样会在ioc启动时候就创建好对象呢,来试试

	/**
     * isSingleton:是不是单例?
     * @return true:是单例,false:不是单例
     */
    @Override
    public boolean isSingleton() {
        //改为true
        return true;
    }

test05

    @Test
    public void test04() {
       System.out.println("ioc容器启动完成。。。'");
    }

结果

ioc容器启动完成。。。

没有创建!!是不是有点惊讶,和之前的xml配置bean的设置单多实例不一样。这个FactoryBean不管是创建单实例还是多实例都是在使用bean的时候才创建的。

8、bean的生命周期

单实例bean:
容器启动的时候创建,容器关闭的时候销毁bean

多实例bean,
使用时候创建bean

我们为为bean自定义一些生命周期方法,spring在创建和销毁的时候会调用这些方法,自定义初始化方法和销毁方法:且这些方法不能带参数,但是可以抛出异常。

7.3 FactoryBean

FactoryBean是一个Spring规定的一个接口,只要实现了这个接口的类,Spring都识别为这是一个工厂

做个小测试

Book

@Data
@AllArgsConstructor
@NoArgsConstructor
@SuppressWarnings("serial")
public class Book {
    private String bname;
    private String btype;
    private Double bprice;

    public void myInit(){
        System.out.println("这是Book初始化方法");
    }

    public void myDestroy(){
        System.out.println("这是Book销毁方法");
    }
}

bean配置

<bean id="book" class="com.entity.Book" init-method="myInit" destroy-method="myDestroy">

test03

	private ConfigurableApplicationContext ioc=new 			ClassPathXmlApplicationContext("applicationContext.xml");
    @Test
    public void test() {
        ioc.close();
    }

结果

这是Book初始化方法
这是Book销毁方法

不知道大家有没有注意到,我上面用的不是ApplicationContext去接收的,那是因为它没有销毁ioc容器的方法,

在ConfigurableApplicationContext类以上就开始有这个方法了。所以用ApplicationContext去接收ioc的实例是只能调用到启动方法的。

ioc容器的bean默认都是单实例的bean,随容器产生,随容器销毁

那么多实例bean在ioc容器销毁的时候会随之销毁吗?来试试

bean配置

<bean id="book" class="com.entity.Book" init-method="myInit" destroy-method="myDestroy" scope="prototype">

test03

	private ConfigurableApplicationContext ioc=new ClassPathXmlApplicationContext("applicationContext.xml");
    @Test
    public void test() {
        Book book = (Book) ioc.getBean("book");
        ioc.close();
    }

结果

这是Book初始化方法z

可以看到销毁的方法并没有被调用,所以多实例bean是在使用bean时候创建,并且ioc容器销毁的时候多实例bean不会销毁。

9、bean的后置处理器

BeanPostProcessor是一个接口,只有两个方法:
postProcessBeforeInitialization();这个方法会在初始化之前调用

​ postProcessAfterInitialization():这个方法会在初始化之后调用

为了验证方法执行顺序,我实现了这个接口,重写了这两个方法

bean配置

	<bean id="book" class="com.entity.Book" init-method="myInit" destroy-	method="myDestroy"/>
    <bean id="myBeanPostprocessor" class="com.entity.MyBeanPostprocessor"/>

MyBeanPostprocessor

public class MyBeanPostprocessor implements BeanPostProcessor {
    /**
     * 初始化之前调用
     * @param bean 将要初始化的bean
     * @param beanName 将要初始化的bean的名称
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("bean即将初始化。。。");
        return bean;
    }

    /**
     * 初始化方法之后调用
     * @param bean 初始化之后的bean
     * @param beanName 初始化之后的bean名称
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("bean初始化已完成。。。");
        return bean;
    }
}

结果

bean即将初始化。。。 这是Book初始化方法 bean初始化已完成。。。 这是Book销毁方法

可以看到的确是这样的,无论自己有没有去写这个bean后置处理器,都会默认有个bean后置处理器在工作。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值