Spring1

Spring 学习(1)

  • 分层架构
  • web 层 ---- Spring MVC | struts
  • service 层 ---- Spring
  • dao 层 ---- Mybatis | JDBCTemplate | hibernate —> Spring-date

Spring 核心

  • Spring 核心是控制反转(Ioc)面向切片(Aop)
  • Ioc
  • Aop

Spring 优点

  • 方便解耦,简化开发(高内聚、低耦合)

Spring 就是大工厂(容器),用于生成Bean

  • Aop 编程的支持

  • 声明式事务的支持

  • 方便程序的测试

  • 方便集成各种优秀的框架

  • 降低JavaEE Api 的使用难度

Spring 体系结构

20180629101137482.png

  • 核心容器: beans、core、context、expression

Spring开发:

入门案例:Ioc(控制反转)

  • 导入jar 包和依赖

  • jar 包
spring-expression-4.3.18.RELEASE.jar
spring-context-4.3.18.RELEASE.jar
spring-beans-4.3.18.RELEASE.jar
spring-core-4.3.18.RELEASE.jar

  • 依赖
    commons-logging-1.2.jar
目标类
  • 提供UserService 接口和实现类
  • 获取UserService 实现类的实例
  • 使用Spring 创建实例对象(Ioc 控制反转)

  • UserService 接口
package com.spring1_Ioc;

/**
 * @InterfaceName Userservice
 * @Author 秃头的JJ
 * Date 2019/5/21 0021 20:20
 */
public interface Userservice {

    void springTest();

}


  • UserServiceImpl 实现类
package com.spring1_Ioc.impl;

import com.spring1_Ioc.Userservice;

/**
 * @ClassName UserServiceImpl
 * @Author 秃头的JJ
 * Date 2019/5/21 0021 20:21
 * Version 1.0
 */
public class UserServiceImpl implements Userservice {
    @Override
    public void springTest() {
        System.out.println("Spring Hello");
    }
}

配置文件
  • 配置文件位置任意,在开发中一般在classPath(src)下
  • 配置文件名称任意,开发中常用applicationContext.xml
  • 内容:添加schema 约束

  • 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
        http://www.springframework.org/schema/beans/spring-beans.xsd">


    <!-- 配置bean
            bean:是需要创建的对象
            id:实例的id
            class:实例的位置
    -->
    <bean id="userServiceId" class="com.spring1_Ioc.impl.UserServiceImpl" >

    </bean>

</beans>
测试方法
@Test
    public void springTest(){
        /* 从Spring 容器获取 */

        // 加载容器
        String xmlPath = "applicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(xmlPath);

        // 从Spring 容器内获取实例
        Userservice us = (Userservice) ac.getBean("userServiceId");
        us.springTest();

    }

入门案例:DI(依赖注入) — 使用 子标签

  • is a:是一个 — 继承
  • has a:有一个 — 成员变量(依赖)
  • 依赖:一个对象需要使用另一个对象
  • 注入:提供setter 方法进行另一个对象实例的设置
目标类
  • 创建两个接口和实现类
  • 配置到xml 文件内

  • Dao 接口
package com.spring2_DI;

/**
 * @InterfaceName Dao
 * @Author 秃头的JJ
 * Date 2019/5/21 0021 21:39
 */
public interface Dao {

    void daoTest();

}


  • Dao 实现类
package com.spring2_DI.impl;

import com.spring2_DI.Dao;

/**
 * @ClassName DaoImpl
 * @Author 秃头的JJ
 * Date 2019/5/21 0021 21:39
 * Version 1.0
 */
public class DaoImpl implements Dao {

    public DaoImpl() {
        System.out.println("Dao Test DaoImpl");
    }

    @Override
    public void daoTest() {
        System.out.println("Dao Test");
    }
}


  • UserService_DI 接口
package com.spring2_DI;

/**
 * @InterfaceName UserService_DI
 * @Author 秃头的JJ
 * Date 2019/5/21 0021 21:37
 */
public interface UserService_DI {

    void DiTest();

}


  • UserService_DI 实现类
package com.spring2_DI.impl;

import com.spring2_DI.Dao;
import com.spring2_DI.UserService_DI;

/**
 * @ClassName UserService_DIImpl
 * @Author 秃头的JJ
 * Date 2019/5/21 0021 21:38
 * Version 1.0
 */
public class UserService_DIImpl implements UserService_DI {
    private Dao dao;
    private String str;

    public void setDao(Dao dao) {
        this.dao = dao;
    }

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

    @Override
    public void DiTest() {
        System.out.println(this.str);
    }
}

配置文件
   property: 依赖注入所使用的标签
        name: 所要输入的属性名称 ===== 要与实例化的类内的属性名称保持一致
        ref: 指代需要传入的实例化对象的bean 的id
        value: 注入到属性的具体值
    -->
    <bean id="UserService_DIImpl" class="com.spring2_DI.impl.UserService_DIImpl">
        <property name="dao" ref="daoID"/>
        <property name="str" value="HelloWorld This Is A String" />
    </bean>

    <bean id="daoID" class="com.spring2_DI.impl.DaoImpl"></bean>
测试方法
package com.spring2_DI.test;

import com.spring2_DI.impl.UserService_DIImpl;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @ClassName MainTest
 * @Author 秃头的JJ
 * Date 2019/5/21 0021 20:31
 * Version 1.0
 */
public class MainTest {
    private static ApplicationContext ac;
    @BeforeAll
    public static void init(){

        // 加载容器
        String xmlPath = "applicationContext.xml";
        ac = new ClassPathXmlApplicationContext(xmlPath);
    }

    @Test
    public void springTest(){
        /* 从Spring 容器获取 */
        // 从Spring 容器内获取实例
        UserService_DIImpl us = (UserService_DIImpl) ac.getBean("UserService_DIImpl");
        us.DiTest();

    }

}

核心API

  • BeanFactory

用于生成任意Bean

  • ApplicationContext
  • 采用延时加载,在第一次调用getBean() 时才会初始化Bean

是BeanFactory 的子接口,功能更加强大(国际化处理、事件传递、Bean 自动装配、各种不同应用层的Context 实现)

  • ClassPathXmlApplicationContext 用于记载classPath(src) 下的指定的XML 文件
  • FileSystemXmlApplicationContext 用于记载指定盘符下的指定XML 文件

基于XMl 的装配Bean

Bean 的实例化方式
  • 三种Bean 的实例化方式:默认构造、静态工厂、实例工厂
  • 默认构造
  • <bean id="" class=""> 必须提供默认构造
  • 静态工厂
  • 常用于Spring 整合其他框架(工具)
  • 用于生产实例,所有方法必须是static
    <bean id="" class="工厂的路径" factory-method="静态方法">
  • 实例代码

  • applicationContext.xml
<!-- 配置静态工厂 
        id: 静态工厂的id
        class:静态工厂的全路径类名
        factory-method:静态工厂内的静态方法(创建类的方法)
        
        由Spring 代理静态工厂,并调用创建类的方法,实现和FactoryName.FatoryMethodName() 一样的功能(输出被创建类的对象)
    -->
    <bean id="staticFactoryTest" 
          class="com.spring3_static_factory.Factory" 
          factory-method="createDao"></bean>

  • 自定义工厂类
package com.spring3_static_factory;

/**
 * @ClassName User
 * @Author 秃头的JJ
 * Date 2019/5/22 0022 10:14
 * Version 1.0
 */
public class Factory {

    private static final Dao dao = new Dao();

    public static Dao createDao(){
        return dao;
    }

}


  • 工厂实现类
package com.spring3_static_factory;

/**
 * @ClassName Dao
 * @Author 秃头的JJ
 * Date 2019/5/22 0022 10:14
 * Version 1.0
 */
public class Dao {

    public void print(){
        System.out.println("Hello Static Fatory");
    }

}


  • 测试方法
@Test
    public void staticFactoryTest(){

        String xmlPath = "applicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(xmlPath);
        Dao dao = ac.getBean("staticFactoryTest", Dao.class);
        dao.print();
    }
  • 实例工厂
  • 必须先有工厂实例对象,通过实例对象创建对象。提供的所有方法都是非静态的

  • applicationContext.xml
<!--
        实例工厂的使用:由Spring 创建工厂Bean 和待实现类的Bean
        factory-bean: 指代工厂类Bean 的id
        factory-method: 指代工厂类内创建实现类对象的方法
    -->
    <!-- 创建实例工厂的Bean -->
    <bean id="factory" class="com.spring4_factory.Factory"></bean>
    <!-- 使用实例工厂的Bean 创建实现对象 -->
    <bean id="dao" factory-bean="factory" factory-method="createDao"></bean>

  • Factory 工厂类
package com.spring4_factory;

/**
 * @ClassName Factory
 * @Author 秃头的JJ
 * Date 2019/5/22 0022 10:55
 * Version 1.0
 */
public class Factory {

    private final Dao dao = new Dao();

    public Dao createDao(){
        return dao;
    }

}


  • 待实现类
package com.spring4_factory;

/**
 * @ClassName Dao
 * @Author 秃头的JJ
 * Date 2019/5/22 0022 10:55
 * Version 1.0
 */
public class Dao {

    public void print(){
        System.out.println("Hello Factory");
    }

}


  • 测试方法
 @Test
    public void factoryTest(){

        String xmlPath = "applicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(xmlPath);
        Dao dao = ac.getBean("dao", Dao.class);
        dao.print();

    }
Bean 的种类
普通Bean
  • <bean id="" class=""> Spring 会直接创建class 对应类型的实例,并返回
FactoryBean
  • 是特使的Bean ,具有工厂生产对象的能力,但是只能生产特点的对象.
    Bean 必须实现FactoryBean 接口, 该接口提供了getObject() 方法获取特点的Bean
  • <bean id="" class="FactoryBean"> 先创建FactoryBean 实例,在调用getObject() 方法,并返回方法的返回值
  • 类比于
FactoryBean fb = new FactoryBean();
return fb.getObject();
注:
  • BeanFactory 和FactoryBean 的区别
  1. BeanFactory : 工厂,生产任意Bean
  2. FactoryBean : 是一个特殊Bean, 生产一个特定的Bean(过去代理对象的实例 AOP是使用)
作用域
  • 作用域:用于确定Spring 所创建的Bean 的实例的个数
类别说明
singleton*(单例,默认的)在Spring Ioc 容器内中仅存在一个Bean 实例,Bean 以单例方式存在,默认值
prototype*(多例)每次从容器中调用Bean 时,都会返回一个新的实例
request每次HTTP 请求都会创建一个新的Bean ,该作用域仅适用于WebApplicationContext 环境
session同一个HTTP session 共享一个Bean, 不同Session 使用不同的Bean ,仅适用于WebApplicationContext 环境
globalSession一般使用在Portlet 应用环境,仅适用于WebApplicationContext 环境
  • 配置信息

<bean id="" class="" scope="">


  • applicationContext.xml
<!--
    作用域:默认使用singleton (单例)
    singleton: 多例,每请求一次,创建一个新的实例
 -->
<bean id="scopTest" class="com.spring5_scope.ScopTest" scope="prototype" />

  • ScopTest 测试类
package com.spring5_scope;

/**
 * @ClassName ScopTest
 * @Author 秃头的JJ
 * Date 2019/5/22 0022 16:03
 * Version 1.0
 */
public class ScopTest {

    public void print(){
        System.out.println(this.hashCode());
    }

}


  • 测试方法
@Test
    public void factoryTest(){

        String xmlPath = "com/spring5_scope/applicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(xmlPath);
        ScopTest st = ac.getBean("scopTest", ScopTest.class);
        ScopTest st1 = ac.getBean("scopTest", ScopTest.class);
        System.out.println(st);
        System.out.println(st1);

    }
生命周期
  • Spring 生命周期(11个步骤)

Spring 生命周期

  1. instantiate Bean 对象实例化
  2. populate properties 封装属性
  3. 如果Bean 实现BeanNameAware 执行setBeanName
  4. 如果Bean 实现BeanfactoryAware 或者 ApplicationContextAware 设置工厂setBeanFactory 或者上下文对象setApplicationContext
  5. 如果存在类实现BeanPostProcessor(后处理Bean), 执行postProcessBeforeInitialization
  6. 如果Bean 实现执行Initialization 执行afterPropertiesSet
  7. 调用<bean init-method="初始化方法"> 指定初始化方法
  8. 如果存在类实现BeanPostProcessor(处理Bean), 执行执行postProcessAfterInitialization
  9. 执行业务处理
  10. 如果Bean 实现DisposableBean 执行destory(销毁)
  11. 调用<bean destory-method="“销毁方法"> 指定销毁的方法
初始化和销毁
  • 在目标方法执行前和执行后,进行初始化和销毁

<bean id="" class="" init-method="初始化方法" destory-method="销毁方法">

  • 销毁方法执行条件1、必须调用Close 方法;2、必须时单例

  • applicationContext.xml
<!--
    定义初始化和销毁方法
    init-method: 指定初始化方法(初始化方法可以为私有)
    destroy-method: 指定销毁方法(销毁方法可以为私有)
    销毁方法执行条件:1、必须调用Close 方法;2、必须时单例
-->
<bean id="lifecycle" class="com.spring6_lifecycle.LifeCycleBean"
      init-method="init" destroy-method="destory" />


  • LifeCycleBean 测试Bean
package com.spring6_lifecycle;

/**
 * @ClassName LifeCycleBean
 * @Author 秃头的JJ
 * Date 2019/5/22 0022 16:45
 * Version 1.0
 */
public class LifeCycleBean {

    private void init(){
        System.out.println("初始化方法");
    }

    public void print(){
        System.out.println("Hello Life Cycle Bean");
    }

    private void destory(){
        System.out.println("销毁");
    }
}


  • 测试方法
@Test
public void lifeCycleTest() throws Exception {

    String xmlPath = "com/spring6_lifecycle/applicationContext.xml";
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext(xmlPath);
    LifeCycleBean lcb = ac.getBean("lifecycle", LifeCycleBean.class);
    lcb.print();

    /* 使用反射调用 close 方法 */
//   ac.getClass().getMethod("close").invoke(ac);

    ac.close();

}
BeanPostProcessor 后处理Bean
  • Spring 提供的一种机制,需要实现此接口BeanPostProcessor,并将实现类提供给Spring
    容器, Spring 容器自动执行,在初始化方法执行前执行befor(), 在初始化方法执行之后执行
    after()
  • Spring 提供工厂钩子,用于修改实例对象,可以生产代理对象,时AOP 的底层
    TIM截图20190522170050.png

  • applicationContext.xml
<!--
        配置测试Bean
     -->
    <bean id="testBean" class="com.spring7_BeanPostProcessor.pojo.TestBean"
          init-method="init" destroy-method="destory"></bean>

    <!--
        将BeanPostProcessor 实现类提供个Spring 进行管理
        其逻辑类比于:
        A a = new A();
        // 在这里调用BeanPostProcessor 实现类的before 方法
        a = B.before(a);
        a.init();
        // 在这里调用BeanPostProcessor 实现类的after 方法
        a = B.after(a);
        // 调用a 的方法
        a.print();
        a.destory();
    -->
    <bean class="com.spring7_BeanPostProcessor.pojo.BeanPostProcessorTest"></bean>

  • BeanPostProcessor 实现类
package com.spring7_BeanPostProcessor.pojo;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

/**
 * @ClassName BeanPostPrecessorTest
 * @Author 秃头的JJ
 * Date 2019/5/22 0022 19:13
 * Version 1.0
 */
public class BeanPostProcessorTest implements BeanPostProcessor {


    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization 执行了");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization 执行了");
        return bean;
    }
}


  • TestBean 测试类
package com.spring7_BeanPostProcessor.pojo;

/**
 * @ClassName TestBean
 * @Author 秃头的JJ
 * Date 2019/5/22 0022 19:13
 * Version 1.0
 */
public class TestBean {

    private void init(){
        System.out.println("初始化方法执行了");
    }

    public void print(){
        System.out.println("BeanPostPrecessorTest Say HelloWorld");
    }

    private void destory(){
        System.out.println("销毁方法执行了");
    }

}


  • 测试方法
 private static ApplicationContext ac;

    @BeforeAll
    public static void init(){
        String xmlPath = "com/spring7_BeanPostProcessor/applicationContext.xml";
        ac = new ClassPathXmlApplicationContext(xmlPath);
    }

    @Test
    public void beanPostProcessorTest(){
        TestBean tb = ac.getBean("testBean", TestBean.class);
        tb.print();
    }
属性的依赖注入
属性依赖注入 — 构造方法(定义了有参构造器需要再定义无参构造器避免错误发生)
  • applicationContext.xml 内Bean 方法配置
<!--
        使用构造器注入值:使用<constructor-arg> 标签
        index: 指代有参构造器参数列表的第几位参数(从o 开始)
        value: 指代具体的参数值
        ref: 传入另一个Bean
        name: 指代具体的参数名称
 -->
<bean id="user" class="com.spring9_DI_Contructor.pojo.User">
    <constructor-arg name="password" value="12138"/>
    <constructor-arg name="username" value="12138"/>
    <constructor-arg name="userInfo" ref="info"/>
</bean>

<bean id="info" class="com.spring9_DI_Contructor.pojo.UserInfo">
    <constructor-arg index="0" value="上官翠花"/>
    <constructor-arg index="1" value="女"/>
    <constructor-arg index="2" value="漕溪路"/>
    <constructor-arg index="3" value="1111级20班"/>
</bean>
属性依赖注入 — setter 方法(待实例化类内必须含有setter 方法)
  • applicationContext.xml
<!--
        setter 方法实现参数注入:使用<property> 标签
        name: 指代参数名称
        value: 指代具体的要传入的值
            <property name="" value="具体的值">
                    ==等价于==>
            <propery name="">
                <value>具体的值</value>
            </property>

        ref: 指代需要传入的bean
        bean: 具体的bean 实例
-->
<bean id="user" class="com.spring8_DI_setter.pojo.User">
    <property name="username" value="12138"></property>
    <property name="password">
        <value>12138</value>
    </property>
    <property name="userInfo" ref="info"></property>
</bean>

<bean id="info" class="com.spring8_DI_setter.pojo.UserInfo">
    <property name="sex" value="男"></property>
    <property name="addr" value="春熙路"></property>
    <property name="class_" value="2222级05班"></property>
    <property name="name" value="欧阳铁柱"></property>
</bean>
属性依赖注入 — P命名空间
  • 对setter 方法注入进行简化,替换了
    而是使用<bean p:属性名=“”"“普通值" p:属性名=”“引用值”>
  • 使用前提:需要添加命名空间
  • 在命名空间内加入:xmlns:p="http://www.springframework.org/schema/p"

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

    <!--
        p:属性名=“"属性值"
        作用与<property name="属性名" value="属性值"> 一样
        但是在处理List 集合等数据不太友善
    -->
    <bean id="user" class="com.spring10_DI_Pnamespace.pojo.User"
        p:username="12138"
        p:password="12138p"
        p:userInfo-ref="info">
        <property name="strings">
            <list>
                <value>HelloWorld1</value>
                <value>HelloWorld2</value>
                <value>HelloWorld3</value>
                <value>HelloWorld4</value>
                <value>HelloWorld5</value>
                <value>HelloWorld6</value>
            </list>
        </property>
    </bean>

    <bean id="info" class="com.spring10_DI_Pnamespace.pojo.UserInfo">
        <constructor-arg index="0" value="上官翠花"/>
        <constructor-arg index="1" value="女"/>
        <constructor-arg index="2" value="漕溪路"/>
        <constructor-arg index="3" value="1111级20班"/>
    </bean>
属性依赖注入 — SpEL
  • 对 进行同样编程,所有内容都使用value
    <property name="" value="#{表达式}">

#{123}、#{‘String’} ----> 数字、字符串
#{beanID} ----> 引用另一个bean
#{beanID.propName} ----> 操作属性
#{beanID.toString()} ----> 执行方法
#{T(静态类).字段或者方法} ----> 静态方法或字段的使用

属性依赖注入 — 集合注入
  • 数组
  • List
  • Set
  • Map — 键值对 使用 子标签
  • Properties (Props)

集合的注入都是使用 的子标签


  • applicationContext.xml
 <bean id="listTestId" class="com.spring12_DI_List.pojo.ListTest">
        <property name="strs">
            <!-- 注入数组值 -->
            <array>
                <value>欧阳铁柱</value>
                <value>上官翠花</value>
                <value>孙秃子</value>
                <value>李狗蛋</value>
            </array>
        </property>
        <property name="ints">
            <array>
                <value>1</value>
                <value>2</value>
                <value>3</value>
                <value>4</value>
            </array>
        </property>
        
        <property name="stringList">
            <!-- 注入List 集合值 -->
            <list>
                <value>欧阳铁柱</value>
                <value>上官翠花</value>
                <value>孙秃子</value>
                <value>李狗蛋</value>
            </list>
        </property>
        
        <property name="stringSet">
            <!-- 注入Set 集合值 -->
            <set>
                <value>欧阳铁柱</value>
                <value>上官翠花</value>
                <value>孙秃子</value>
                <value>李狗蛋</value>
            </set>
        </property>
        
        <property name="map">
            <!-- 注入Map 集合值 -->
            <map>
                <description>map Test</description>
                <!-- 使用entry 绑定键值对 -->
                <entry key="黄金缔造者" value="欧阳铁柱"></entry>
                <entry key="白银缔造者" value="上官翠花"></entry>
                <entry key="青铜缔造者" value="孙秃子"></entry>
                <entry key="黑铁缔造者" value="李狗蛋"></entry>
            </map>
        </property>
        <property name="props">
            <!-- 注入Properties 值 -->
            <props>
                <prop key="黄金">欧阳铁柱</prop>
                <prop key="白银">欧阳铁柱</prop>
                <prop key="青铜">欧阳铁柱</prop>
                <prop key="黑铁">欧阳铁柱</prop>
            </props>
        </property>
    </bean>
装配Bean 基于annotation (注解)
  • 注解: 实际上是一个Class ,使用 @注解名 来实现
  • 注解的作用就是简化开发,替代XML 配置文件
  • 使用注解的前提

TIM截图20190525142400.png

IOC 控制反转 ---- 创建Bean
  • @Component(“id”) 取代
  • 注解使用时,要添加命名空间,让Spring 扫描含有注解的Class
  • 在web 开发中,提供了3个 @Component 衍生注解(功能是一样的)
* @Repository --- Dao 层
* @Service --- service 层
* Controller --- 控制层(WEB 层)
DI 依赖注入 ---- 对Bean 内参数进行赋值
  • 可以个私有字段设置值,也可以使用setter 方法进行设置
  • 普通值: @Value(“值”)
  • 引用值:
  • 使用类型注入
  • @Autowired
  • 使用名称注入 (两种注入方式)
@Autowired
@Qualifier("名称")
  • @Resource(name = “名称”)
生命周期
  • 初始化:@PostConstruct
  • 销毁:@PreDestroy
作用域
  • @Socpe(“prototype”) ---- 多例

注解.png

XML 和注解配合使用

  • 一般是将所有Bean 配置在XML 内
  • 将所有依赖通过@Autowired 实现

注:
1、默认情况这样配置无效,需要在xml 内配置<context:annotation-config>
2、<context:annotation-config><context:component-scan> 不同时出现

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值