Spring(八):Spring配置Bean(一)BeanFactory&ApplicationContext概述、依赖注入的方式、注入属性值细节...

在Spring的IOC容器里配置Bean

配置Bean形式:基于xml文件方式、基于注解的方式

在xml文件中通过bean节点配置bean:

<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <bean name="helloSpring" class="com.dx.spring.beans.HelloSpring">
        <property name="name" value="Spring"></property>
    </bean>

</beans>

参考:《Spring(二):Spring框架&Hello Spring》中配置过程

id:Bean的名称

1)在IOC容器中必须是唯一的;

2)若id没有指定,Spring会自动将全系nag定型为类名作为Bean的名字;

3)id可以指定多个名字,名字之间可用逗号、分号、或空格分隔。

Bean的配置方式:

1)通过全类名(反射实现,此时要求该bean类拥有一个无参数构造器)、

2)通过工厂方法(静态工厂方法&实例工厂方法,参考:《Spring(十三):使用工厂方法来配置Bean的两种方式(静态工厂方法&实例工厂方法)》)、

3)FactoryBean(参考:《Spring(十四):使用FactoryBean配置Bean》)

IOC容器BeanFactory&ApplicationContext概述

Spring容器

1)在Spring IOC容器读取Bean配置创建Bean实例之前,必须对它进行实例化。只有在容器实例化后,才可以从IOC容器里获取Bean实例并使用。

2)Spring提供了两种类型IOC容器实现

---BeanFactory:IOC容器的基本实现。BeanFactory是Spring框架的基础设施,面向Spring本身。

---ApplicationContext:提供了更多的高级属性,是BeanFactory的子接口。ApplicationContext面向使用Spring框架的开发者,几乎所有的应用场合都直接使用ApplicationContext,而非底层的BeanFactory。

备注:

1)无论使用两种方式的哪一种,配置文件是相同的。

2)如何却别ApplicatinContext是BeanFactory的子接口:

ApplicatinContext的类定义:

// Compiled from ApplicationContext.java (version 1.8 : 52.0, no super bit)
public abstract interface org.springframework.context.ApplicationContext 
extends org.springframework.core.env.EnvironmentCapable,
org.springframework.beans.factory.ListableBeanFactory,
org.springframework.beans.factory.HierarchicalBeanFactory,
org.springframework.context.MessageSource,
org.springframework.context.ApplicationEventPublisher,
org.springframework.core.io.support.ResourcePatternResolver {
// Method descriptor #10 ()Ljava/lang/String; @org.springframework.lang.Nullable public abstract java.lang.String getId(); // Method descriptor #10 ()Ljava/lang/String; public abstract java.lang.String getApplicationName(); // Method descriptor #10 ()Ljava/lang/String; public abstract java.lang.String getDisplayName(); // Method descriptor #16 ()J public abstract long getStartupDate(); // Method descriptor #18 ()Lorg/springframework/context/ApplicationContext; @org.springframework.lang.Nullable public abstract org.springframework.context.ApplicationContext getParent(); // Method descriptor #20 ()Lorg/springframework/beans/factory/config/AutowireCapableBeanFactory; public abstract org.springframework.beans.factory.config.AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws java.lang.IllegalStateException; }

从上边我们知道ApplicationContext是继承了org.springframework.beans.factory.ListableBeanFactoryorg.springframework.beans.factory.HierarchicalBeanFactory接口,而这两个接口都是继承了org.springframework.beans.factory.BeanFactory接口。

public interface BeanFactory {
    String FACTORY_BEAN_PREFIX = "&";
    Object getBean(String name) throws BeansException;
    <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;
    Object getBean(String name, Object... args) throws BeansException;
    <T> T getBean(Class<T> requiredType) throws BeansException;
    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
    boolean containsBean(String name);
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
    boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
    @Nullable
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;
    String[] getAliases(String name);

}

因此,说ApplicationContext是BeanFactory的子接口类。

ApplicaionContext

ApplicationContext的主要实现类:

---ClassPathXmlApplicationContext:从类路径下加载配置文件

---FileSystemXmlApplicationContext:从文件系统中加载配置文件。

 ConfigurableApplicationContext扩展与ApplicationContext,新增加两个主要的方法:refresh()和close(),让ApplicationContext具有启动、刷新和关闭上下文的能力。

// Compiled from ConfigurableApplicationContext.java (version 1.8 : 52.0, no super bit)
public abstract interface org.springframework.context.ConfigurableApplicationContext extends org.springframework.context.ApplicationContext, org.springframework.context.Lifecycle, java.io.Closeable {
  
  // Field descriptor #8 Ljava/lang/String;
  public static final java.lang.String CONFIG_LOCATION_DELIMITERS = ",; \t\n";
  
  // Field descriptor #8 Ljava/lang/String;
  public static final java.lang.String CONVERSION_SERVICE_BEAN_NAME = "conversionService";
  
  // Field descriptor #8 Ljava/lang/String;
  public static final java.lang.String LOAD_TIME_WEAVER_BEAN_NAME = "loadTimeWeaver";
  
  // Field descriptor #8 Ljava/lang/String;
  public static final java.lang.String ENVIRONMENT_BEAN_NAME = "environment";
  
  // Field descriptor #8 Ljava/lang/String;
  public static final java.lang.String SYSTEM_PROPERTIES_BEAN_NAME = "systemProperties";
  
  // Field descriptor #8 Ljava/lang/String;
  public static final java.lang.String SYSTEM_ENVIRONMENT_BEAN_NAME = "systemEnvironment";
  
  // Method descriptor #22 (Ljava/lang/String;)V
  public abstract void setId(java.lang.String arg0);
  
  // Method descriptor #24 (Lorg/springframework/context/ApplicationContext;)V
  public abstract void setParent(@org.springframework.lang.Nullable org.springframework.context.ApplicationContext arg0);
  
  // Method descriptor #28 (Lorg/springframework/core/env/ConfigurableEnvironment;)V
  public abstract void setEnvironment(org.springframework.core.env.ConfigurableEnvironment arg0);
  
  // Method descriptor #30 ()Lorg/springframework/core/env/ConfigurableEnvironment;
  public abstract org.springframework.core.env.ConfigurableEnvironment getEnvironment();
  
  // Method descriptor #32 (Lorg/springframework/beans/factory/config/BeanFactoryPostProcessor;)V
  public abstract void addBeanFactoryPostProcessor(org.springframework.beans.factory.config.BeanFactoryPostProcessor arg0);
  
  // Method descriptor #34 (Lorg/springframework/context/ApplicationListener;)V
  // Signature: (Lorg/springframework/context/ApplicationListener<*>;)V
  public abstract void addApplicationListener(org.springframework.context.ApplicationListener arg0);
  
  // Method descriptor #38 (Lorg/springframework/core/io/ProtocolResolver;)V
  public abstract void addProtocolResolver(org.springframework.core.io.ProtocolResolver arg0);
  
  // Method descriptor #40 ()V
  public abstract void refresh() throws org.springframework.beans.BeansException, java.lang.IllegalStateException;
  
  // Method descriptor #40 ()V
  public abstract void registerShutdownHook();
  
  // Method descriptor #40 ()V
  public abstract void close();
  
  // Method descriptor #47 ()Z
  public abstract boolean isActive();
  
  // Method descriptor #49 ()Lorg/springframework/beans/factory/config/ConfigurableListableBeanFactory;
  public abstract org.springframework.beans.factory.config.ConfigurableListableBeanFactory getBeanFactory() throws java.lang.IllegalStateException;
  
  // Method descriptor #50 ()Lorg/springframework/core/env/Environment;
  // Stack: 1, Locals: 1
  public bridge synthetic org.springframework.core.env.Environment getEnvironment();
    0  aload_0 [this]
    1  invokeinterface org.springframework.context.ConfigurableApplicationContext.getEnvironment() : org.springframework.core.env.ConfigurableEnvironment [1] [nargs: 1]
    6  areturn
      Line numbers:
        [pc: 0, line: 43]
      Local variable table:
        [pc: 0, pc: 7] local: this index: 0 type: org.springframework.context.ConfigurableApplicationContext
}

ApplicationContext在初始化上下文时就实例化所有单例的Bean。

WebApplicationContext是专门为WEB应用而准备的,它允许从相对于WEB根目录的路径中完成初始化工作。

 

依赖注入的方式:

1)属性注入(set方法注入);

package com.dx.spring.beans;

public class HelloSpring {
    private String name;

    public HelloSpring() {

    }

    public void setName(String name) {
        this.name = name;
    }

    public void sayHello() {
        System.out.println("Hello " + this.name);
    }
}
setName方法注入:
    <bean name="helloSpring" class="com.dx.spring.beans.HelloSpring">
        <property name="name" value="Spring"></property>
    </bean>

2)构造函数注入;

a)通过构造方法注入Bean的属性值或依赖的对象,它保证了Bean实例在实例化后就可以使用。

b)构造器注入在<constructor-arg>元素里声明属性,<constructor-arg>中没有name属性。

    <bean name="car" class="com.dx.spring.beans.Car">
        <constructor-arg value="AUDI" index="0"></constructor-arg>
        <constructor-arg value="Shanghai" index="1"></constructor-arg>
        <constructor-arg value="400000" type="double"></constructor-arg>
    </bean>
    <bean name="car2" class="com.dx.spring.beans.Car">
        <constructor-arg value="BMW" type="java.lang.String"></constructor-arg>
        <constructor-arg value="Shanghai" type="java.lang.String"></constructor-arg>
        <constructor-arg value="260" type="int"></constructor-arg>
    </bean>

Car.java

package com.dx.spring.beans;

public class Car {
    private String brand;
    private String corp;
    private double price;
    private int maxSpeed;

    public Car(String brand, String corp, double price) {
        super();
        this.brand = brand;
        this.corp = corp;
        this.price = price;
    }

    public Car(String brand, String corp, int maxSpeed) {
        super();
        this.brand = brand;
        this.corp = corp;
        this.maxSpeed = maxSpeed;
    }

    @Override
    public String toString() {
        return "Car [brand=" + brand + ", corp=" + corp + ", price=" + price + ", maxSpeed=" + maxSpeed + "]";
    }

}

client.java

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        HelloSpring helloSpring = (HelloSpring) applicationContext.getBean("helloSpring");
        
        Car car = (Car) applicationContext.getBean("car");

        Car car2 = (Car) applicationContext.getBean("car2");
        
        System.out.println(car);
        System.out.println(car2);
    }

输出信息:

Car [brand=AUDI, corp=Shanghai, price=400000.0, maxSpeed=0]
Car [brand=BMW, corp=Shanghai, price=0.0, maxSpeed=260]

3)工厂方法注入

不推荐、也不常用

注入属性值细节

1)字面值

字面值:可用字符串表示的值,可以通过<value>元素标签或value属性进行注入。

基本数据类型及封装类、String等类型都可以采用字面值注入的方式

若字面值中包含特殊字符,可以使用<![CDATA[]]>把字面值包裹起来。

Person.java

package com.dx.spring.beans;

public class Person {
    private String name;
    private String age;
    private Car car;
    
    public Person(String name, String age, Car car) {
        super();
        this.name = name;
        this.age = age;
        this.car = car;
    }
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAge() {
        return age;
    }
    public void setAge(String age) {
        this.age = age;
    }
    public Car getCar() {
        return car;
    }
    public void setCar(Car car) {
        this.car = car;
    }
    
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + ", car=" + car + "]";
    }
    
}
View Code
    <bean name="car2" class="com.dx.spring.beans.Car">
        <constructor-arg value="BMW" type="java.lang.String"></constructor-arg>
        <constructor-arg type="java.lang.String">
            <value><![CDATA[<Shanghai>]]></value>
        </constructor-arg>
        <constructor-arg type="int">
            <value>260</value>
        </constructor-arg>
    </bean>

2)引用其它Bean

组成应用程序的Bean机场需要相互协作已完成应用程序的功能,需要使用Bean能够相互访问,就必须在Bean配置文件中指定对Bean的引用

在Bean的配置文件中,可以通过<ref>元素或者ref属性为bean的属性或,构造器参数指定对bean的引用。

也可以在属性或构造器里包含Bean的声明,这样的Bean成为内部Bean。

3)注入参数详解:null值和级联属性

可以使用专用的<null/>元素标签为bean的字符串或其它对象类型的属性注入null值。

    <bean id="person3" class="com.dx.spring.beans.Person">
        <constructor-arg value="Lisi"></constructor-arg>
        <constructor-arg value="25"></constructor-arg>
        <constructor-arg name="car">
            <null/>
        </constructor-arg>
    </bean>

和struts、hibernate等框架一样,spring支持级联属性的配置。

    <bean id="person" class="com.dx.spring.beans.Person">
        <constructor-arg value="Zhangsan"></constructor-arg>
        <constructor-arg value="24"></constructor-arg>
        <constructor-arg ref="car"></constructor-arg>
        <property name="car.maxSpeed" value="260"></property>
    </bean>

注意:

1)上边代码必须要要求先初始化car,否则直接给car.maxSpeed赋值会抛出异常与struts2不同点。

2)需要在car中实现setMaxSpeed方法。

    public void setMaxSpeed(int maxSpeed) {
        this.maxSpeed = maxSpeed;
    }

4)集合属性

在Spring中可以通过一组内置的xml标签(例如:<list>,<set>,<map>)来配置集合属性。

4.1)配置java.util.List类型的属性,需要指定<list>标签,在标签里包含一些元素。这些标签可以通过<value>指定简单的常量值,通过<ref>指定对其它Bean的引用,通过<bean>指定内置bean定义。通过<null/>指定空元素。甚至可以内嵌其他集合。

applicationContext.xml

    <bean name="car" class="com.dx.spring.beans.Car">
        <constructor-arg value="AUDI" index="0"></constructor-arg>
        <constructor-arg value="Shanghai" index="1"></constructor-arg>
        <constructor-arg value="400000" type="double"></constructor-arg>
    </bean>
    <bean name="car2" class="com.dx.spring.beans.Car">
        <constructor-arg value="BMW" type="java.lang.String"></constructor-arg>
        <constructor-arg type="java.lang.String">
            <value><![CDATA[<Shanghai>]]></value>
        </constructor-arg>
        <constructor-arg type="int">
            <value>260</value>
        </constructor-arg>
    </bean>
    <bean id="person4" class="com.dx.spring.beans.collections.Person">
        <property name="name" value="Nike"></property>
        <property name="age" value="25"></property>
        <property name="cars">
            <list>
                <ref bean="car" />
                <ref bean="car2" />
            </list>
        </property>
    </bean>

client.java

    public static void main(String[] args) {
        ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");
        Person person = (Person) cxt.getBean("person4");

        System.out.println(person);
    }

Person.java

package com.dx.spring.beans.collections;

import java.util.List;

import com.dx.spring.beans.Car;

public class Person {
    private String name;
    private int age;
    private List<Car> cars;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public List<Car> getCars() {
        return cars;
    }

    public void setCars(List<Car> cars) {
        this.cars = cars;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + ", cars=" + cars + "]";
    }    
    
}
View Code

打印结果:

Person [name=Nike, age=25, cars=[Car [brand=AUDI, corp=Shanghai, price=400000.0, maxSpeed=260], Car [brand=BMW, corp=<Shanghai>, price=0.0, maxSpeed=260]]]

4.2)数组的定义和List一样,都可以使用<list>

修改Person.java为:

    public Car[] getCars() {
        return cars;
    }

    public void setCars(Car[] cars) {
        this.cars = cars;
    }

    @Override
    public String toString() {
        String carsStr = "";
        for (Car car : cars)
            carsStr += car.toString() + ",";
        return "Person [name=" + name + ", age=" + age + ", cars=[" + carsStr + "]]";
    }

applicationContext.xml不变,client.java也不变,打印结果:

Person [name=Nike, age=25, cars=[Car [brand=AUDI, corp=Shanghai, price=400000.0, maxSpeed=260],Car [brand=BMW, corp=<Shanghai>, price=0.0, maxSpeed=260],]]

4.3)配置java.util.Set需要使用<set>标签,定义元素的方法与List一样。

修改Person.car属性类型:

    private Set<Car> cars;

    public Set<Car> getCars() {
        return cars;
    }

    public void setCars(Set<Car> cars) {
        this.cars = cars;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + ", cars=" + cars+ "]";
    }    

修改applicationContext.xml

    <bean id="person4" class="com.dx.spring.beans.collections.Person">
        <property name="name" value="Nike"></property>
        <property name="age" value="25"></property>
        <property name="cars">
            <set>
                <ref bean="car" />
                <ref bean="car2" />
            </set>
        </property>
    </bean>

client.java不变,打印结果:

Person [name=Nike, age=25, cars=[Car [brand=AUDI, corp=Shanghai, price=400000.0, maxSpeed=260], Car [brand=BMW, corp=<Shanghai>, price=0.0, maxSpeed=260]]]

4.4)java.util.Map通过<map>标签定义,<map>标签里可以使用多个<entry>作为子标签,每个条目包含一个键和一个值。

4.4.1)map必须在<key>标签里定义键。

4.4.2)因为键和值的类型没有限制,所以可以自由地为它们指定<value>,<ref>,<bean>或<null>元素。

4.4.3)可以将Map的键和值作为<entry>的属性定义:简单常量可以使用key和value来定义;Bean引用通过key-ref和value-ref属性定义。

修改Person.car属性类型:

    private Map<String,Car> cars;

    public Map<String,Car> getCars() {
        return cars;
    }

    public void setCars(Map<String,Car> cars) {
        this.cars = cars;
    }

修改applicationContext.xml

    <bean id="person4" class="com.dx.spring.beans.collections.Person">
        <property name="name" value="Nike"></property>
        <property name="age" value="25"></property>
        <property name="cars">
            <map>
                <entry key="car1" value-ref="car"></entry>
                <entry key="car2" value-ref="car2"></entry>
            </map>
        </property>
    </bean>

client.java不变,打印结果为:

Person [name=Nike, age=25, cars={car1=Car [brand=AUDI, corp=Shanghai, price=400000.0, maxSpeed=260], car2=Car [brand=BMW, corp=<Shanghai>, price=0.0, maxSpeed=260]}]

5)使用<props>定义java.util.Properties,该标签使用多个<prop>作为子标签,每个<prop>标签必须定义key属性。

定义DataSource.java

package com.dx.spring.beans.collections;

import java.util.Properties;

public class DataSource {
    private Properties properties;

    public Properties getProperties() {
        return properties;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    @Override
    public String toString() {
        return "DataSource [properties=" + properties + "]";
    }
}

applicationContext.xml配置:

    <bean id="proerpties_test" class="com.dx.spring.beans.collections.DataSource">
        <property name="properties">
            <props>
                <prop key="username">username</prop>
                <prop key="password">password</prop>
                <prop key="jdbcUri">jdbc:mysql:3306///test</prop>
                <prop key="driverClass">com.mysql.jdbc.Driver</prop>
            </props>
        </property>
    </bean>

client.java

    public static void main(String[] args) {
        ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");
        DataSource dataSource=(DataSource)cxt.getBean("proerpties_test");
        System.out.println(dataSource);
    }

打印结果:

DataSource [properties={jdbcUri=jdbc:mysql:3306///test, driverClass=com.mysql.jdbc.Driver, password=password, username=username}]

5)使用utility schema 定义集合

5.1)之前使用基本的集合标签定义集合是,不能将集合作为独立的Bean定义,导致其他Bean无法引用该集合,所以无法在不同的Bean之间共享集合。

5.2)可以使用util schema 里的集合标签定义独立的集合Bean。需要注意的是,必须在<beans>根元素里添加util schema定义。

举例:

Person.java

package com.dx.spring.beans.collections;

import java.util.List;

import com.dx.spring.beans.Car;

public class Person {
    private String name;
    private int age;
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }    

    private List<Car> cars;

    public List<Car> getCars() {
        return cars;
    }

    public void setCars(List<Car> cars) {
        this.cars = cars;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + ", cars=" + cars+ "]";
    }    
    
}
View Code

applicationContext.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" xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd">

    <bean id="person4" class="com.dx.spring.beans.collections.Person">
        <property name="name" value="Nike"></property>
        <property name="age" value="25"></property>
        <property name="cars" ref="cars"></property>
    </bean>

    <util:list id="cars">
        <ref bean="car"></ref>
        <ref bean="car2"></ref>
        <bean name="car3" class="com.dx.spring.beans.Car">
            <constructor-arg value="Ford" index="0"></constructor-arg>
            <constructor-arg value="Changan" index="1"></constructor-arg>
            <constructor-arg value="140000" type="double"></constructor-arg>
        </bean>
    </util:list>
</beans>

测试client.java

    public static void main(String[] args) {
        ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");                
        Person person = (Person) cxt.getBean("person4");
        System.out.println(person);
    }

打印结果:

Person [
name=Nike,
age=25,
cars=
[
Car [brand=AUDI, corp=Shanghai, price=400000.0, maxSpeed=260],
Car [brand=BMW, corp=<Shanghai>, price=0.0, maxSpeed=260],
Car [brand=Ford, corp=Changan, price=140000.0, maxSpeed=0]
]
]

6)使用p命名空间

6.1)为了简化xml文件的配置,原来越多的xml文件采用属性而非子元素配置信息。

6.2)Spring从2.5版本开始引入了一个新的p命名空间,可以通过<bean>元素属性的方式配置bean的属性。

6.3)使用p命名空间后,基于xml的配置方式将进一步简化。

 

    <bean id="person4" class="com.dx.spring.beans.collections.Person" p:name="Nike" p:age="25" p:cars-ref="cars">
        <!-- 
        <property name="name" value="Nike"></property>
        <property name="age" value="25"></property>
        <property name="cars" ref="cars"></property>
         -->
    </bean>

    <util:list id="cars">
        <ref bean="car"></ref>
        <ref bean="car2"></ref>
        <bean name="car3" class="com.dx.spring.beans.Car">
            <constructor-arg value="Ford" index="0"></constructor-arg>
            <constructor-arg value="Changan" index="1"></constructor-arg>
            <constructor-arg value="140000" type="double"></constructor-arg>
        </bean>
    </util:list>

  

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值