关闭

Oracle Coherence中文教程十九:使用便携式对象格式

标签: Coherence集群
1714人阅读 评论(0) 收藏 举报
分类:

使用便携式对象格式

使用便携式对象格式(POF)具有许多优点,包括语言独立性的性能优势。建议你仔细看您的系列化解决方案时,在POF工作具有连贯性。对于如何使用POF建设。NET时,延长客户的信息,请参阅楼宇集成对象NET客户端”Oracle Coherence的客户端指南。对于如何构建C++扩展客户时,与POF的信息,请参阅楼宇集成对象的C + +客户端”Oracle Coherence的客户端指南。

本章包含以下各节:

    POF系列化概述
    使用POF API序列化对象
    使用POF注解序列化对象
    使用POF萃取和POF更新程式

19.1 POF序列化概述

序列化是将对象转换成二进制格式编码。它是连贯工作,必须将数据移动在网络的一个关键组成部分。可移植对象格式(也可简称为POF)是一种语言无关的二进制格式。 POF的设计是令人难以置信的高效率,在空间和时间与连贯性工作,并已成为一个基石元素。 POF二进制流欲了解更多信息,请参阅附录E“PIF-POF的二进制格式。

有几个选项提供系列化,包括标准的Java序列化,POF,和你自己的自定义序列化例程。每个人都有自己的权衡。标准Java序列化很容易实现,支持循环对象图,并保留对象的身份。不幸的是,它也比较慢,有详细的二进制格式,并限制只有Java对象。

可移植对象格式具有以下优点:

    1这是独立与目前支持Java语言,NETC ++

    2这是非常有效的,在一个String一个简单的测试类,一个长期的,和3整数,(反)    序列化快7倍,并与标准的Java序列化相比,所产生的二进制是六分之一的大小。

    3版本,对象可以进化,并有向前和向后的兼容性。

    4它支持外部序列化逻辑能力。

    5它的索引,允许提取值,而反序列化整个对象。请参阅使用POF脱水机和POF    新程式

19.2使用POF API的序列化对象

POF需要知道如何序列化和反序列化对象的序列化例程。有两个接口,可序列化的对象:com.tangosol.io.pof.PortableObject的接口和com.tangosol.io.pof.PofSerializer接口。 POF还支持自动实现序列化出具有实施本PortableObjectPofSerializer接口的注释。请参阅使用POF批注对象序列化的细节。

在本节包括以下主题:

    实现PortableObject接口
    实现PofSerializer接口
    为分配POF指数的指引
    使用POF对象参考
    注册POF对象
    配置一致性使用ConfigurablePofContext类的

19.2.1实施的PortableObject接口

PortableObject接口是一个接口,它由以下两种方法:

    public void readExternal(PofReader reader)

public void writeExternal(PofWriter writer)

POF元素的索引为每个元素提供一个数值要写入或读取的POF流。重要的是要记住,索引写入和读出的POF流的每个元素必须是唯一的,尤其是当你的派生类型,因为父类和派生类之间必须是唯一的指标。下面的示例演示实施PortableObject接口:

public void readExternal(PofReader in) 

   throws IOException 

   {

   m_symbol    = (Symbol) in.readObject(0);

   m_ldtPlaced = in.readLong(1);

   m_fClosed   = in.readBoolean(2);

   }

 

public void writeExternal(PofWriter out) 

   throws IOException 

   {

   out.writeObject(0, m_symbol);

   out.writeLong(1, m_ldtPlaced);

   out.writeBoolean(2, m_fClosed);

   }

19.2.2实现PofSerializer接口

PofSerializer接口提供外部序列化逻辑的方式来要序列化的类。这是特别有用,当你不想改变你的类的结构与POF和连贯性。的PofSerializer接口也是由两种方法完成:

    public Object deserialize(PofReader in)

public void serialize(PofWriter out, Object o)

至于与PortableObject接口,所有元素的写入或读取POF流必须唯一索引。下面是的示例实现的PofSerializer接口:

19-1实施的PofSerializer接口

public Object deserialize(PofReader in) 

   throws IOException 

   {

   Symbol symbol    = (Symbol)in.readObject(0);

   long   ldtPlaced = in.readLong(1);

   bool   fClosed   = in.readBoolean(2);

   

   // mark that reading the object is done

   in.readRemainder(null);

 

   return new Trade(symbol, ldtPlaced, fClosed);

   }

 

public void serialize(PofWriter out, Object o) 

   throws IOException 

   {

   Trade trade = (Trade) o;

   out.writeObject(0, trade.getSymbol());

   out.writeLong(1, trade.getTimePlaced());

   out.writeBoolean(2, trade.isClosed());

    

   // mark that writing the object is done

   out.writeRemainder(null);

   }

19.2.3为将POF指标分配指引

POF指标分配到一个对象的属性时,请使用下列准则:

    订购您读取和写入:指数最低值在序列化程序开始并完成的最高。当反序列化的值,执行读取相同的顺序写入。

    非连续的指标是可以接受的,但必须按顺序读/写。

    当子类储备指数范围:指数是累积的派生类型。因此,每一个派生类型必须注意保留其超类的的POF指数范围。

    不要重新目的索引:支持进化型,这是必须的属性的索引不重新立志跨越阶级修订。

    标签索引:索引都贴有一个公共静态最终诠释,更容易的工作,尤其是当使用POF脱水机和POF更新程式。请参阅使用POF脱水机和POF更新程式” 。仍然必须以相同的顺序读出和写入如上所述被标记为指数。

19.2.4使用POF对象参考

POF支持使用对象的身份和引用的对象所发生的超过一次在POF流。对象标示与在同一POF流标记对象的身份和后续实例所引用其身份。对象引用仅支持用户自定义对象类型。

使用引用避免编码相同的对象多次,并有助于减少数据大小。参考文献时,通常使用大量的相当大的对象被多次创建对象时使用嵌套的或循环数据结构。然而,对于包含大量的数据,但只有少数重复的应用程序,对象引用的使用提供了最低限度的好处,由于所产生的跟踪对象标识和引用的开销。

在本节包括以下主题:

    启用POF对象参考
    注册POF循环和嵌套对象的对象标识

19.2.4.1启用POF对象的引用

对象引用默认不启用,必须启用内pof-config.xml配置文件或编程时使用的SimplePofContext类。

启用对象引用在POF的配置文件,包括的<enable-references>元素,内<pof-config>元素,并设置值设为true 。例如:

<?xml version='1.0'?>

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

   xmlns="http://xmlns.oracle.com/coherence/coherence-pof-config"

   xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-pof-config

   coherence-pof-config.xsd">

   ...

   <enable-references>true</enable-references>

</pof-config>

为了使对象引用使用的SimplePofContext类时,请致电setReferenceEnabled的的方法与属性设置为true 。例如:

SimplePofContext ctx = new SimplePofContext();

ctx.setReferenceEnabled(true);

注意事项:
已写入与POF的情况下,不支持引用的对象不能被读取,支持引用的POF上下文。相反也是如此。

19.2.4.2注册POF循环和嵌套对象的对象标识

圆形或嵌套对象创建对象时,必须手动注册一个身份。否则,子对象引用父对象将无法找到父母的身份在参考地图。从序列化对象的身份可以注册在反序列化程序使用com.tangosol.io.pof.PofReader.registerIdentity方法。

下面的例子演示两个对象(客户和产品),其中包含一个循环引用和一个串行执行的客户对象上注册身份。

客户对象的定义如下:

public class Customer

   {

      private String m_sName;

      private Product m_product;

 

   public Customer(String sName)

      {

      m_sName = sName;

      }

 

   public Customer(String sName, Product product)

      {

      m_sName = sName;

      m_product = product;

      }

   public String getName()

      {

      return m_sName;

      }

   public Product getProduct()

      {

      return m_product;

      }

 

   public void setProduct(Product product)

      {

      m_product = product;

      }

   }

The Product object is defined as follows:

public class Product

   {

      private Customer m_customer;

 

   public Product(Customer customer)

      {

      m_customer = customer;

      }

 

   public Customer getCustomer()

      {

      return m_customer;

      }

   }

串行执行寄存器idetity反序列化过程中,被定义为如下:

public class CustomerSerializer implements PofSerializer

   {

   @Override

   public void serialize(PofWriter pofWriter, Object o) throws IOException

   {

      Customer customer = (Customer) o;

      pofWriter.writeString(0, customer.getName());

      pofWriter.writeObject(1, customer.getProduct());

      pofWriter.writeRemainder(null);

   }

 

   @Override

   public Object deserialize(PofReader pofReader) throws IOException

      {

         String sName = pofReader.readString(0);

         Customer customer = new Customer(sName);

         pofReader.registerIdentity(customer);

         customer.setProduct((Product) pofReader.readObject(1));

         pofReader.readRemainder();

         return customer;

      }

   }

19.2.5注册POF对象

Coherence 提供com.tangosol.io.pof.ConfigurablePofContext的序列化器类POF序列化对象映射到一个合适的序列化程序 (无论是PofSerializer实现或通过调用通过PortableObject接口) ,这是负责。

一旦你的类的序列化例程,使用 pof-config.xml 配置文件的ConfigurablePofContext类类注册。 POF配置的文件有<user-type-list>元素,其中包含一个列表类,实现PortableObject或有一个与他们有联系的PofSerializer 。为每个类<type-id>必须是唯一的,而且必须在群集的所有实例(包括扩展客户端)相匹配。请参阅附录,的“ POF用户类型配置元素, 详细参考的POF的配置元素。

以下是一个示例的POF的配置文件:

<?xml version='1.0'?>

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

   xmlns="http://xmlns.oracle.com/coherence/coherence-pof-config"

   xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-pof-config

   coherence-pof-config.xsd">

   <user-type-list>

      <include>coherence-pof-config.xml</include>

      <!-- User types must be above 1000 -->

      <user-type>

         <type-id>1001</type-id>

         <class-name>com.examples.MyTrade</class-name>

         <serializer>

            <class-name>com.examples.MyTradeSerializer</class-name>

         </serializer>

      </user-type>

 

      <user-type>

        <type-id>1002</type-id>

        <class-name>com.examples.MyPortableTrade</class-name>

      </user-type>

   </user-type-list>

</pof-config>

注意事项:
相干保留供内部使用的1000-ID 。在上面的例子所示,的<user-type-list>包括位于根的coherence.jar文件的连贯性POF -config.xml文件。这是连贯性的特定用户类型的定义,应包括所有POF配置文件中。

19.2.6使用ConfigurablePofContext类配置Coherence

连贯性可以被配置为使用三种不同的方式的基础上所需要的粒度水平在ConfigurablePofContext序列化器类:

    Per Service - 每个服务提供一个完整的ConfigurablePofContext序列化器类配置或引用的运行配置文件中包含一个预定义的配置。

    All Services - 所有的服务使用全局ConfigurablePofContext的序列化类的配置。服务提供自己的配置覆盖全局配置。的全局配置信息也可以是一个完整的配置或引用的业务配置文件中包含的预定义的配置。

    启用JVM - ConfigurablePofContext的序列化器类为整个JVM 

19.2.6.1 使用ConfigurablePofContext配置per service

要配置一个服务使用ConfigurablePofContext类,添加一个<serializer>元素缓存配置文件中的缓存方案。请参阅序列化的完整参考的<serializer>元素。

下面的例子演示了一个分布式的缓存配置使用ConfigurablePofContext类定义一个自定义的POF配置文件:

<distributed-scheme>

   <scheme-name>example-distributed</scheme-name>

   <service-name>DistributedCache</service-name>

   <serializer>

      <instance>

         <class-name>com.tangosol.io.pof.ConfigurablePofContext</class-name>

         <init-params>

            <init-param>

               <param-type>String</param-type>

               <param-value>my-pof-config.xml</param-value>

            </init-param>

         </init-params>

      </instance>

   </serializer>

</distributed-scheme>

以下示例引用默认的运行配置文件中的定义。请参阅序列化” ,看到默认ConfigurablePofContext串行定义。

<distributed-scheme>

    <scheme-name>example-distributed</scheme-name>

    <service-name>DistributedCache</service-name>

    <serializer>pof</serializer>

 </distributed-scheme>

19.2.6.2配置ConfigurablePofContext的所有服务类别

在配置全局ConfigurablePofContext的类的所有服务,加一个<serializer>元素内的<defaults>元素在缓存中的配置文件。下面的例子在全球配置所有的缓存方案定义序列化,并且不需要任何额外的配置在单个缓存方案定义。请参阅默认的完整参考的<defaults>元素。

下面的例子演示了一个的全球配置为ConfigurablePofContext类和定义一个自定义的POF配置文件:

<?xml version='1.0'?>

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

   xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config"

   xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-cache-config

   coherence-cache-config.xsd">

   <defaults>

      <serializer>

         <instance>

            <class-name>com.tangosol.io.pof.ConfigurablePofContext</class-name>

            <init-params>

               <init-param>

                  <param-type>String</param-type>

                  <param-value>my-pof-config.xml</param-value>

               </init-param>

            </init-params>

         </instance>

      </serializer>

   </defaults>

   ...

以下示例引用默认的运行配置文件中的定义。请参阅序列化” ,看到默认ConfigurablePofContext串行定义。

<?xml version='1.0'?>

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

   xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config"

   xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-cache-config

   coherence-cache-config.xsd">

   <defaults>

      <serializer>pof</serializer>

   </defaults>

   ...

19.2.6.3配置为JVM ConfigurablePofContext

可以配置一个整个JVM实例使用的POF使用以下系统属性:

tangosol.pof.enabled=true - Enables POF for the entire JVM instance.

tangosol.pof.config=CONFIG_FILE_PATH - The path to the POF configuration file you want to use. If the files is not in the classpath, then it must be presented as a file resource (for example, file:///opt/home/coherence/mycustom-pof-config.xml).

19.3使用POF批注对象序列化

POF注释提供了一个自动化的方式来实现对象的序列化和反序列化例程。 POF注释使用PofAnnotationSerializer这是一个实现的PofSerializer接口的类的序列化和反序列化。注解提供了一个替代使用PortableObjectPofSerializer接口,减少了时间和代码量,使对象序列化。

在本节包括以下主题:

    注释POF序列化的对象
    注册POF注释对象
    启用自动索引
    提供定制编解码器

19.3.1注释POF序列化的对象

可用来指示类及其属性POF序列化两个注解:

    @Portable- 标记类POF序列化。只允许在类级别的注解,并没有任何成员。

    @ PortableProperty - 标记作为POF系列化的属性的一个成员变量或方法存取。注解的方法必须符合存取符号(获取,设置)。大家可以用来指定POF指标以及自定义序列化或反序列化之前或之后执行的编解码器,。也可以省略索引值,并自动分配。如果没有输入一个自定义的编解码器,默认的编解码器使用。

下面的例子演示了注解类,方法,属性和分配明确的性能指标值。请参阅指引更多详细信息,POF索引将POF指标分配。

@Portable

public class Person

   {

   @PortableProperty(0)

   public String getFirstName()

      {

      return m_firstName;

      }

 

   private String m_firstName;

   @PortableProperty(1)

   private String m_lastName;

   @PortableProperty(2)

   private int m_age;

}

19.3.2注册POF注释对象

POF注释对象必须登记一个POF-config.xml文件中内<user-type>元素。请参阅附录D,的“POF用户类型配置元素POF配置元素进行了详细的参考。 POF的批注对象使用PofAnnotationSerializer的序列化,如果一个对象没有实现PortableObject的是标注为便携式,序列化,但是如果一个对象被自动认为是注释和不需要被包含在用户类型定义。下面的示例注册一个用户类型的注释Person对象:

<?xml version='1.0'?>

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

   xmlns="http://xmlns.oracle.com/coherence/coherence-pof-config"

   xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-pof-config

   coherence-pof-config.xsd">

   <user-type-list>

      <include>coherence-pof-config.xml</include>

      <!-- User types must be above 1000 -->

      <user-type>

         <type-id>1001</type-id>

         <class-name>com.examples.Person</class-name>

      </user-type>

</pof-config>

19.3.3启用自动索引

POF的注解支持自动索引缓解需要显式地分配和管理索引值。索引值可以省略,每当定义@ PortableProperty的注解。任何财产,并指定一个明确的指标值不分配一个自动索引值。自动索引算法可以被描述如下:

Name 

Explicit Index(显式指数)

Determined Index(确定索引)

c

1

1

a

Omitted()

0

b

omitted

2


注意事项:
自动索引目前不支持可进化类。

要启用自动索引,必须序列化PofAnnotationSerializer类被明确定义注册时,作为一个用户对象中的POF的配置文件类型。布尔参数的构造fAutoIndex启用自动索引,必须设置为true。例如:

<user-type>

   <type-id>1001</type-id>

   <class-name>com.examples.Person</class-name>

   <serializer>

      <class-name>com.tangosol.io.pof.PofAnnotationSerializer</class-name>

         <init-params>

         <init-param>

            <param-type>int</param-type>

            <param-value>{type-id}</param-value>

         </init-param>

         <init-param>

            <param-type>class</param-type>

            <param-value>{class}</param-value>

         </init-param>

         <init-param>

            <param-type>boolean</param-type>

            <param-value>true</param-value>

         </init-param>

      </init-params>

   </serializer>

</user-type>

19.3.4提供定制编解码器

编解码器允许序列化或反序列化之前或之后要执行的代码。该编解码器定义了如何编码和解码的便携式的物业(,使用PofWriterPofReader接口)。编解码器通常用于可能迷路时被反序列化或序列化一个对象显式调用特定的方法之前在PofWriter接口的具体实现。

要创建一个编解码器,创建一个类实现com.tangosol.io.pof.reflect.Codec接口。下面的例子演示了一个编解码器定义一个链表类型的具体实施:

public static class LinkedListCodec implements Codec

   {

   public Object decode(PofReader in, int index) throws IOException

      {

      return (List<String>) in.readCollection(index, new LinkedList<String>());

      }

   public void encode(PofWriter out, int index, Object value) throws IOException

      {

      out.writeCollection(index, (Collection) value);

      {

   }

属性分配一个编解码器的编解码器,输入作为@ PortableProperty注释的成员。如果未指定的编解码器,使用一个默认的编解码器(DefaultCodec)。下面的例子演示了分配上述LinkedListCodec编解码器:

@PortableProperty(codec = LinkedListCodec.class)

private List<String> m_aliases;

19.4使用POF提取和POF更新程式

在Coherence中,ValueExtractorValueUpdater接口,用于提取和存储在缓存中的对象的更新值。该PofExtractorPofUpdater接口利用的POF索引状态,而不需要经过完整的序列化/反序列化程序来提取或更新一个对象。

PofExtractorPofUpdater添加非原始类型的连贯性与灵活性。对于许多扩展客户端的情况下,在网格中一个相应的Java类不再需要。由于POF榨汁机和POF更新程序,可以浏览的二进制,整个键/值不具有反序列化为对象的形式。这意味着索引,可以实现通过简单的使用POF提取的拉指数值。然而,仍然需要相应的Java类时使用缓存店。在这种情况下,反序列化版本的关键和价值传递给缓存存储写入到后端。

19.4.1导航POF对象

由于POF的索引这样的事实,这是可能的,包括:遍历二进制提取或更新一个特定的元素。这是责任的PofNavigator接口遍历POF值对象,并返回所需的POF值对象。开箱,相干提供了SimplePofPath的类,它可以浏览一个基于POF价值的整数索引。在最简单的形式,提供的索引属性提取/ updated.Consider下面的例子:

public class Contact

        implements PortableObject

    {

    ...

    // ----- PortableObject interface ---------------------------------------

 

    /**

    * {@inheritDoc}

    */

    public void readExternal(PofReader reader)

            throws IOException

        {

        m_sFirstName     = reader.readString(FIRSTNAME);

        m_sLastName      = reader.readString(LASTNAME);

        m_addrHome       = (Address) reader.readObject(HOME_ADDRESS);

        m_addrWork       = (Address) reader.readObject(WORK_ADDRESS);

        m_mapPhoneNumber = reader.readMap(PHONE_NUMBERS, null);

        }

 

    /**

    * {@inheritDoc}

    */

    public void writeExternal(PofWriter writer)

            throws IOException

        {

        writer.writeString(FIRSTNAME, m_sFirstName);

        writer.writeString(LASTNAME, m_sLastName);

        writer.writeObject(HOME_ADDRESS, m_addrHome);

        writer.writeObject(WORK_ADDRESS, m_addrWork);

        writer.writeMap(PHONE_NUMBERS, m_mapPhoneNumber);

        }

 

    ....

 

    // ----- constants -------------------------------------------------------

 

    /**

    * The POF index for the FirstName property

    */

    public static final int FIRSTNAME = 0;

 

    /**

    * The POF index for the LastName property

    */

    public static final int LASTNAME = 1;

 

    /**

    * The POF index for the HomeAddress property

    */

    public static final int HOME_ADDRESS = 2;

 

    /**

    * The POF index for the WorkAddress property

    */

    public static final int WORK_ADDRESS = 3;

 

    /**

    * The POF index for the PhoneNumbers property

    */

    public static final int PHONE_NUMBERS = 4;

 

    ...

}

请注意,有一个恒定的被写入到从POF流的每个数据成员。这是一个很好的做法,因为它简化了写您的序列化例程,使得它更容易使用的POF提取和POF更新程序。每个索引标签,就变得容易多想想索引。正如上文所述,在最简单的情况下,工作地址可以拉出接触的由使用WORK_ADDRESS指数。的SimplePofPath还允许使用int数组遍历PofValues​​的。例如,为了得到工作地址使用[WORK_ADDRESSZIP]的邮政编码。在下面更详细讨论的例子。

19.4.2使用POF提取器

POF提取时,通常使用查询缓存,提高查询性能。例如,使用上面的类展示,查询缓存中的所有联系人的姓Jones,查询如下:

ValueExtractor veName = new PofExtractor(String.class, Contact.LASTNAME);

Filter         filter = new EqualsFilter(veName, "Jones");

 

// find all entries that have a last name of Jones

Set setEntries = cache.entrySet(filter);

另外,在上述情况下, PofExtractor有一个便利的构造函数,它使用一个SimplePofPath检索奇异性指数,在我们的例子中在Contact.LASTNAME索引。要找到所有接触区域代码01803 ,查询如下:

ValueExtractor veZip = new PofExtractor(

   String.class, new SimplePofPath(new int[] {Contact.WORK_ADDRESS, Address.ZIP}));

 

Filter filter = new EqualsFilter(veZip, "01803");

 

// find all entries that have a work address in the 01803 zip code

Set setEntries  = cache.entrySet(filter);

请注意,在前面的例子中,第一个参数PofExtractor构造类提取的值或空。 POF采用了紧凑的形式在序列化值时,可能的原因是通过类型信息。例如,某些数值表示作为特殊的POF内在类型的类型表示的值。其结果是, POF要求的值的接收机中,有不同的隐式知识。 PofExtractor使用在构造函数中提供不同信息源的类。如果类是空的, PofExtractor的类型推断的序列化状态,但所提取的类型可能不同于预期的类型。 String类型,事实上,可以正确地推断出从POF流,所以空在前面的例子已经足够。然而,在一般情况下,空值不应该被使用。

19.4.3使用POF更新程式

POF更新程序以同样的方式作为POF提取的除了他们的值更新的对象,而不是提取工作。史密斯与琼斯的姓氏,要改变所有条目使用的UpdaterProcessor类如下:

ValueExtractor veName  = new PofExtractor(String.class, Contact.LASTNAME);

Filter         filter  = new EqualsFilter(veName, "Jones");

ValueUpdater   updater = new PofUpdater(Contact.LASTNAME);

// find all Contacts with the last name Jones and change them to have the last

// name "Smith"

cache.invokeAll(filter, new UpdaterProcessor(updater, "Smith"));

注意事项:
而这些例子上运行基于字符串值,此功能工作任何POF编码值。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:643952次
    • 积分:5444
    • 等级:
    • 排名:第5200名
    • 原创:82篇
    • 转载:0篇
    • 译文:26篇
    • 评论:50条
    最新评论