Day38——Spring学习笔记1

Spring初识

1 认识

Spring:给软件行业带来春天

现在的java 开发,说白了就是基于Spring的

Spring目的:解决企业应用开发的复杂性

现在学习Spring,为了之后学习SpringMVC,SpringBoot作为基础

核心:Spring中的IOC与AOP

学习之地:

  • 官网:http://spring.io/
  • Spring的学习文档,API文档
  • GitHub:https://github.com/spring-projects/spring-framework

2 IOC基础

IOC:控制反转(一种思想)

初识案例:
  1. UserMapper 接口

  2. UserMapperImpl 实现类

  3. UserService 业务接口

  4. UserServiceImpl 业务实现类

首先建立一个UserMapper的接口,里面写入一方法:

public interface UserMapper {
    public void getUser();
}

再,建立实现接口的类,先建立一个:

public class UserMapperImpl implements UserMapper{
    public void getUser() {
        System.out.println("一般获取用户数据");
    }
}

接着建立一个UserService接口,写入方法

public interface UserService {
    public void getUser();
}

写它对应的实现接口类

public class UserServiceImpl implements UserService {
    
    private UserMapper userMapper = new UserMapperImpl();

    public void getUser() {
        userMapper.getUser();
    }
}

进行测试:

public class Mytest {
    @Test
    public void test1(){
        UserServiceImpl userService = new UserServiceImpl();
        userService.getUser();
    }
}
//打印出一般获取用户数据

当我们在建立了更多的UserMapperImpl类,每次都需要在底层代码里,

比如,建立了一个

public class UserMapperSqlImpl implements UserMapper {
    public void getUser() {
        System.out.println("sql获取用户数据");
    }
}

需要在User ServiceImpl中,要new一个UserMapperSqlImpl(),在测试的时候才会输出我们想要的"sql获取用户数据";或者可以这样解决:

//设置一个set方法
public class UserServiceImpl implements UserService {

    //private UserMapper userMapper = new UserMapperImpl();
    //private UserMapper userMapper = new UserMapperSqlImpl();
    private UserMapper userMapper;
    public void setUserMapper(UserMapper userMapper) {
        this.userMapper = userMapper;
    }
    public void getUser() {
        userMapper.getUser();
    }
}

测试的时候就可以这样写:

@Test
public void test1(){
    UserServiceImpl userService = new UserServiceImpl();
    userService.getUser();

    userService.setUserMapper(new UserMapperOracleImpl());
    userService.getUser();
    userService.setUserMapper(new UserMapperSqlImpl());
    userService.getUser();
}

用一张图来解释我们在干什么:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nnT4Y1u2-1582461580732)(Spring1.assets/1582451248350.png)]

我们的目的就是让用户拿到主动权,他需要什么就调用什么.

像在没有使用set方法之前,程序需要主动的创建需要的对象,但是用了set之后,程序就不主动创建了,而是接受用户所需的对象.

这样的思想,程序猿不用再去管理对象的创建了。系统的耦合性大大降低~,可以更加专注的在业务的实现上!这是IOC 的原型!

IOC本质

控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。

采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。

控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。


3 HelloSpring

首先建立一个空的Maven项目

1、导入依赖

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.2.RELEASE</version>
</dependency>

2、编写代码,分析

建立一个Hello类,通过show()方法把hello+s所传入参数打印出来

public class Hello {
    private String name;
    public void setName(String name){
        this.name=name;
    }
    public void show(){
        System.out.println("Hello"+name);
    }
}

在resources中建立applicationContext.xml文件,并需要将官网上的配置文件内容拷贝下来

 1  <?xml version="1.0" encoding="UTF-8"?>
 2  <beans xmlns="http://www.springframework.org/schema/beans"
 3         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4         xsi:schemaLocation="http://www.springframework.org/schema/beans
 5          http://www.springframework.org/schema/beans/spring-beans.xsd">
 6  
 7      <!--bean就是java对象 , 由Spring创建和管理-->
 8      <bean id="hello" class="com.feng.pojo.Hello">
 9          <property name="name" value="Spring"/>
10      </bean>
11  
12  </beans>

在测试类中进行测试

 @Test
 public void test2(){
     //解析beans.xml文件 , 生成管理相应的Bean对象
     ApplicationContext context = newClassPathXmlApplicationContext("applicationContext.xml");
     
     //getBean : 参数即为spring配置文件中bean的id .
     Hello hello = (Hello) context.getBean("hello");
     hello.show();
 }

这样会打印:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PUGc2bK0-1582461580735)(Spring1.assets/1582452293864.png)]

解释:

  • hello 对象是由Spring创建的

  • hello 对象的属性是由Spring容器设置的

  • 控制 : 谁来控制对象的创建 , 传统应用程序的对象是由程序本身控制创建的 , 使用Spring后 , 对象是由Spring来创建的 .

  • 反转 : 程序本身不创建对象 , 而变成被动的接收对象 .

  • 依赖注入 : 就是利用set方法来进行注入的.

到目前,彻底不用再程序中去改动了 , 要实现不同的操作 , 只需要在xml配置文件中进行修改


4 IOC创建对象的方式

使用情况:

1、无参构造创建,需要类中有set方法

2、有参构造创建,需要类中有有参构造方法,构造器注入,也叫C注入

3、有参构造创建拓展方法

测试步骤:

1、创建实体类,设置set方法

2、使用Spring创建bean

3、测试结果(有参构造创建,无参创建,删除set方法测试)

详细内容:

User类:

public class User {

    private String name;

    public User(){
        System.out.println("对象被创建了");
    }

//    public void setName(String name) {
//        this.name = name;
//    }
    //构造方法
    public User(String name) {
        this.name = name;
    }

    public void show(){
        System.out.println("hello,"+name);
    }
}

在application Context配置文件中进行配置:

<!--类中有set方法-->
<bean id="user" class="com.feng.pojo.User">
    <property name="name" value="xiaoming1"/>
</bean>

<!--类中无set方法后,建立有参构造方法-->
<bean id="user" class="com.feng.pojo.User">
    <constructor-arg name="name" value="xiaoming2"/>
</bean>

<!--拓展方法-->
<bean id="user" class="com.feng.pojo.User">
    <!--根据参数对应的次序,第一个参数的index从0开始-->
    <constructor-arg index="0" value="xiaoming3"/>
    <!--当参数只有一个时,可以根据类型来;当有多个参数且类型不一致时,不好使用-->
    <constructor-arg type="java.lang.String" value="xiaoming4"/>
</bean>

测试类中:

@Test
public void test3(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    User user = (User) context.getBean("user");
    System.out.println("===========");
    user.show();
}

测试1(类中有set方法):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tYSiVHaT-1582461580735)(Spring1.assets/1582449898258.png)]

测试2(类中无set方法后,建立有参构造方法):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pFJT6MOs-1582461580736)(Spring1.assets/1582449973603.png)]

测试(拓展方法):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dR7UPHOD-1582461580736)(Spring1.assets/1582450034730.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P6IAOv0O-1582461580736)(Spring1.assets/1582450064043.png)]


5 Spring配置

在applicationContext.xml中

1、alias别名

<!--别名,如果添加了别名,我们也可以使用别名获取到这个对象-->
<alias name="user" alias="userNew"/>

2、Bean

bean标签中

id:bean的唯一标识符,对象名

name:就是bean的别名

class:bean的全类名

<bean name="hello hello2 hello3" class="com.feng.pojo.Hello">
    <property name="name" value="Spring"/>
</bean>

3、import,在多人协作中

假设,现在项目中有多个人开发,这三个人复制不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的applicationContext.xml合并为一个总的

<import resource="Student.xml"/>
<import resource="spring_autowire.xml"/>
<import resource="SpringContext.xml"/>

4 scope作用域

prototype:每次创建新对象

singleton:在容器中只有一个对象(默认)

request/session在web 中使用

<bean id="student" class="com.feng.pojo.Student" scope="singleton"></bean>

6 属性注入

实现一个小程序:

1、建立实体类

2、通过编写xml配置注入student对象

3、测试结果注入成功

public class Student {
    private String name;
    private Address address;
    private String[] books;
    private List<String> list;
    private Map<String,String> map;
    private Set<String> set;
    private String wife;
    private Properties info;
}
//要么加上lombok注解
//要么重写set方法与toString方法

编写student.xml文件:(针对student类中不同属性进行注入),且这个xml文件要import至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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="address" class="com.feng.pojo.Address">
        <property name="address" value="西安"/>
    </bean>

    <bean id="student" class="com.feng.pojo.Student" scope="singleton">
        <!--第一种,普通值注入,value-->
        <property name="name" value="fff"/>

        <!--第二种,Bean注入,ref-->
        <property name="address" ref="address"/>

        <!--数组-->
        <property name="books">
            <array>
                <value>红楼梦</value>
                <value>西游记</value>
                <value>水浒传</value>
                <value>三国演义</value>
            </array>
        </property>

        <!--List-->
        <property name="list">
            <list>
                <value>list1</value>
                <value>list2</value>
                <value>list3</value>
            </list>
        </property>

        <!--Map-->
        <property name="map">
            <map>
                <entry key="key1" value="value1"/>
                <entry key="key2" value="value2"/>
            </map>
        </property>

        <!--Set-->
        <property name="set">
            <set>
                <value>s1</value>
                <value>s2</value>
                <value>s3</value>
            </set>
        </property>

        <!--null-->
        <property name="wife">
            <null/>
        </property>

        <!--Properties-->
        <property name="info">
            <props>
                <prop key="year">2020</prop>
                <prop key="gender"></prop>
                <prop key="name">f</prop>
                <prop key="pwd">00000</prop>
            </props>
        </property>
    </bean>

</beans>

测试类:

@Test
public void test4(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    Student student = (Student) context.getBean("student");
    System.out.println(student.toString());
}

测试结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B1G1MV6D-1582461580738)(Spring1.assets/1582453236244.png)]


7 自动装配

前面学会如何使用元素来声明 bean 和通过使用 XML 配置文件中的< constructor-arg>和< property>元素来注入 。

Spring 容器可以在不使用< constructor-arg>和< property>元素的情况下自动装配相互协作的 bean 之间的关系,这有助于减少编写一个大的基于 Spring 的应用程序的 XML 配置的数量。


Spring 自动装配 ‘byName’

autowire=“byName” 自动寻找和属性相关的bean,本质为set方法

这种模式由属性名称指定自动装配。Spring 容器看作 beans,在 XML 配置文件中 beans 的 autowire 属性设置为 byName。然后,它尝试将它的属性与配置文件中定义为相同名称的 beans 进行匹配和连接。如果找到匹配项,它将注入这些 beans,否则,它将抛出异常。

进行测试:

程序展示一个人有两个宠物

实体类:User,Dog,Cat

public class User {

    private Cat cat;

    private Dog dog;

    private String str;
    
    public void setCat(Cat cat) {
        this.cat = cat;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    public void setStr(String str) {
        this.str = str;
    }

    public Cat getCat() {
        return cat;
    }
    
    public Dog getDog() {
        return dog;
    }

    public String getStr() {
        return str;
    }

}

public class Dog {
    public void shout(){
        System.out.println("wang~");
    }
}
public class Cat {
    public void shout(){
        System.out.println("miao~");
    }
}

在autowire.xml中进行配置,须导入至applicationContext.xml中

<bean id="cat" class="com.feng.entity.Cat"></bean>
<bean id="dog" class="com.feng.entity.Dog"></bean>

<bean id="uu"
      class="com.feng.entity.User"
      autowire="byName">
    <property name="cat" ref="cat"></property>
    <property name="dog" ref="dog"></property>
    <property name="str" value="xiaoming"></property>
</bean>

进行测试:

@Test
public void test5(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    com.feng.entity.User user = context.getBean("uu",com.feng.entity.User.class);
    System.out.println(user.getStr());
    user.getCat().shout();
    user.getDog().shout();
}

打印输出:

xiaoming

miao~

wang~


Spring 自动装配 ‘byType’

autowire=“byType”

这种模式由属性类型指定自动装配。Spring 容器看作 beans,在 XML 配置文件中 beans 的 autowire 属性设置为 byType。然后,如果它的 type 恰好与配置文件中 beans 名称中的一个相匹配,它将尝试匹配和连接它的属性。如果找到匹配项,它将注入这些 beans,否则,它将抛出异常。

进行测试:

利用上面的案例,将xml文件中改为

<bean id="uu" class="com.feng.entity.User" autowire="byType">
    <property name="str" value="xiaoming"></property>
</bean>

<bean id="dog" class="com.feng.entity.Dog"></bean>
<bean id="cat" class="com.feng.entity.Cat"></bean>

测试程序不变:

输出与上面结果一致


基于注解的配置

从 Spring 2.5 开始就可以使用注解来配置依赖注入。而不是采用 XML 来描述一个 bean 连线,你可以使用相关类,方法或字段声明的注解,将 bean 配置移动到组件类本身。

在 XML 注入之前进行注解注入,因此后者的配置将通过两种方式的属性连线被前者重写。

注解连线在默认情况下在 Spring 容器中不打开。

所以做准备工作:

需要在我们的 Spring 配置文件中启用它

<?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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd">

       <!--开启属性注解-->
    <context:annotation-config/>

</beans>

看看几种注解:

[@Resource] [@Autowired] [@Qualifier]

在测试中认识一下,继续上面的栗子:

[@Autowired]

@Autowired是按类型自动转配的,不支持id匹配。将User中的set方法去掉,加入@Autowired注解。

public class User {

    @Autowired
    private Cat cat;
    @Autowired
    private Dog dog;
//@Autowired(required=false)   
  false,对象可以为null;true,对象必须存对象,不能为null。

    private String str;
//    开启属性注解后,去掉set方法,加入@Autowired注解

    public void setCat(Cat cat) {
        this.cat = cat;
    }
    public void setDog(Dog dog) {
        this.dog = dog;
    }
    public void setStr(String str) {
        this.str = str;
    }
}

xml中:

<bean id="catXX" class="com.feng.entity.Cat"></bean>
<bean id="dogX" class="com.feng.entity.Dog"></bean>
<bean id="uu" class="com.feng.entity.User"></bean>

结果输出正常,可发现catXX和dogX的id与类中不一致也不会影响结果


[@Resource]

@Resource如有指定的name属性,先按该属性进行byName方式查找装配;其次再进行默认的byName方式进行装配;如果以上都不成功,则按byType的方式自动装配。都不成功,则报异常。

public class User {

    @Resource
    private Cat cat;

    @Resource(name="dogggg2")
    private Dog dog;

    private String str;
....
}

xml文件中

<bean id="catXX" class="com.feng.entity.Cat"></bean>
<bean id="dogggg2" class="com.feng.entity.Dog"></bean>
<bean id="uu" class="com.feng.entity.User"></bean>

    <!--测试后正常输出,虽没找到catXX,但是依据类型找到了-->
    <!--不改变catXX,再把dogggg2改成其他之后,报错,因为@Resource(name="dogggg2")
	没有对应的dogggg2,报错为NoSuchBeanDefinitionException: No bean named 'dogggg2' available
	-->

@Resource 和@ Autowired 的区别:

  • @ Autowired 通过byType的方式实现,而且必须要求这个对象存在!如果要允许null 值,可以设置它的required属性为false 【常用】
  • @ Resource 默认通过byname的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到的情况下,就报错!如果name属性一旦指定,就只会按照名称进行装配。 【常用】
  • 执行顺序不同:@ Autowired 通过byType的方式实现。@ Resource 默认通过byname的方式实现。

8 使用注解开发

1 必须有AOP的包(添加了Spring-webmvc的jar包就会有)

2 导入context约束文件(与基于注解的配置开头处的xml文件一致)


测试注入Bean

在User类中

@Component("user1")
public class User {

    private Cat cat;

    private Dog dog;

    private String str;

    public void setCat(Cat cat) {
        this.cat = cat;
    }
    public void setDog(Dog dog) {
        this.dog = dog;
    }
    //相当于 <property name="str" value="小明明"/>
    @Value("小明明")
    public void setStr(String str) {
        this.str = str;
    }
...
}

xml文件中

<bean id="user1" class="com.feng.entity.User">
    <property name="cat" ref="cat"></property>
    <property name="dog" ref="dog"></property>
</bean>
<bean id="dog" class="com.feng.entity.Dog"></bean>
<bean id="cat" class="com.feng.entity.Cat"></bean>

进行测试打印:小明明 miao~ wang~


3 衍生的注解

@Component 有几个衍生注解,在web开发中,会按照mvc三层架构分层!

  • dao 【@Repository】

  • service 【@Service】

  • controller 【@Controller】

    这四个注解功能都是一样的,都是代表将某个类注册到Spring中,装配Bean

注解和XML的最佳使用

  • xml管理Bean
  • 通过注解完成属性的注入
使用Java的方式配置Spring

现在要完全不使用Spring的xml配置了,全权交给Java来做

使用一个User类

//这里这个注解的意思,就是说明这个类被Spring接管了,注册到了容器中
@Component
public class User {
    
    //@Value("英子")也可以
    private String name;

    public String getName() {
        return name;
    }

    @Value("英子") //属性注入值
    public void setName(String name) {
        this.name = name;
    }
}

此时不建立xml文件,使用一个类

// 这个也会Spring容器托管,注册到容器中,因为他本来就是一个@Component
// @Configuration代表这是一个配置类,就和我们之前看的beans.xml
@Configuration
public class Myconfig {

    //注册一个bean,就相当于之前写的一个bean标签
    //这个方法的名字,就相当于bean标签中的id属性
    //这个方法的返回值,就相当于bean标签中的class属性
    @Bean
    public User user(){
        return new User();
    }
}

写测试类:

@Test
public void test6(){
    ApplicationContext context = new AnnotationConfigApplicationContext(Myconfig.class);
    User user = (User) context.getBean("user");
    System.out.println(user.name);
}

,就是说明这个类被Spring接管了,注册到了容器中
@Component
public class User {

//@Value("英子")也可以
private String name;

public String getName() {
    return name;
}

@Value("英子") //属性注入值
public void setName(String name) {
    this.name = name;
}

}


此时不建立xml文件,使用一个类

```java
// 这个也会Spring容器托管,注册到容器中,因为他本来就是一个@Component
// @Configuration代表这是一个配置类,就和我们之前看的beans.xml
@Configuration
public class Myconfig {

    //注册一个bean,就相当于之前写的一个bean标签
    //这个方法的名字,就相当于bean标签中的id属性
    //这个方法的返回值,就相当于bean标签中的class属性
    @Bean
    public User user(){
        return new User();
    }
}

写测试类:

@Test
public void test6(){
    ApplicationContext context = new AnnotationConfigApplicationContext(Myconfig.class);
    User user = (User) context.getBean("user");
    System.out.println(user.name);
}

输出:英子

再如:建立一个Student类,使用Java 的方式配置Spring,类里面包含list,map,数组等属性

@Component
public class Student {

@Value("英子")
    public String name;

@Autowired
    public Address address;

@Resource
    public String[] books;

@Resource
    private List<String> list;
@Resource
    private Map<String,String> map;
@Resource
    private Set<String> set;
@Value("null")
    private String wife;

    private Properties info;

    public String getName() {
        return name;
    }

    public Address getAddress() {
        return address;
    }

    public String[] getBooks() {
        return books;
    }

    public List<String> getList() {
        return list;
    }

    public Map<String, String> getMap() {
        return map;
    }

    public Set<String> getSet() {
        return set;
    }

    public String getWife() {
        return wife;
    }

    public Properties getInfo() {
        return info;
    }

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

    public void setAddress(Address address) {
        this.address = address;
    }

    public void setBooks(String[] books) {
        this.books = books;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public void setMap(Map<String, String> map) {
        this.map = map;
    }

    public void setSet(Set<String> set) {
        this.set = set;
    }

    public void setWife(String wife) {
        this.wife = wife;
    }

    public void setInfo(Properties info) {
        this.info = info;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", address=" + address.getAddress() +
                ", books=" + Arrays.toString(books) +
                ", list=" + list +
                ", map=" + map +
                ", set=" + set +
                ", wife='" + wife + '\'' +
                ", info=" + info +
                '}';
    }
}

建立一个配置类

@Configuration
public class StudentConfig {

    @Bean
    public Student student(){
        return new Student();
    }

    @Bean
    public Address address(){
        return new Address();
    }

    @Bean
    public String[] books(){
        return new String[]{};
    }

    @Bean
    public List<String> list() {
        return new ArrayList<String>();
    }

    @Bean
    public Map<String,String> map(){
        return new HashMap<String, String>(){};
    }

    @Bean
    public Set<String> set(){
        return new HashSet<String>();
    }

}

在测试:

@Test
public void test7(){
    ApplicationContext context = new AnnotationConfigApplicationContext(StudentConfig.class);
    Student student = (Student) context.getBean("student");
    student.address.setAddress("西安");
    String[] strings = new String[3];
    strings[0]="三国";
    strings[1]="水浒";
    strings[2]="西游";
    student.setBooks(strings);

    ArrayList<String> strings1 = new ArrayList<String>();
    strings1.add("清单1");
    strings1.add("清单2");
    student.setList(strings1);

    HashMap<String, String> map = new HashMap<String, String>();
    map.put("key1","Value1");
    map.put("key2","Value2");
    student.setMap(map);

    HashSet<String> set = new HashSet<String>();
    set.add("s1");
    set.add("s2");
    student.setSet(set);
    System.out.println(student.toString());

}

输出打印:

Student{name='英子', address=西安, books=[三国, 水浒, 西游], list=[清单1, 清单2], map={key1=Value1, key2=Value2}, set=[s1, s2], wife='null', info=null}

这种纯Java的配置方式,在SpringBoot中就很常见了

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值