Spring Framework学习之路(10)--- 依赖注入

1.4. Dependencies

1.4. 依赖

  A typical enterprise application does not consist of a single object (or bean in the Spring parlance). Even the simplest application has a few objects that work together to present what the end-user sees as a coherent application. This next section explains how you go from defining a number of bean definitions that stand alone to a fully realized application where objects collaborate to achieve a goal.
  典型的企业应用程序不包含单个对象(或Spring的说法中的bean)。 即使最简单的应用程序也有几个对象一起工作来展示最终用户将其视为一个连贯的应用程序。 下一节将介绍如何从定义许多独立的bean定义到完全实现的应用程序,在这些应用程序中对象进行协作以实现目标。

1.4.1. Dependency Injection

1.4.1. 依赖注入

  Dependency injection (DI) 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 (IoC), of the bean itself controlling the instantiation or location of its dependencies on its own by using direct construction of classes, or the Service Locator pattern.
  **依赖注入(DI)**是一个过程,通过这种过程,对象可以通过构造函数参数,工厂方法参数或者在构造或返回对象实例后设置的属性来定义它们的依赖关系,也就是说,它们使用的其他对象从工厂方法。容器在创建bean时会注入这些依赖关系。这个过程从根本上说是相反的,因此名为控制反转(IoC),它本身通过使用类的直接构造或服务定位器模式来控制它自己的依赖关系的实例化或位置。

  Code is cleaner with the DI principle and decoupling is more effective when objects are provided with their dependencies. The object does not look up its dependencies, and does not know the location or class of the dependencies. As such, your classes become easier to test, in particular when the dependencies are on interfaces or abstract base classes, which allow for stub or mock implementations to be used in unit tests.
  代码与DI原理相比更加清晰,并且在对象提供依赖关系时解耦更有效。该对象不查找其依赖项,并且不知道依赖项的位置或类。因此,您的类变得更容易测试,特别是当依赖关系位于接口或抽象基类上时,它们允许在单元测试中使用存根或模拟实现。

  DI exists in two major variants, Constructor-based dependency injection and Setter-based dependency injection.
  DI存在两种主要的变体,基于构造器的依赖注入基于Setter的依赖注入

Constructor-based dependency injection
基于构造函数的依赖注入

  Constructor-based DI is accomplished by the container invoking a constructor with a number of arguments, each representing a dependency. Calling a static factory method with specific arguments to construct the bean is nearly equivalent, and this discussion treats arguments to a constructor and to a static factory method similarly. The following example shows a class that can only be dependency-injected with constructor injection. Notice that there is nothing special about this class, it is a POJO that has no dependencies on container specific interfaces, base classes or annotations.
  基于构造器的DI通过容器调用具有多个参数的构造函数完成,每个参数表示一个依赖项。调用具有特定参数的静态工厂方法来构造bean几乎是等价的,本讨论类似地将参数视为构造函数和静态工厂方法。以下示例显示了只能通过构造函数注入进行依赖注入的类。请注意,这个类没有什么特别之处,它是一个POJO,它不依赖于容器特定的接口,基类或注释。

public class SimpleMovieLister {
    // the SimpleMovieLister has a dependency on a MovieFinder
    private MovieFinder movieFinder;
    // a constructor so that the Spring container can inject a MovieFinder
    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
    // business logic that actually uses the injected MovieFinder is omitted...
}

  Notice that there is nothing special about this class. It is a POJO that has no dependencies on container specific interfaces, base classes or annotations.
  注意,该类没有什么特别的。 它是一个POJO,不依赖于特定于容器的接口,基类或注释。

Constructor argument resolution
构造函数参数解析

  Constructor argument resolution matching occurs using the argument’s type. If no potential ambiguity exists in the constructor arguments of a bean definition, then the order in which the constructor arguments are defined in a bean definition is the order in which those arguments are supplied to the appropriate constructor when the bean is being instantiated. Consider the following class:
  使用参数的类型发生构造函数参数解析匹配。 如果bean定义的构造函数参数中没有可能存在的歧义,那么在bean定义中定义构造函数参数的顺序是当实例化bean时将这些参数提供给相应构造函数的顺序。 考虑以下类:

package x.y;
public class ThingOne {
 	public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
 	// ...
 	}
}

  Assuming that the ThingTwo and ThingThree classes are not related by inheritance, no potential ambiguity exists. Thus, the following configuration works fine, and you do not need to specify the constructor argument indexes or types explicitly in the <constructor-arg /> element.
  假设 ThingTwoThingThree 类没有继承关系,则不存在潜在的歧义。 因此,以下配置工作正常,您无需在 <constructor-arg/> 元素中显式指定构造函数参数索引或类型。

<beans>
	<bean id="beanOne" class="x.y.ThingOne">
		<constructor-arg ref="beanTwo"/>
 		<constructor-arg ref="beanThree"/>
 	</bean>
 	<bean id="beanTwo" class="x.y.ThingTwo"/>
 	<bean id="beanThree" class="x.y.ThingThree"/>
</beans>

  When another bean is referenced, the type is known, and matching can occur (as was the case with the preceding example). When a simple type is used, such as <value>true</value>, Spring cannot determine the type of the value, and so cannot match by type without help. Consider the following class:
  当引用另一个bean时,类型是已知的,并且可以发生匹配(就像前面的例子那样)。当使用简单类型时,例如 <value> true </value>,Spring无法确定值的类型,因此无法在没有帮助的情况下按类型进行匹配。考虑以下类:

package examples;
public class ExampleBean {
    // Number of years to calculate the Ultimate Answer
    private int years;
    // The Answer to Life, the Universe, and Everything
    private String ultimateAnswer;
    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }
}

Constructor argument type matching
构造函数参数类型匹配

  In the preceding scenario, the container can use type matching with simple types if you explicitly specify the type of the constructor argument using the type attribute. For example:
  在前面的场景中,如果使用type属性显式指定构造函数参数的类型,则容器可以使用简单类型的类型匹配。 例如:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg type="int" value="7500000"/>
    <constructor-arg type="java.lang.String" value="42"/>
</bean>

Constructor argument index
构造函数参数索引

  Use the index attribute to specify explicitly the index of constructor arguments. For example:
  使用index属性明确指定构造函数参数的索引。 例如:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg index="0" value="7500000"/>
    <constructor-arg index="1" value="42"/>
</bean>

In addition to resolving the ambiguity of multiple simple values, specifying an index resolves ambiguity where a constructor has two arguments of the same type. .
除了解决多个简单值的歧义之外,指定索引还解决了构造函数具有相同类型的两个参数的不明确性。

Note that the index is 0 based
请注意,该索引是基于0的。

Constructor argument name
构造函数参数名称
  You can also use the constructor parameter name for value disambiguation:
  您也可以使用构造函数参数名称进行值消歧:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg name="years" value="7500000"/>
    <constructor-arg name="ultimateAnswer" value="42"/>
</bean>

  Keep in mind that to make this work out of the box your code must be compiled with the debug flag enabled so that Spring can look up the parameter name from the constructor. If you can’t compile your code with debug flag (or don’t want to) you can use @ConstructorProperties JDK annotation to explicitly name your constructor arguments. The sample class would then have to look as follows:
  请记住,要使这项工作脱离框架,您的代码必须在启用了调试标志的情况下编译,以便Spring可以从构造函数中查找参数名称。 如果你不能用调试标志编译你的代码(或者不想),你可以使用@ConstructorProperties JDK注解来明确地命名你的构造函数参数。 示例类将不得不如下所示:

package examples;
public class ExampleBean {
    // Fields omitted
    @ConstructorProperties({"years", "ultimateAnswer"})
    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }
}

Setter-based dependency injection
基于Setter的依赖注入
  Setter-based DI is accomplished by the container calling setter methods on your beans after invoking a no-argument constructor or no-argument static factory method to instantiate your bean.
  在调用无参数构造函数或无参数静态工厂方法来实例化bean之后,基于Setter的DI通过调用bean上的容器调用setter方法来完成。

  The following example shows a class that can only be dependency-injected using pure setter injection. This class is conventional Java. It is a POJO that has no dependencies on container specific interfaces, base classes or annotations.
  以下示例显示了一个只能使用纯setter注入进行依赖注入的类。 这个类是传统的Java。 这是一个POJO,它不依赖于容器特定的接口,基类或注释。

public class SimpleMovieLister {
    // the SimpleMovieLister has a dependency on the MovieFinder
    private MovieFinder movieFinder;
    // a setter method so that the Spring container can inject a MovieFinder
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
    // business logic that actually uses the injected MovieFinder is omitted...
}

  The ApplicationContext supports constructor-based and setter-based DI for the beans it manages. It also supports setter-based DI after some dependencies have already been injected through the constructor approach. You configure the dependencies in the form of a BeanDefinition, which you use in conjunction with PropertyEditor instances to convert properties from one format to another. However, most Spring users do not work with these classes directly (i.e., programmatically) but rather with XML bean definitions, annotated components (i.e., classes annotated with @Component,@Controller, etc.), or @Bean methods in Java-based @Configuration classes. These sources are then converted internally into instances of BeanDefinition and used to load an entire Spring IoC container instance.
  ApplicationContext支持它所管理的bean的基于构造函数和基于setter的DI。 它也支持基于setter的DI之后,一些依赖已经通过构造器方法注入。 您可以以BeanDefinition的形式配置依赖项,您可以使用它与PropertyEditor实例一起使用,以将属性从一种格式转换为另一种格式。 然而,大多数Spring用户不直接使用这些类(即以编程方式),而是使用XML bean定义,带注释的组件(即用@Component,@Controller等注释的类)或基于Java的@Bean方法 @Configuration类。 然后将这些源内部转换为BeanDefinition的实例,并用于加载整个Spring IoC容器实例。

Constructor-based or setter-based DI?
基于构造函数或基于setter的DI?
Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies. Note that use of the @Requiredannotation on a setter method can be used to make the property a required dependency.
既然你可以混合使用基于构造函数和基于setter的DI,对于可选的依赖项,使用强制依赖和构造方法的构造函数或配置方法是一个很好的经验法则。请注意,在setter方法上使用@Required注释可用于使属性成为必需的依赖项。
The Spring team generally advocates constructor injection as it enables one to implement application components as immutable objects and to ensure that required dependencies are not null. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state.As a side note, a large number of constructor arguments is a bad code smell, implying that the class likely has too many responsibilities and should be refactored to better address proper separation of concerns.
Spring团队通常提倡构造器注入,因为它使得可以将应用程序组件实现为不可变对象,并确保所需的依赖项不为空。此外,构造器注入的组件总是以完全初始化的状态返回给客户端(调用)代码。作为一个侧面说明,大量的构造函数参数是一种糟糕的代码异味,这意味着该类可能有太多的责任,应该重构以更好地解决问题的分离问题。
Setter injection should primarily only be used for optional dependencies that can be assigned reasonable default values within the class. Otherwise, not-null checks must be performed everywhere the code uses the dependency. One benefit of setter injection is that setter methods make objects of that class amenable to reconfiguration or re-injection later. Management through JMX MBeans is therefore a compelling use case for setter injection.
Setter注入主要只应用于可选的依赖关系,这些依赖关系可以在类中分配合理的默认值。否则,必须在代码使用依赖关系的任何地方执行非空检查。 setter注入的一个好处是setter方法使得该类的对象可以重新配置或稍后重新注入。通过JMX MBeans进行管理因此是一个引人注目的setter注入用例。
Use the DI style that makes the most sense for a particular class. Sometimes, when dealing with third-party classes for which you do not have the source, the choice is made for you. For example, if a third-party class does not expose any setter methods, then constructor injection may be the only available form of DI.
使用对特定班级最有意义的DI风格。有时候,在处理没有源代码的第三方课程时,可以选择你。例如,如果第三方类不公开任何setter方法,则构造函数注入可能是DI的唯一可用形式。

Dependency resolution process
依赖性解决过程
The container performs bean dependency resolution as follows:
该容器执行bean依赖性解析如下:

  • The ApplicationContext is created and initialized with configuration metadata that describes all the beans. Configuration metadata can be specified via XML, Java code, or annotations.
  • ApplicationContext是使用描述所有Bean的配置元数据创建和初始化的。配置元数据可以通过XML,Java代码或注释来指定。
  • For each bean, its dependencies are expressed in the form of properties, constructor arguments, or arguments to the static-factory method if you are using that instead of a normal constructor. These dependencies are provided to the bean, when the bean is actually created.
  • 对于每个bean,如果使用该属性而不是普通构造函数,则它的依赖关系以属性,构造函数参数或静态工厂方法的参数的形式表示。当bean被实际创建时,这些依赖被提供给bean。
  • Each property or constructor argument is an actual definition of the value to set, or a reference to another bean in the container.
  • 每个属性或构造函数参数都是要设置的值的实际定义,或者是对容器中另一个bean的引用。
  • Each property or constructor argument which is a value is converted from its specified format to the actual type of that property or constructor argument. By default Spring can convert a value supplied in string format to all built-in types, such as int, long, String, boolean, etc.
  • 作为值的每个属性或构造函数参数都从其指定的格式转换为该属性或构造函数参数的实际类型。默认情况下,Spring可以将以字符串格式提供的值转换为所有内置类型,例如int,long,String,boolean等。

  The Spring container validates the configuration of each bean as the container is created. However, the bean properties themselves are not set until the bean is actually created. Beans that are singleton-scoped and set to be pre-instantiated (the default) are created when the container is created. Scopes are defined in Bean scopes. Otherwise, the bean is created only when it is requested. Creation of a bean potentially causes a graph of beans to be created, as the bean’s dependencies and its dependencies’ dependencies (and so on) are created and assigned. Note that resolution mismatches among those dependencies may show up late, i.e. on first creation of the affected bean.
  Spring容器在容器创建时验证每个bean的配置。但是,在实际创建bean之前,bean属性本身不会被设置。 Beans是单身作用域并且被设置为预先实例化的(默认的)是在创建容器时创建的。范围在Bean范围中定义。否则,只有在请求时才创建bean。创建一个bean可能会导致创建一个bean图,因为bean的依赖关系及其依赖关系的依赖关系(等等)被创建和分配。请注意,这些依赖项之间的解决方案不匹配可能会出现较晚,即第一次创建受影响的bean。

Circular dependencies
循环依赖
If you use predominantly constructor injection, it is possible to create an unresolvable circular dependency scenario.
如果您主要使用构造函数注入,则可能会创建一个无法解析的循环依赖方案。
For example: Class A requires an instance of class B through constructor injection, and class B requires an instance of class A through constructor injection. If you configure beans for classes A and B to be injected into each other, the Spring IoC container detects this circular reference at runtime, and throws a BeanCurrentlyInCreationException.
例如:类A需要通过构造函数注入的类B的实例,而类B需要通过构造函数注入的类A的实例。如果将类A和B的Bean配置为相互注入,则Spring IoC容器会在运行时检测到此循环引用,并引发BeanCurrentlyInCreationException。
One possible solution is to edit the source code of some classes to be configured by setters rather than constructors. Alternatively, avoid constructor injection and use setter injection only. In other words, although it is not recommended, you can configure circular dependencies with setter injection.
一种可能的解决方案是编辑某些类的源代码,以便由setter而不是构造器进行配置。或者,避免构造函数注入并仅使用setter注入。换句话说,虽然不推荐,但您可以使用setter注入来配置循环依赖关系。
Unlike the typical case (with no circular dependencies), a circular dependency between bean A and bean B forces one of the beans to be injected into the other prior to being fully initialized itself (a classic chicken/egg scenario).
与典型情况(没有循环依赖)不同,bean A和bean B之间的循环依赖关系迫使其中一个bean在被完全初始化之前注入另一个bean(经典的鸡/鸡蛋场景)。

  You can generally trust Spring to do the right thing. It detects configuration problems, such as references to non-existent beans and circular dependencies, at container load-time. Spring sets properties and resolves dependencies as late as possible, when the bean is actually created. This means that a Spring container which has loaded correctly can later generate an exception when you request an object if there is a problem creating that object or one of its dependencies. For example, the bean throws an exception as a result of a missing or invalid property. This potentially delayed visibility of some configuration issues is why ApplicationContext implementations by default pre-instantiate singleton beans. At the cost of some upfront time and memory to create these beans before they are actually needed, you discover configuration issues when the ApplicationContext is created, not later. You can still override this default behavior so that singleton beans will lazy-initialize, rather than be pre-instantiated.
  通常你可以相信Spring做正确的事情。它在容器加载时检测配置问题,例如引用不存在的bean和循环依赖关系。当bean实际创建时,Spring会尽可能晚地设置属性并解决依赖关系。这意味着,如果在创建该对象或其某个依赖关系时遇到问题,那么请求对象时,正确加载的Spring容器可能会稍后生成异常。例如,由于缺少或无效的属性,bean抛出异常。某些配置问题的可能延迟可见性是为什么ApplicationContext实现默认预先实例化单例bean。为了在实际需要它们之前创建这些bean,需要花费一些预先的时间和内存,您会在ApplicationContext创建时发现配置问题,而不是稍后。您仍然可以重写此默认行为,以便单例bean将会进行延迟初始化,而不是预先实例化。
  If no circular dependencies exist, when one or more collaborating beans are being injected into a dependent bean, each collaborating bean is totally configured prior to being injected into the dependent bean. This means that if bean A has a dependency on bean B, the Spring IoC container completely configures bean B prior to invoking the setter method on bean A. In other words, the bean is instantiated (if not a pre-instantiated singleton), its dependencies are set, and the relevant lifecycle methods (such as a configured init method or the InitializingBean callback method) are invoked.
  如果不存在循环依赖关系,则当一个或多个协作bean被注入到一个依赖bean中时,每个协作bean都被注入到依赖bean之前完全配置。这意味着如果bean A对bean B有依赖性,Spring IoC容器在调用bean A上的setter方法之前完全配置bean B.换句话说,bean被实例化(如果不是预先实例化的单例),它的将设置依赖关系,并调用相关的生命周期方法(例如配置的init方法或InitializingBean回调方法)。

Examples of dependency injection
依赖注入的例子
  The following example uses XML-based configuration metadata for setter-based DI. A small part of a Spring XML configuration file specifies some bean definitions:
  以下示例使用基于setter的DI的基于XML的配置元数据。 Spring XML配置文件的一小部分指定了一些bean定义:

<bean id="exampleBean" class="examples.ExampleBean">
    <!-- setter injection using the nested ref element -->
    <property name="beanOne">
        <ref bean="anotherExampleBean"/>
    </property>
    <!-- setter injection using the neater ref attribute -->
    <property name="beanTwo" ref="yetAnotherBean"/>
    <property name="integerProperty" value="1"/></bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean {
    private AnotherBean beanOne;
    private YetAnotherBean beanTwo;
    private int i;
    public void setBeanOne(AnotherBean beanOne) {
        this.beanOne = beanOne;
    }
    public void setBeanTwo(YetAnotherBean beanTwo) {
        this.beanTwo = beanTwo;
    }
    public void setIntegerProperty(int i) {
        this.i = i;
    }
}

  In the preceding example, setters are declared to match against the properties specified in the XML file. The following example uses constructor-based DI:
  在前面的例子中,setters被声明为与XML文件中指定的属性相匹配。 以下示例使用基于构造函数的DI:

<bean id="exampleBean" class="examples.ExampleBean">
    <!-- constructor injection using the nested ref element -->
    <constructor-arg>
        <ref bean="anotherExampleBean"/>
    </constructor-arg>
    <!-- constructor injection using the neater ref attribute -->
    <constructor-arg ref="yetAnotherBean"/>
    <constructor-arg type="int" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean {
    private AnotherBean beanOne;
    private YetAnotherBean beanTwo;
    private int i;
    public ExampleBean(
        AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
        this.beanOne = anotherBean;
        this.beanTwo = yetAnotherBean;
        this.i = i;
    }
}

  The constructor arguments specified in the bean definition will be used as arguments to the constructor of the ExampleBean.
  在bean定义中指定的构造函数参数将用作ExampleBean构造函数的参数。

  Now consider a variant of this example, where instead of using a constructor, Spring is told to call a static factory method to return an instance of the object:
  现在考虑一下这个例子的一个变种,在这个例子中,不是使用构造函数,而是让Spring调用一个静态工厂方法来返回对象的一个实例:

<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance">
    <constructor-arg ref="anotherExampleBean"/>
    <constructor-arg ref="yetAnotherBean"/>
    <constructor-arg value="1"/></bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean {
    // a private constructor
    private ExampleBean(...) {
        ...
    }
    // a static factory method; the arguments to this method can be
    // considered the dependencies of the bean that is returned,
    // regardless of how those arguments are actually used.
    public static ExampleBean createInstance (
        AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
        ExampleBean eb = new ExampleBean (...);
        // some other operations...
        return eb;
    }
}

  Arguments to the static factory method are supplied via <constructor-arg/> elements, exactly the same as if a constructor had actually been used. The type of the class being returned by the factory method does not have to be of the same type as the class that contains the static factory method, although in this example it is. An instance (non-static) factory method would be used in an essentially identical fashion (aside from the use of the factory-bean attribute instead of the class attribute), so details will not be discussed here.
  静态工厂方法的参数通过**元素提供,就像构造函数实际使用一样。 工厂方法返回的类的类型不必与包含静态**工厂方法的类的类型相同,尽管在本例中它是。 实例(非静态)工厂方法将以基本相同的方式使用(除了使用factory-bean属性而不是class属性),因此在此不讨论细节。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
spring-framework-5.0.2.release-中文注释版是Spring Framework的一个版本,它是一个Java开发框架,用于构建企业级应用程序。 Spring Framework被设计为一个轻量级的、模块化的开发框架,它提供了一系列的功能和组件,使开发人员可以更加方便地构建可扩展和可维护的应用程序。这个版本中的中文注释提供了对框架内部工作原理和代码片段的解释,使开发人员可以更加容易地理解和使用Spring框架。 Spring Framework的核心特点包括依赖注入(DI)和面向切面编程(AOP)。依赖注入是指通过配置文件或注释方式将对象的依赖关系注入到目标对象中,降低了对象之间的耦合度。面向切面编程则是一种编程范式,用于处理与业务逻辑无关的横切关注点,如日志、事务等。 除了依赖注入和面向切面编程外,Spring Framework还提供了许多其他功能,如企业级服务(如事务管理、远程访问和消息传递)、Web应用程序开发(如MVC框架和REST服务)、数据访问(如对数据库的支持)和测试支持等。通过这些功能,开发人员可以更高效地开发和管理应用程序。 总的来说,spring-framework-5.0.2.release-中文注释版是一个功能强大且易于使用的框架,它在企业级应用程序开发中发挥着重要作用。通过使用这个版本,开发人员可以更容易地理解和使用Spring框架,并构建出高质量和可扩展的应用程序。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

工地码哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值