Parsley开发指南 3 配置和初始化

3 配置和初始化

配置和初始化Parsley框架通常包括以下步骤:

l 第一步:告诉IOC容器哪些类应该管理。这可以用MXML,XML文件或ActionScript来完成。

l 第二步: 为每个单独的类配置容器服务,如依赖注入或消息传递。这可以通过你第一步的选择的机制来完成(例如用MXML或者XML配置标签)或在大多数情况下,用AS3元数据标签对类来说很方便。

l 第三步:初始化IOC容器(通常是在应用程序启动的时候)。在Parsley中这通常只需要一行代码或一个简单的MXML标签。看下面的部分为例。

3.1 AS3 Metadata 配置

AS3元数据标签可用于配置服务,如依赖注入或消息传递。他们可以被放在任何由Parsley管理的类上.通过元数据标签配置的功能可以与MXML或者XML中的配置结合。几乎每一个元数据标签有一个相应的MXML和XML配置标签。

相应的各节中所描述的各个元数据标签和它们的属性一起示例:

依赖注入

8.2 使用工厂

8.3 异步对象初始化

8.5 对象生命周期方法

8.6 生命周期观察方法

元数据继承

从观察来看,这些mxmlc编译器使用规则决定着元数据的继承。

l 一个元数据标签在类级别是从来没有子类的。

l 属性或者方法上的元数据标签,它是不会在子类重写的,但当反射子类的时候总是可用的。

l 重写的属性getter / setter或者方法上的元数据标签只能继承,如果重写的方法或者属性没有元数据标签的话。

在默认情况下Parsley 不会使用这些继承规则。特别是他只处理被配置的类的时候,而不是整个继承树。因为现在反射在Flash Players中非常慢,所以由于性能原因,那将是不切实际的。这意味着任何在类级别的元数据,会在子类丢失。在某些情况下这可能是不受欢迎的,例如在类有很多冗余的[ManagedEvents]声明的时候,框架提供了选项来显式地请求处理超类或接口的具体类的实现。

[ProcessSuperclass][ProcessInterfaces]

public class SomeClass {

由于性能的原因,当使用[ProcessSuperclass]的时候只有直接父类会被处理。如果你希望框架在继承树上处理得更高,父类本身也可以使用这个标签。当父类的标签被处理的时候,这些可用的标签会被添加到子类中。这意味着,当使用该功能的时候,你不能在子类中覆盖父类的元数据标签,在这种情况下,父类和子类都将使用。如果你想覆盖标签,那么你不能使用[ProcessSuperclass]

当使用[ProcessInterfaces]时,所有被类实现的接口会被处理,除了类本身。

编译自定义元数据到 SWFs中

不幸的是,当mxmlc编译器决定是否将自定义元数据包含在SWFs中的时候,它有一个非常不一致的行为。如果你使用 Parsley 和 Spicelib SWCs 去编译主应用程序,你(或你的工具)可能会指定库使用 -l选项,这样库将被编译到你的SWF中。在这种情况下所有被Parsley 和Spicelib 使用的元数据会自动的被包含在里面。所以对于主应用程序的 SWF 来说,没有什么你必须要做的事情,除非你创建了你想作为AS3元数据使用的自定义配置的标签。

你(或者你的工具)选择使用-el选项来指定Parsley SWC 来编译模块或其他的SWFs虽然它是不同的。框架类不会被编译到你的SWF中,然后(这是预期的行为)元数据也不会被编译到SWF中。这是相当古怪的行为,既然你打算使用这个框架,那么其元数据配置标签,无论你编译框架类到SWF中没有,它不应该有任何差异。因此,当使用 -el选项必须显式地指定Parsley 和Spicelib的元数据标签。你使用编译器选项的时候你可以可以通过添加所有Spicelib和Parsley 元数据标签来指定它们,即直接写-keep-as3-metadata标签或者写在配置文件中然后用-load-config+= 选项指定。

<flex-config>

    <compiler>

        <keep-as3-metadata append="true">

            <name>Inject</name>

            <name>Init</name>

            <name>Destroy</name>

            <name>Observe</name>

            <name>MessageDispatcher</name>

            <name>MessageHandler</name>

            <name>CommandResult</name>

            <name>CommandComplete</name>

            <name>CommandError</name>

            <name>Publish</name>

            <name>Subscribe</name>

            <name>PublishSubscribe</name>

        </keep-as3-metadata>

        

</compiler>

</flex-config>

上面这些只是Parsley可用标签的一部分。你应该结合你应用程序中正在使用的具体部分,或者直接通过-load-config+= 选项来指定 spicelib-metadata.xml  parsley-metadata.xml 文件。你会在你下载或者从Subversion导出的项目中的  build/config文件夹下面找到这些文件。

如果元数据标签没有编译到SWF中,会出现一些操作失败的症状。例如你指定 [Inject]标签进行注入,但是不会执行。当很多操作都默默失败了(也没有错误),这通常是一个清晰的迹象表明,SWF中没有元数据标签。因为如果有一些其他类型的问题,比如框架无法为一个注入类型在容器中找到匹配的对象,Parsley 总会抛出一个错误。

这部分被添加到本章因为少数用户已经遇到了这个问题。

3.2 MXML配置

这是Parsley唯一(显然)只能用于Flex 应用程序的配置机制。

让我们假设你想要配置以下两个类:

package com.bookstore.service {

class LoginServiceImpl implements LoginService {

    public var timeout:int;

    

    public function login (username:String password:String) : void {

        // execute service    

    }

    

}

}

package com.bookstore.actions {

class LoginAction {

    [Inject]

    public var service:LoginService

    

    [MessageHandler]

    public function handleLoginEvent (event:LoginEvent) : void {

           service.login(event.username event.password); 

    } 

    

}

}

你看到了一些已经用元数据标签配置特性。更多查看 4依赖注入 和 消息传递

 我们现在必须告诉容器来管理这些类和创建一个MXML配置文件。

<Objects 

    xmlns:fx="http://ns.adobe.com/mxml/2009"

    xmlns:services="com.bookstore.services.*"

    xmlns:actions="com.bookstore.actions.*"

    xmlns="http://www.spicefactory.org/parsley"

    >

        

    <fx:Declarations>

    

        <services:LoginServiceImpl timeout="3000"/>

        <actions:LoginAction/>

        

    </fx:Declarations>

    </Objects>

除了元数据标签之外你还可以使用MXML标签进行额外的配置,像上面例子中的timeout。这个例子用一个特定的框架提供的Parsley 命名空间的根标签。它允许使用配置的类的属性。

早期版本使用的fx:Object的根标签仍然支持,但不允许使用外部属性。

框架初始化

最后你必须初始化容器。假设你保存了名叫BookstoreConfig.mxml的配置,你可以这样初始化它:

<parsley:ContextBuilder config="{BookStoreConfig}"/>

在很多程序中上面显示的标签可能是唯一直接依赖于Parsley框架的地方。可以在类的方法上使用元数据标签[Init]去初始化应用程序,应该在应用程序启动的时候执行逻辑。详细查看8.5 对象声明周期方法

理论上你也可以直接使用Parsley Context:

var context:Context = FlexContextBuilder.build(BookStoreConfig this);

var initializer:BookStoreInitializer 

= context.getObjectByType(BookStoreInitializer) as BookStoreInitializer;

initializer.execute();

但是不推荐这种方法,在正常的应用程序代码里通常不需要直接使用Parsley 的API该API主要用于扩展框架或在Parsley之上构建定制框架。

日志输出的上下文描述

如果你想要控制一个特定的上下文在日志输出中的描述,你可以简单地用ContextBuilder标签设置它:

<parsley:ContextBuilder config="{MyConfig}" description="Main"/>

ContexttoString输出将会是:

[Context(Main)]

如果你没有明确的设置description,它会从配置的Context下的配置构件的名字来生成,因此最终可能会很长:

[Context(FlexConfig{MainConfigServiceConfig}XmlConfig{logging.xml}RuntimeConfig{2 instance(s)})]

使用 Parsleys MXML标签

还有一种不同的MXML配置模式,使用的Parsley特殊标签,而不是正常的对象标签。

<Objects

    xmlns:fx="http://ns.adobe.com/mxml/2009"

    xmlns="http://www.spicefactory.org/parsley"

    >

    

    <fx:Script>

        <![CDATA[

            import com.bookstore.services.*;        

            import com.bookstore.actions.*;        

        ]]>

    </fx:Script>

    

    <fx:Declarations>

    

        <Object type="{LoginServiceImpl}">

            <Property name="timeout" value="3000"/>

        </Object>

    

        <Object type="{LoginAction}"/>

        

    </fx:Declarations>

</Objects>

这些特殊标签给你一些附加功能,这些特性在使用普通的标签的时候是不可用的,如第一个例子。

这两种方法都有优点和缺点:

普通的MXML标签的优势:

l 简单并且可以直接使用

l 没有必要了解Parsley的配置标签

l 编译器会检查属性值的类型

Parsley MXML标签的优势:

l 允许构造函数注入。一些人认为这是最干净的依赖注入方式,这种方式在封装方面你可以创建不可变类。你不能用普通标签使用构造函数注入,因为在这种情况下,MXML编译器生成的对象的创建代码,Parsley在获得被实例化的对象后进行额外的配置。当使用普通标签的时候,你的类都必须有一个无参数的构造函数。

l 允许定义一个lazy的对象(<Object lazy="true" type="..."/>这意味着它在你第一次使用之前不会被初始化和配置。

l 允许配置一个DynamicObject/non-singleton的对象 (<DynamicObject type="..."/>).这意味着每次从容器中获取这个对象或注入另一个对象,容器将创建一个新的实例。

l 允许使用自定义标签。Parsley是易于扩展的。你可以用单个类创建自定义扩展,然后可以当作定制的元数据,MXML或XML使用。例子查看 13 扩展框架

动态对象

除了已经介绍过的 <Object>标签, 你还可以使用 <DynamicObject>标签。它可以使用<Object>标签同样的子标签:

<DynamicObject type="{LoginServiceImpl}">

<Property name="timeout" value="3000"/>

</DynamicObject>

相比之下,<Object>这只是创建一个实例和后续注入请求重用它,这个标签为每个请求创建一个新的实例。所以每个使用这个服务的对象或视图会得到自己的实例。这样一个动态的对象的生命周期,与被注入对象是同步的。所以,当目标对象被移除从上下文时,注入的动态对象也会移除,因为它是专为这个目标创建的并且不再需要了。编程访问细节可以在 8.7 动态对象 中找到。

3.3 XML配置文件

外部XML文件可能是一个适当的替代MXML如果:

l 你的配置更改频繁,你想避免额外的编译步骤。

l 配置必须被没有编程知识的人编辑。

l 你没有使用Flex。

当然你也可以选择只外部化的部分配置XML文件并且坚持你的核心服务是MXML的布局。详细查看 3.7组合多个配置机制

使用上面MXML小节所示的相同的两个示例类,XML配置文件将看起来像:

<objects 

    xmlns="http://www.spicefactory.org/parsley"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://www.spicefactory.org/parsley 

        http://www.spicefactory.org/parsley/schema/2.3/parsley-core.xsd"

    >

    <object type="com.bookstore.services.LoginServiceImpl">

        <property name="timeout" value="3000"/>

    </object>

<object type="com.bookstore.services.LoginServiceImpl"/>

</objects>

通常XML配置看起来非常类似于用Parsleys MXML标签的MXML配置。套在下面的XML标签和MXML标签映射到相同的框架类。主要的区别是,我们遵循XML命名约定,因此标签不是大写的,并且属性名称使用破折号而不是驼峰式大小写(如target-property而不是targetProperty)。

初始化只是一行代码,假设你保存文件为config . xml:

XmlContextBuilder.build("config.xml");

如果在Flex中使用XML配置,你可以选择用一个标签来初始化:

<parsley:ContextBuilder>

<parsley:XmlConfig file="config.xml"/>

</parsley:ContextBuilder>

编译配置在XML中的类

有一件事你需要注意,如果你不在你的代码中明确地使用它们,与使用MXML配置相比你使用的配置文件将不会被编译到你的SWC或SWF中。这可能会经常发生,因为对接口的程序来说,只在容器配置中声明具体实现,它是很好的做法。

基本上有三个选择来解决这个:

l 把这些类明确地在你的代码中添加一个引用,即使它不需要。大多数开发人员认为这是一个丑陋的hack。

l 如果你想要使用这些类作为一个库,将它们编译成的SWC(compc 命令,你可以包括完整的源代码文件夹到SWC),然后用 mxmlc 编译器的 -include-libraries选项将整个SWC包含到SWF中。

l 你可以选择使用mxmlc 编译器的 -includes 选项包含单独的类。

3.4 运行时配置

2.2版本中添加此配置机制,允许指定在运行时添加到容器中的实例:

<parsley:ContextBuilder>

    <parsley:FlexConfig type="{ServiceConfig}"/>

    <parsley:FlexConfig type="{ControllerConfig}"/>

    <parsley:XmlConfig file="logging.xml"/>

<parsley:RuntimeConfig instances="{[instance1 instance2]}"/>

</parsley:ContextBuilder>

如果你需要指定id,那么你可以选择使用嵌套的子标签:

<parsley:ContextBuilder>

    <parsley:FlexConfig type="{ServiceConfig}"/>

    <parsley:FlexConfig type="{ControllerConfig}"/>

    <parsley:XmlConfig file="logging.xml"/>

    <parsley:RuntimeConfig>

        <parsley:Instance id="obj1" instance="{instance1}"/>    

        <parsley:Instance id="obj2" instance="{instance2}"/>    

        <parsley:Instance id="obj3" instance="{instance3}"/>    

</parsley:RuntimeConfig>

</parsley:ContextBuilder>

通过RuntimeConfig添加和在稍后时间通过DynamicObjects添加的对象的不同之处在于,前者实际上是注入到其他对象的根对象定义,因为他们在上下文构造的时候被指定了。

你现在甚至可以使用常规的 <Object> 标签:

<parsley:ContextBuilder>

    <parsley:FlexConfig type="{ServiceConfig}"/>

    <parsley:FlexConfig type="{ControllerConfig}"/>

    <parsley:XmlConfig file="logging.xml"/>

    <parsley:RuntimeConfig>

        <parsley:Instance id="obj1" instance="{instance1}"/>    

        <parsley:Instance id="obj2" instance="{instance2}"/>    

        <parsley:Object id="obj3" type="{LoginInterceptor}"/> 

            <parsley:MessageHandler method="handleLogin" type="{LoginEvent}"/>

        </parsley:Object>    

</parsley:RuntimeConfig>

</parsley:ContextBuilder>

你还可以用DSL配置来指定现有的实例:

ContextBuilder.newBuilder()

    .config(FlexConfig.forClass(ServiceConfig))

    .config(FlexConfig.forClass(ControllerConfig))

    .config(XmlConfig.forFile("logging.xml"))

    .object(instance1 "id1")

.object(instance2 "id2")

.build();

3.5 配置 DSL

在2.3版本中添加此配置选项,它允许在代码中用流利语法指定大多数你通常使用 (MXML, XML, Metadata)指定的特性,允许你为配置添加逻辑。DSL跨越多个级别的配置,它可以被用来从头开始创建一个完整的上下文,而它也可以用来在代码中创建对象定义,然后与其他配置机制结合。因为它对于许多类型的配置任务都很有用,现在也已经被几乎所有主要的扩展点使用。因此,当为例子构建一个自定义的元数据标签时,你可以使用DSL方便的添加功能到一个对象。 你可以参考 13 扩展框架 来使用DSL扩展。这一章是关于应用程序的基本配置任务,所以将会列出一些相对简单的例子。

创建一个空的上下文

var context:Context = ContextBuilder.newBuilder().build();

可能看似毫无意义,但是你待会仍然可以使用这个上下文来动态地添加对象。

指定配置的类和文件

ContextBuilder.newBuilder()

    .config(FlexConfig.forClass(BookStoreServices))

    .config(FlexConfig.forClass(BookStoreActions))

.config(XmlConfig.forFile("logging.xml"))

.build();

添加现有的实例到上下文

var instance1:Object = ...;

var instance2:Object = ...;

ContextBuilder.newBuilder()

    .config(FlexConfig.forClass(MainConfig))

    .object(instance1 "id1")

.object(instance2 "id2")

.build();

Flex应用中的第一个Context

如果你使用<ContextBuilder> MXML标签来创建应用的第一个Context ,Parsley 所有提供给Flex的支持是自动初始化,因为MXML标签是Flex-specific。 换句话说这部分展示的ContextBuilder API 是Parsley Core的一部分,并不依赖于Flex。如果你使用这个API创建Flex应用程序第一个上下文,首先你需要调用一次下面的初始化方法。

FlexSupport.initialize();

当你使用<ContextBuilder>MXML标签或使用这个API创建后续上下文实例的时候,这不是必需的。

以编程方式创建对象定义

var contextBuilder:ContextBuilder = ContextBuilder.newBuilder();

var objectBuilder:ObjectDefinitionBuilder 

        = contextBuilder.objectDefinition().forClass(SomeClass);

objectBuilder

    .property("dependency")

        .value(Inject.byType());

MessageHandler

    .forMethod("handleLogin")

        .type(LoginMessage)

        .scope("local")

            .apply(builder);

            

objectBuilder

    .asDynamicObject()

        .id("myObject")

        .register();

            

contextBuilder

.config(FlexConfig.forClass(MainConfig))

.build();

这有一点啰嗦,但是仍然方便。

注意,在object builder上调用注册器来添加定义到上下文中,因此除了MainConfig MXML配置中定义的对象来说,它是可用的。

因此上述完全相当于添加以下MXML声明到MainConfig:

<DynamicObject id="myObject" type="{SomeClass}">

    <Property name="dependency" typeRef="{SomeOtherClass}"/>

    <MessageDispatcher property="dispatcher"/>

<MessageHandler method="handleLogin" type="{LoginMessage}" scope="local"/>

</DynamicObject>

这反过来就相当于在类定义中使用 [Inject] [MessageDispatcher] [MessageHandler]元数据标签。

3.6 ActionScript配置

如果到目前为止你只知道Flex的IOC容器,那么他的配置机制似乎很陌生。它类似于Spring JavaConfig。它允许你在代码中创建需要被Parsley管理的对象。配置类是纯ActionScript,不与Parsley API交互,因此这种机制在你只使用元数据标签配置对象的情况下使用。如果你需要以编程方式指定功能像一个消息处理程序或注入点,你应该更喜欢3.5配置DSL

让我们再次使用两个类的实例,并将它们添加到MXML IOC容器中:

package com.bookstore.config {

class BookStoreConfig {

    public const action:LoginAction = new LoginAction();

    

    public function get service () : LoginServiceImpl {

        var service:LoginServiceImpl = new LoginServiceImpl();

        service.timeout = 3000;

        return service;    

    }

}

}

我们又一次设置timeout属性,这次是用的ActionScript请注意,它并不重要,如果你用var定义一个对象,一个常量或一个隐式的getter函数。这些属性的对象持有都将被添加到IOC容器。

当然初始化还是只有一行:

ActionScriptContextBuilder.build(BookStoreConfig);

或者使用MXML标签(在Flex中):

<parsley:ContextBuilder config="{BookStoreConfig}"/>

这个配置模式允许你添加元数据:

[DynamicObject]

public function get service () : LoginServiceImpl {

    var service:LoginServiceImpl = new LoginServiceImpl();

    service.timeout = 3000;

return service;    

}

在上面的例子中,每次请求这个对象,容器都会调用getter方法。因为默认的是创建单例对象,所以没有任何元数据标签,Parsley 只会调用此方法一次,然后为随后的注入用内部缓存返回的对象和重复使用相同的实例。

3.7组合多个配置机制

尽管大多数简单的应用程序中,你可能更愿意使用一个配置机制,没有人强迫你这么做。你可以使用呈现在这一章的任何组合配置风格,甚至创建你自己的。

首先你可能在相同的配置风格下,想要把配置分开在不同的文件/类里:

<parsley:ContextBuilder>

    <parsley:FlexConfig type="{BookStoreServices}"/>

<parsley:FlexConfig type="{BookStoreActions}"/>

</parsley:ContextBuilder>

但是最后你也可以用希望的方式混合它们:

<parsley:ContextBuilder>

    <parsley:FlexConfig type="{BookStoreServices}"/>

    <parsley:FlexConfig type="{BookStoreActions}"/>

<parsley:XmlConfig file="logging.xml"/>

</parsley:ContextBuilder>

或以编程方式使用DSL配置:

ContextBuilder.newBuilder()

    .config(FlexConfig.forClass(BookStoreServices))

    .config(FlexConfig.forClass(BookStoreActions))

.config(XmlConfig.forFile("logging.xml"))

.build();

所有这些例子最终的结果是一个单一的Parsley上下文。对于所有的IOC容器特性,如依赖注入或消息传递,你如何在文件和类之间分割对象配置这不重要,如果它们被配置在一个文件中,结果总是一样的,。

但是对于大型的复杂应用程序来说你可能想创建模块化的Contexts,这意味着多个配置构件不会合并成一个单一的上下文,所以他们可以根据需要加载和卸载。对模块化的应用程序你可能想看 10构建模块化应用程序

最后,如果你想要创建自己的配置机制并且与现有配置风格无缝集成,你可以创建ConfigurationProcessor接口的实现。详细查看 13 扩展框架

3.8 Properties配置

自2.3版以来Parsley支持使用properties配置,在运行时从外部文件加载或在内部声明。

支持语法

支持的语法允许用“=”分隔的键/值对,多行属性值在行末使用' \ ',注释使用“#”在行的开头:

prop1 = value1

prop2=value2

#This is a comment which will be ignored

prop2 = This is a property value that \

spans more than just one line

然后对于属性的名称还有一个限制:这些名字必须也是有效的AS3属性名。这意味着你不能像 Ant-Style 属性文件 一样 使用 ‘.’,相反的你可以使用’_’作为长属性名的分隔符。

像显示在上面的例子中,'='分隔符周围的空格不会有什么影响,属性名和值将被修剪。

在MXML配置使用properties

MXML中使用properties配置需要使用在2.3添加的新标签<Objects>

<Objects 

    xmlns:fx="http://ns.adobe.com/mxml/2009"

    xmlns="http://www.spicefactory.org/parsley"

    >

    

    <fx:Script>

        <![CDATA[

            import com.mycompany.bookstore.BookstoreService;

        ]]>

    </fx:Script>

    

    <fx:Declarations>

    

        <Object type="{BookstoreService}">

            <Property name="serviceUrl" value="{properties.serviceUrl}"/>

        </Object>

    

</fx:Declarations>

</Objects>

XML配置中使用properties

XML文件中,Ant-style 语法  ${someProperty}是可以使用的:

<objects xmlns="http://www.spicefactory.org/parsley">

    

    <object type="com.mycompany.bookstore.BookstoreService">

        <property name="serviceUrl" value="${serviceUrl}"/>

</object>

</Objects>

这里你不需要像在MXML中一样使用properties.前缀。

外部properties文件

如果你希望在运行时加载properties,你可以使用PropertiesFile 标签。

<parsley:ContextBuilder>

    <parsley:PropertiesFile file="bookstore.properties"/>

    <parsley:FlexConfig type="{BookStoreConfig}"/>

<parsley:XmlConfig file="logging.xml"/>

</parsley:ContextBuilder>

确保任何类或文件使用这些properties之前声明properties文件,因为构建器会按指定顺序处理子标签。

properties文件编译到应用程序中

如果你在一个单独的文件中维护properties,但是希望把它编译到应用程序中,而不是在运行时加载它们,你可以使用PropertiesString 标签:

<fx:String id="props" source="bookstore.properties"/>

<parsley:ContextBuilder>

    <parsley:PropertiesString source="{props}"/>

    <parsley:FlexConfig type="{BookStoreConfig}"/>

<parsley:XmlConfig file="logging.xml"/>

</parsley:ContextBuilder>

内联属性声明

最后,你还可以直接在构建器标签中定义属性(当然可以和外部属性结合)。

<parsley:ContextBuilder>

    <parsley:PropertiesObject>

        <fx:Object 

            serviceUrl="http://www.company.com/services/" 

            mediaPath="images"

        />

    </parsley:PropertiesObject>

    <parsley:FlexConfig type="{BookStoreConfig}"/>

<parsley:XmlConfig file="logging.xml"/>

</parsley:ContextBuilder>

DSL配置

如果你不使用MXML标签构建一个上下文,上面所示的也可以在DSL配置用另外一种方式:

ContextBuilder

    .newBuilder()

        .config(Properties.forFile("bookstore.properties"))

        .config(FlexConfig.forClass(BookStoreConfig))

       .build()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值