Spring Core 之 The IOC Container Overview(IOC容器概述)

The IoC Container

一、Overview

1、Introduction to the Spring IoC

IoC is also known as dependency injection .It is a process whereby objects define their dependencies (that is,the other objects they work with) only through constructor arguments,arguments to a factory method ,or properties that are set on the object instance after it is constructed or returned from a factory method.The container then injects those dependencies when it creates the bean.This process is fundamentally the inverse(hence the name,inversion of Control) of the bean itself controlling the instantiation or location of its dependencies by using direct construction of classes or a mechanism such as the Service Locator pattern.

IoC也称为依赖注入,这是一个过程,其中对象仅通过构造函数,工厂方法的参数,或在对象实例被构造或从工厂方法返回后在对象上设置的属性定义它们的依赖(即它们使用的其他对象)。然后容器在创建bean时注入这些依赖。这个过程基本上是bean本身控制实例化或通过使用类的直接构造如服务器模式之类的机制来定位其依赖项的逆过程(因此得名,控制反转)。

The org.springframework.beans and org.springframework.context packages are the basis for Spring Framework’s IoC container. The BeanFactory interface provides an advanced configuration mechanism capable of managing any type of object. ApplicationContext is a sub-interface of BeanFactory. It adds:

  • Easier integration with Spring’s AOP features

  • Message resource handling (for use in internationalization)

  • Event publication

  • Application-layer specific contexts such as the WebApplicationContext for use in web applications.

org.springframework.beans 和org.springframework.context包是spring IOC容器的基础。BeanFactory接口提供了能够管理任何类型对象的高级配置机制。ApplicationContext是BeanFactory的一个子接口,它增加了更易于与AOP整合、消息资源处理、事件发布、应用层特定上下文,例如用于 Web 应用程序的 WebApplicationContext的特点。

In short, the BeanFactory provides the configuration framework and basic functionality, and the ApplicationContext adds more enterprise-specific functionality. The ApplicationContext is a complete superset of the BeanFactory.

简而言之,BeanFactory 提供配置框架和基本功能,而ApplicationContext 添加了更多企业级特定的功能。ApplicationContext 是 BeanFactory 的完整超集。

In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and managed by a Spring IoC container. Otherwise, a bean is simply one of many objects in your application. Beans, and the dependencies among them, are reflected in the configuration metadata used by a container.

在 Spring 中,构成应用程序主干并由 Spring IoC 容器管理的对象称为 bean。一个bean就是一个被实例化、装配和由spring IOC容器管理的对象。除此之外,bean 只是应用程序中众多对象之一。 Bean 以及它们之间的依赖关系反映在容器使用的配置元数据中。

2、Container Overview

The org.springframework.context.ApplicationContext interface represents the Spring IoC container and is responsible for instantiating, configuring, and assembling the beans. The container gets its instructions on what objects to instantiate, configure, and assemble by reading configuration metadata. The configuration metadata is represented in XML, Java annotations, or Java code. It lets you express the objects that compose your application and the rich interdependencies between those objects.

org.springframework.context.ApplicationContext 接口代表 Spring IoC 容器,负责实例化、配置和组装 bean。容器通过读取配置元数据来获取有关要实例化、配置和组装哪些对象的指令。配置元数据以 XML、Java 注释或 Java 代码表示。它可以让您表达组成应用程序的对象以及这些对象之间丰富的相互依赖关系。

Several implementations of the ApplicationContext interface are supplied with Spring. In stand-alone applications, it is common to create an instance of ClassPathXmlApplicationContext or FileSystemXmlApplicationContext. While XML has been the traditional format for defining configuration metadata, you can instruct the container to use Java annotations or code as the metadata format by providing a small amount of XML configuration to declaratively enable support for these additional metadata formats.

Spring 提供了 ApplicationContext 接口的几个实现。在独立应用程序中,通常创建 ClassPathXmlApplicationContext 或 FileSystemXmlApplicationContext 的实例。虽然 XML 一直是定义配置元数据的传统格式,您可以通过提供少量 XML 配置来声明性地启用对这些附加元数据格式的支持,从而指示容器使用 Java 注释或代码作为元数据格式。

In most application scenarios, explicit user code is not required to instantiate one or more instances of a Spring IoC container. For example, in a web application scenario, a simple eight (or so) lines of boilerplate web descriptor XML in the web.xml file of the application typically suffices (see Convenient ApplicationContext Instantiation for Web Applications).

在大部分应用场景,实例化 Spring IoC 容器的一个或多个实例不需要显式的用户代码。例如,在 Web 应用程序场景中,应用程序的 web.xml 文件中简单的八行(左右)样板 Web 描述符 XML 通常就足够了(请参阅 Web 应用程序的便捷 ApplicationContext 实例化)。

The following diagram shows a high-level view of how Spring works. Your application classes are combined with configuration metadata so that, after the ApplicationContext is created and initialized, you have a fully configured and executable system or application.

下图显示了 Spring 如何工作的高级视图。您的应用程序类与配置元数据相结合,以便在创建和初始化 ApplicationContext 之后,你有一个完全配置和可执行的系统或应用程序。
在这里插入图片描述

Configuration Metadata
As the preceding diagram shows, the Spring IoC container consumes a form of configuration metadata. This configuration metadata represents how you, as an application developer, tell the Spring container to instantiate, configure, and assemble the objects in your application.

Configuration metadata is traditionally supplied in a simple and intuitive XML format, XML-based metadata is not the only allowed form of configuration metadata. The Spring IoC container itself is totally decoupled from the format in which this configuration metadata is actually written. These days, many developers choose Java-based configuration for their Spring applications.

如上图所示,SpringIOC容器会使用一种配置元数据的形式。配置元数据体现了你作为一个开发者告诉Spring容器要如何实例化、配置和装配你应用程序中的各个对象。
配置元数据传统上以简单直观的 XML 格式提供,基于XML的元数据并不是唯一被允许的配置元数据方式,Spring IOC容器本身与实际写入此配置元数据的方式完全解耦。当前,许多开发者选择基于java代码配置他们的Spring应用。

Spring configuration consists of at least one and typically more than one bean definition that the container must manage. XML-based configuration metadata configures these beans as elements inside a top-level element. Java configuration typically uses @Bean-annotated methods within a @Configuration class.

These bean definitions correspond to the actual objects that make up your application. Typically, you define service layer objects, data access objects (DAOs), presentation objects such as Struts Action instances, infrastructure objects such as Hibernate SessionFactories, JMS Queues, and so forth. Typically, one does not configure fine-grained domain objects in the container, because it is usually the responsibility of DAOs and business logic to create and load domain objects. However, you can use Spring’s integration with AspectJ to configure objects that have been created outside the control of an IoC container.

Spring 配置包含至少一个并且通常不止一个容器必须管理的 bean 定义。基于 XML 的配置元数据将这些 bean 配置为顶级 元素内的 元素,Java 配置通常在@Configuration 类中使用@Bean 注释的方法。

这些bean定义符合构成你应用的实际对象,通常,你定义服务层对象、数据访问对象 (DAO)、表示对象(例如 Struts Action 实例)、基础设施对象(例如 Hibernate SessionFactories、JMS 队列等)。通常,不会在容器中配置细粒度的域对象,因为创建和加载域对象通常是 DAO 和业务逻辑的责任。但是,您可以使用 Spring 与 AspectJ 的集成来配置在 IoC 容器控制之外创建的对象。

The following example shows the basic structure of XML-based configuration metadata:

以下示例显示了基于 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">

    <bean id="myBean" class="com.test.MyBean">  
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <bean id="..." class="...">
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions go here -->

</beans>

The id attribute is a string that identifies the individual bean definition.
The class attribute defines the type of the bean and uses the fully qualified
classname.

id 属性是一个字符串,用于标识单个 bean 定义。
class 属性定义了 bean 的类型并使用了全限定类名称。

Instantiating a Container

The location path or paths supplied to an ApplicationContext constructor are resource strings that let the container load configuration metadata from a variety of external resources,such as the local file system,the Java CLASSPATH,and so on.

一个或多个位置路径提供给 ApplicationContext 构造函数,这些资源字符串使容器从各种外部资源(例如本地文件系统、Java CLASSPATH 等)加载配置元数据。

ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

The following example shows the service layer objects (services.xml) configuration file:
以下示例显示了服务层对象 (services.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">

    <!-- services -->

    <bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
        <property name="accountDao" ref="accountDao"/>
        <property name="itemDao" ref="itemDao"/>
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions for services go here -->

</beans>

The following example shows the data access objects daos.xml file:
以下示例显示了数据访问对象 daos.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">

    <bean id="accountDao"
        class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions for data access objects go here -->

</beans>

In the preceding example, the service layer consists of the PetStoreServiceImpl class and two data access objects of the types JpaAccountDao and JpaItemDao (based on the JPA Object-Relational Mapping standard). The property name element refers to the name of the JavaBean property, and the ref element refers to the name of another bean definition. This linkage between id and ref elements expresses the dependency between collaborating objects.

在前面的示例中,服务层由 PetStoreServiceImpl 类和 JpaAccountDao 和 JpaItemDao 类型的两个数据访问对象组成(基于 JPA Object-Relational Mapping 标准)。property 元素的name 是指 JavaBean 属性的名称,ref 属性是指另一个 bean 定义的名称。 id 和 ref 元素之间的这种联系表达了协作对象之间的依赖关系。

Composing XML-based Configuration Metadata 编写基于 XML 的配置元数据

It can be useful to have bean definitions span multiple XML files. Often, each individual XML configuration file represents a logical layer or module in your architecture.

让 bean 定义跨越多个 XML 文件会很有用。通常,每个单独的 XML 配置文件代表架构中的一个逻辑层或模块。

You can use the application context constructor to load bean definitions from all these XML fragments. This constructor takes multiple Resource locations, as was shown in the previous section. Alternatively, use one or more occurrences of the element to load bean definitions from another file or files. The following example shows how to do so:

您可以使用应用程序上下文构造函数从所有这些 XML 片段加载 bean 定义。此构造函数采用多个 Resource 位置,如上一节所示。或者,使用一个或多个 元素从另一个文件或多个文件加载 bean 定义。以下示例显示了如何执行此操作:

<beans>
    <import resource="services.xml"/>
    <import resource="resources/messageSource.xml"/>
    <import resource="/resources/themeSource.xml"/>

    <bean id="bean1" class="..."/>
    <bean id="bean2" class="..."/>
</beans>

In the preceding example, external bean definitions are loaded from three files: services.xml, messageSource.xml, and themeSource.xml. All location paths are relative to the definition file doing the importing, so services.xml must be in the same directory or classpath location as the file doing the importing, while messageSource.xml and themeSource.xml must be in a resources location below the location of the importing file. As you can see, a leading slash is ignored. However, given that these paths are relative, it is better form not to use the slash at all. The contents of the files being imported, including the top level element, must be valid XML bean definitions, according to the Spring Schema.

在前面的示例中,外部 bean 定义是从三个文件加载的:services.xml、messageSource.xml 和 themeSource.xml。所有位置路径都相对于执行导入的定义文件,因此 services.xml 必须与执行导入的文件位于同一目录或类路径位置,而 messageSource.xml 和 themeSource.xml 必须位于导入文件位置下方的资源位置。如您所见,前导斜杠被忽略。但是,鉴于这些路径是相对的,最好根本不使用斜杠。根据 Spring 规约,被导入文件的内容,包括顶级 元素,必须是有效的 XML bean 定义。

Using the Container

The ApplicationContext is the interface for an advanced factory capable of maintaining a registry of different beans and their dependencies. By using the method T getBean(String name, Class requiredType), you can retrieve instances of your beans.

The ApplicationContext lets you read bean definitions and access them, as the following example shows:

ApplicationContext 是能够维护不同 bean 及其依赖项的注册表的高级工厂接口。通过使用getBean(String name, Class requiredType)方法,你可以取到bean的实例。
ApplicationContext可以使你读取bean定义和访问它们,正如下面的例子所示:

// 创建和配置beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

// 取出被装配好的实例
PetStoreService service = context.getBean("petStore", PetStoreService.class);

// 使用装配好的实例
List<String> userList = service.getUsernameList();

You can then use getBean to retrieve instances of your beans. The ApplicationContext interface has a few other methods for retrieving beans, but, ideally, your application code should never use them. Indeed, your application code should have no calls to the getBean() method at all and thus have no dependency on Spring APIs at all. For example, Spring’s integration with web frameworks provides dependency injection for various web framework components such as controllers and JSF-managed beans, letting you declare a dependency on a specific bean through metadata (such as an autowiring annotation).

你可以使用 getBean 来检索 bean 的实例。ApplicationContext 接口也有一些其他的方法来检索 bean,但是,理想情况下,您的应用程序代码不应使用它们。实际上,您的应用程序代码根本不应该调用 getBean() 方法,因此完全不依赖 Spring API。例如,Spring 与 Web 框架的集成为各种 Web 框架组件(例如控制器和 JSF 管理的 bean)提供了依赖注入,让您通过元数据(例如自动装配注释)声明对特定 bean 的依赖。

3、Bean Overview

A Spring IoC container manages one or more beans. These beans are created with the configuration metadata that you supply to the container (for example, in the form of XML definitions).

一个Spring IOC容器管理一个或多个bean。这些bean基于你提供给容器的配置元数据(例如xml配置文件)被创建。

Within the container itself, these bean definitions are represented as BeanDefinition objects, which contain (among other information) the following metadata:

在容器本身内部,这些bean定义表现为BeanDefinition对象,包含有如下元数据信息:

  • A package-qualified class name: typically, the actual implementation class of the bean being defined.

  • 一个包限定类名,通常,是定义的 bean 的实际实现类。

  • Bean behavioral configuration elements, which state how the bean should behave in the container (scope, lifecycle callbacks, and so forth).

  • Bean 行为配置元素,它说明 bean 在容器中的行为方式(范围、生命周期回调等)。

  • References to other beans that are needed for the bean to do its work. These references are also called collaborators or dependencies.

  • 对 bean 执行其工作所需的其他 bean 的引用。这些引用也称为协作者或依赖项。

  • Other configuration settings to set in the newly created object — for example, the size limit of the pool or the number of connections to use in a bean that manages a connection pool.

  • 要在新创建的对象中设置的其他配置设置 — 例如,池的大小限制或在管理连接池的 bean 中使用的连接数。

In addition to bean definitions that contain information on how to create a specific bean, the ApplicationContext implementations also permit the registration of existing objects that are created outside the container (by users). This is done by accessing the ApplicationContext’s BeanFactory through the getBeanFactory() method, which returns the BeanFactory DefaultListableBeanFactory implementation. DefaultListableBeanFactory supports this registration through the registerSingleton(…) and registerBeanDefinition(…) methods. However, typical applications work solely with beans defined through regular bean definition metadata.

除了包含有关如何创建特定 bean 的信息的 bean 定义之外,ApplicationContext 实现还允许注册在容器外部(由用户)创建的现有对象。这是通过 getBeanFactory() 方法访问 ApplicationContext 的 BeanFactory 来完成的,该方法返回 BeanFactory DefaultListableBeanFactory 实现。DefaultListableBeanFactory 通过 registerSingleton(…) 和 registerBeanDefinition(…) 方法支持这种注册。但是,通常应用程序仅使用通过常规 bean 定义元数据进行bean的定义。

Naming Beans

Every bean has one or more identifiers.These identifiers must be unique within the container that hosts the bean.A bean usually has only one identifier.However,if it requires more than one,the extra ones can be considered aliases.

每个 bean 都有一个或多个标识符。这些标识符在承载该 bean 的容器中必须是唯一的。一个 bean 通常只有一个标识符。但是,如果它需要多个,可以被认为是额外的别名。

In XML-based configuration metadata,you use the id attribute,the name attribute ,or both to specify the bean identifiers.The id attribute lets you specify exactly one id.Conventionally,thes names are alphannumeric(‘myBean’,‘someService’,etc),but they can contain special characters as well.If you want to introduce other aliases for the bean,you can also specify them in the name attribute.separated by a comma (,), semicolon (😉, or white space. As a historical note, in versions prior to Spring 3.1, the id attribute was defined as an xsd:ID type, which constrained possible characters. As of 3.1, it is defined as an xsd:string type. Note that bean id uniqueness is still enforced by the container, though no longer by XML parsers.

在基于 XML 的配置元数据中,您使用 id 属性、name 属性、或两者都指定 bean 标识符。id 属性允许您指定一个 id。按照惯例,这些名称是字母数字(‘myBean’、‘someService’ 等),但它们也可以包含特殊字符。如果要为 bean 引入其他别名,也可以在 name 属性中指定它们,用逗号 (,)、分号 (😉 或空格分隔。作为历史记录,在 Spring 3.1 之前的版本中, id 属性被定义为 xsd:ID 类型,这限制了可能的字符。从 3.1 开始,它被定义为 xsd:string 类型。请注意,bean id 唯一性仍由容器强制执行,但不再由 XML 解析器强制执行。

You are not required to supply a name or an id for a bean. If you do not supply a name or id explicitly, the container generates a unique name for that bean. However, if you want to refer to that bean by name, through the use of the ref element or a Service Locator style lookup, you must provide a name. Motivations for not supplying a name are related to using inner beans and autowiring collaborators.

您可以不为 bean 提供名称或 ID。如果您未明确提供名称或 ID,则容器会为该 bean 生成一个唯一名称。但是,如果您想通过名称引用该 bean,通过使用 ref 元素或服务定位器样式查找,你必须提供一个名字。不提供名称的动机与使用内部 bean 和自动装配协作者有关。

Instantiating Beans
A bean definition is essentially a recipe for creating one or more objects. The container looks at the recipe for a named bean when asked and uses the configuration metadata encapsulated by that bean definition to create (or acquire) an actual object.

bean 定义本质上是创建一个或多个对象的配方。当被询问时,容器会查看命名 bean 的配方,并使用该 bean 定义封装的配置元数据来创建(或获取)实际对象。

If you use XML-based configuration metadata, you specify the type (or class) of object that is to be instantiated in the class attribute of the element. This class attribute (which, internally, is a Class property on a BeanDefinition instance) is usually mandatory. You can use the Class property in one of two ways:

如果使用基于 XML 的配置元数据,则要在 元素的 class 属性中指定实例化对象的类型(或类)。这个类属性(在内部,它是 BeanDefinition 实例上的 Class 属性)通常是强制性的。可以通过以下两种方式之一使用 Class 属性:

  • Typically, to specify the bean class to be constructed in the case where the container itself directly creates the bean by calling its constructor reflectively, somewhat equivalent to Java code with the new operator.

  • 通常,在容器本身通过反射调用其构造函数直接创建 bean 的情况下,指定要构造的 bean 类,有点等同于带有 new 运算符的 Java 代码。

  • To specify the actual class containing the static factory method that is invoked to create the object, in the less common case where the container invokes a static factory method on a class to create the bean. The object type returned from the invocation of the static factory method may be the same class or another class entirely.

  • 要指定包含被调用以创建对象的静态工厂方法的实际类,在不太常见的情况下,容器调用类上的静态工厂方法来创建 bean。调用静态工厂方法返回的对象类型可能是同一个类,也可能完全是另一个类。

Instantiation with a Constructor
When you create a bean by the constructor approach, all normal classes are usable by and compatible with Spring. That is, the class being developed does not need to implement any specific interfaces or to be coded in a specific fashion. Simply specifying the bean class should suffice. However, depending on what type of IoC you use for that specific bean, you may need a default (empty) constructor.
当您通过构造函数方法创建 bean 时,所有普通类都可以被 Spring 使用并与 Spring 兼容。也就是说,正在开发的类不需要实现任何特定的接口或以特定的方式进行编码。只需指定 bean 类就足够了。但是,根据您对该特定 bean 使用的 IoC 类型,您可能需要一个默认(空)构造函数。

The Spring IoC container can manage virtually any class you want it to manage. It is not limited to managing true JavaBeans. Most Spring users prefer actual JavaBeans with only a default (no-argument) constructor and appropriate setters and getters modeled after the properties in the container. You can also have more exotic non-bean-style classes in your container. If, for example, you need to use a legacy connection pool that absolutely does not adhere to the JavaBean specification, Spring can manage it as well.

With XML-based configuration metadata you can specify your bean class as follows:

<bean id="exampleBean" class="examples.ExampleBean"/>

<bean name="anotherExample" class="examples.ExampleBeanTwo"/>

Instantiation with a Static Factory Method

When defining a bean that you create with a static factory method, use the class attribute to specify the class that contains the static factory method and an attribute named factory-method to specify the name of the factory method itself. You should be able to call this method (with optional arguments, as described later) and return a live object, which subsequently is treated as if it had been created through a constructor. One use for such a bean definition is to call static factories in legacy code.

定义使用静态工厂方法创建的 bean 时,使用 class 属性指定包含静态工厂方法的类,并使用名为 factory-method 的属性指定工厂方法本身的名称。您应该能够调用此方法(带有可选参数,如下所述)并返回一个活动对象,随后将其视为通过构造函数创建的。这种 bean 定义的一种用途是在遗留代码中调用静态工厂。

The following bean definition specifies that the bean be created by calling a factory method. The definition does not specify the type (class) of the returned object, only the class containing the factory method. In this example, the createInstance() method must be a static method. The following example shows how to specify a factory method:

以下 bean 定义指定通过调用工厂方法来创建 bean。定义中没有指定返回对象的类型(类),只指定包含工厂方法的类。在这个例子中,createInstance() 方法必须是一个静态方法。以下示例显示了如何指定工厂方法:

<bean id="clientService"
    class="examples.ClientService"
    factory-method="createInstance"/>

The following example shows a class that would work with the preceding bean definition:

public class ClientService {
    private static ClientService clientService = new ClientService();
    private ClientService() {}

    public static ClientService createInstance() {
        return clientService;
    }
}

Instantiation by Using an Instance Factory Method

Similar to instantiation through a static factory method, instantiation with an instance factory method invokes a non-static method of an existing bean from the container to create a new bean. To use this mechanism, leave the class attribute empty and, in the factory-bean attribute, specify the name of a bean in the current (or parent or ancestor) container that contains the instance method that is to be invoked to create the object. Set the name of the factory method itself with the factory-method attribute. The following example shows how to configure such a bean:

<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>

<!-- the bean to be created via the factory bean -->
<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>

The following example shows the corresponding class:

public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值