spring

一、Spring 概述

1.1、web项目开发中的耦合度问题

·在Servlet中需要调用service中的方法,则需要在Servlet类中通过new关键字创建Service的实

public interface ProductService{
        public List<Product> listProducts();
    }
public interface ProductService{
        public List<Product> listProducts();
    }
public class ProductServiceImpl2 implements ProductService{
        public List<Product> listProducts(){
            //查询好评商品
        }
    }
public class ProductListServlet extends HttpServlet{
    //在servlet中使用new关键字创建ProductServiceImpl1对象,增加了servlet和service的耦合度
    private ProductService productService = new ProductServiceImpl1();
    
    protected void doGet(HttpServletRequest request,HttpServletResponse response){
        doPost(request,response);
    }
    protected void doPost(HttpServletRequest request,HttpServletResponse response){
        productService.listProducts();
    }
}

·在service实现类中需要调用DAO中的方法,也需要在service实现类通过new关键字创建DAO实现类对象
·如果在实现new关键字创建对象:

 ·标题失去了面向接口编程的灵活性
 ·代码的侵入性增强(在一个类中new另外一个,增加了耦合度)、降低了代码的灵活性

1.2、 面向接口编程

在这里插入图片描述
解决方案:在Servlet中定义Service接口的对象,不适用new关键字创建实现类对象,在servlet的实例化的时候,通过反射动态的给Service接口的对象变量赋值。
如何实现:Spring可以做到!

1.3Spring介绍

Spring是一个轻量级的控制反转和面向切面的容器框架,用来解决企业项目开发的复杂度问题——解耦
·轻量级:体积小,对代码没有侵入性
·控制反转:IoC(Inverse of Control),把创建对象的工作由Spring完成,·Spring在创建对象的时候同时可以完成
对象属性赋值(DI)
·面向切面:AOP(Aspect Oriented Programming)面向切面编程,可以在不改变原有业务逻辑的情况下实现对业务的增强
·容器:实例的容器,管理创建的对象.

1.4spring架构

·官网 https://spring.io/
在这里插入图片描述

·Spring架构图
在这里插入图片描述

1.4.1 Core Container

Spring容器组件,用于完成实例的创建和管理
·core 核心
·beans 实例管理
·context 容器上下文

1.4.2 AOP、Aspects

Spring AOP组件,实现面向切面编程
·AOP
·Aspects

1.4.3 web

Spring web组件实际指的是SpringMVC框架、实现web项目的MVC控制
·web(Spring对web项目的支持)
·webmvc(SpringMVC)

1.4.4 Data Access

Spring数据访问组件,也是一个基于JDBC封装的持久层框架(即使没有mybatis,Spring也可以完成持久化的数据库操作)
·tx

1.4.5 Test

Spring的单元测试组件,提供了Spring环境下的单元测试支持
·test

二:Spring IoC

Spring Ioc 容器组件,可以完成对象的创建,对象属性赋值,对象管理

2.1 Spring框架部署(IoC)

2.1.1 创建Maven工程

·Java
·Web

2.1.2 添加SpringIoc依赖

·core
·beans
·aop
·expression
·context

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

2.1.3 创建Spring配置文件

通过配置文件“告诉”Spring容器创建什么对象,给对象属性赋什么值
·在resource目录下创建名为 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:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--对于一个xml文件如果作为框架的配置文件,需要遵守框架的配置规则-->
<!--通常一个框架为了开发者能够正确的配置,都会提供xml的规范文件(dtd\xsd)-->
<!--xsd 可以根据配置引入多个,只要引入了相应的xsd,可以使用相应标签规则属性-->


</beans>

2.2 SpringIoC使用

使用SpringIoC组件创建并管理对象

2.2.1 创建一个实体类

public class Student {

    private String stuNum;
    private String stuName;
    private String stuGender;
    private int stuAge;
    private Date enterenceTime;//入学日期

}

2.2.2 在Spring配置文件中配置实体类

ClassPathXmlApplicationContext

//通过Spring容器创建Student对象
//1、初始化Spring容器,加载Spring配置文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2、通过Spring容器,获取Student对象
Student student2 = (Student) context.getBean("stu");

2.3 IoC和DI

·IoC(Inverse of Control) 控制反转,依赖Spring对象工厂,完成对象的创建
·DI (Dependency Injection)依赖注入,在Spring完成对象创建的同时依赖Spring容器完成对象属性的赋值

2.3.1 IoC

当我们需要通过Spring对象工厂创建某个类的对象时候,需要将这个交给Spring管理——通过bean标签配置

<bean id="book" class="com.liguoqing.ioc.bean.Book"></bean>  

2.3.2 DI

通过Spring容器给创建的对象属性赋值

<!--通过bean标签将实体类配置给Spring进行管理,id表示实体类的唯一标识(自己定义,一般是类名小写)-->
    <bean id="stu" class="com.liguoqing.ioc.bean.Student">
        <property name="stuNum" value="10002"/>
        <property name="stuName" value="李四"/>
        <property name="stuGender" value="女"/>
        <property name="stuAge" value="18"/>
        <property name="enterenceTime" ref="date"/>
    </bean>

2.4 DI依赖注入

2.4.1 依赖注入三种方式

Spring容器加载配置文件之后,通过反射创建类的对象,并给属性赋值;
Spring容器通过反射实现属性注入有三种种方式:
·set方法注入
·构造器注入
·接口注入(不常用)

2.4.2 set方法注入

在bean标签中通过配置property标签给对象属性赋值,实际上就是通过反射调用set方法完成属性的注入
简单类型以及字符串
·直接通过property标签的value属性赋值

<bean id="stu" class="com.liguoqing.ioc.bean.Student">
    <!--简单类型-->
    <property name="stuNum" value="10001"/>
    <!--字符串类型--> 
    <property name="stuName" value="李四"/>
</bean>

日期对象
方式1:在property标签中通过ref引用Spring容器中的一个对象

 <bean id = "date" class="java.util.Date"></bean>
   
    <bean id="stu" class="com.liguoqing.ioc.bean.Student">
        <property name="enterenceTime" ref="date"/>
    </bean>

方式2:在property标签中添加子标签bean来指定对象

<bean id="stu" class="com.liguoqing.ioc.bean.Student">
        <property name="enterenceTime">
            <bean class="java.util.Date"/>
        </property>
    </bean>

自定义类对象属性
方式1:

   <bean id="clazz" class="com.liguoqing.ioc.bean.Clazz">
        <property name="classId" value="2010"/>
        <property name="className" value="java2010"/>
    </bean>
    
    <bean id="stu" class="com.liguoqing.ioc.bean.Student">
        <property name="clazz" ref="clazz"/>
    </bean>

方式2:

 <bean id="stu" class="com.liguoqing.ioc.bean.Student">
        <property name="clazz">
            <bean class="com.liguoqing.ioc.bean.Clazz">
                <property name="classId" value="2010"/>
                <property name="className" value="java2010"/>
            </bean>
        </property>
    </bean>

集合类型
1: List List中的元素是字符串或者简单类型的封装类

 <bean id="stu" class="com.liguoqing.ioc.bean.Student">
        <property name="hobbies" value="旅游,电影"/>
    </bean>
    --------------------或者下面这种引用方式也可以---------------------------------------
    <bean id="stu" class="com.liguoqing.ioc.bean.Student">
           <property name="hobbies">
            <list>
               <value>旅游</value>
               <value>电影</value>
               <value>吃粑粑</value>
            </list>
        </property>
    </bean>

2:List List中的元素是对象类型

 <bean id="stu" class="com.liguoqing.ioc.bean.Student">
        <property name="hobbies">
            <list>
                <bean class="com.liguoqing.ioc.bean.Book"/>
                <bean class="com.liguoqing.ioc.bean.Book"/>
                <bean class="com.liguoqing.ioc.bean.Book"/>
            </list>
        </property>
    </bean>
 ------------------或者下面这种引用方式也可以-----------------------------------------------------
    <bean id="book" class="com.liguoqing.ioc.bean.Book"></bean>
    <bean id="stu" class="com.liguoqing.ioc.bean.Student">
          <property name="hobbies">
            <list>
                <ref bean="book"></ref>
                <ref bean="book"></ref>
                <ref bean="book"></ref>
            </list>
        </property>
    </bean>   

·Set

Set 略,自己看list,只需要原来的list标签换成set标签

·Map
键值对

  <bean id="stu" class="com.liguoqing.ioc.bean.Student">
        <property name="map">
            <map>
                <entry>
                    <key>
                        <value>k1</value>
                    </key>
                    <value>123</value>
                </entry>
                <entry>
                    <key>
                        <value>k2</value>
                    </key>
                    <value>456</value>
                </entry>
            </map>
        </property>
    </bean>

·Properties

 <bean id="stu" class="com.liguoqing.ioc.bean.Student">
       <property name="properties">
           <props>
               <prop key="key1">aaa</prop>
               <prop key="key2">aaa</prop>    
           </props>
        </property>
    </bean>

2.4.3 构造器注入

简单类型、字符串、对象

 public class Student {
        private String stuNum;
        private String stuName;
        private String stuGender;
        private int stuAge;
        private Date date;
        private Clazz clazz;
    
       public Student(String stuNum, String stuName, String stuGender, int stuAge, Date date, Clazz clazz) {
        this.stuNum = stuNum;
        this.stuName = stuName;
        this.stuGender = stuGender;
        this.stuAge = stuAge;
        this.date = date;
        this.clazz = clazz;
    }
}
 
 --------------------------------------------------
 
 <bean id="data" class="java.util.Date"></bean>

    <bean id="stu" class="com.liguoqing.ioc.bean.Student">
        <constructor-arg index="0" value="10001"/> <!--字符串类型-->
        <constructor-arg value="张三"/>
        <constructor-arg value="女"/>
        <constructor-arg value="21"/>  <!--简单类型-->
        <constructor-arg ref="data"/>   <!--对象类型-->
        <constructor-arg >
            <bean class="com.liguoqing.ioc.bean.Clazz"></bean>
        </constructor-arg>    <!--自定义类对象类型-->

    </bean>

集合类型属性


public class Student {
    private List<String> hobbies;
    private Set<String> sets;
    private Map<String,Object> maps;
    private Properties properties;
    
    public Student(List<String> hobbies, Set<String> sets, Map<String, Object> maps, Properties properties) {
         this.hobbies = hobbies;
        this.sets = sets;
        this.maps = maps;
        this.properties = properties;
    }
}

-----------------------------------------------------------------------------------------------

<bean id="stu2" class="com.liguoqing.ioc.bean.Student">
    <constructor-arg index="0">
        <list>
            <value>11</value>
            <value>13</value>
            <value>12</value>
        </list>
    </constructor-arg>
    <constructor-arg index="1">
        <set>
            <value>11</value>
            <value>13</value>
            <value>12</value>
        </set>
    </constructor-arg>
    <constructor-arg index="2">
        <map>
            <entry>
                <key><value>1</value></key>
                <value>value</value>
            </entry>
            <entry>
                <key><value>2</value></key>
                <value>value2</value>
            </entry>
        </map>
    </constructor-arg>
    <constructor-arg index="3">
        <props>
            <prop key="k1">value1</prop>
            <prop key="k2">value2</prop>
        </props>
    </constructor-arg>
</bean>

2.5 Bean的作用域

在Bean标签中可以通过scope属性指定对象的作用域
scope=“singleton” 表示当前bean是单例模式(默认饿汉模式;Spring容器初始化阶段就会完成此对象的创建;当在bean标签中设置lazy-init=“true” 变为懒汉模式)
scope=“prototype” 表示当前bean是非单例模式,每次通过Spring容器获取此bean的对象时都会创建一个新的对象
单例

<bean id="book" class="com.liguoqing.ioc.bean.Book" scope="singleton" lazy-init="true"></bean>

多例

<bean id="book" class="com.liguoqing.ioc.bean.Book" scope="prototype" lazy-init="true"></bean>

2.6 Bean的生命周期方法

<!--在bean标签中可以通过 init-method 属性指定当前bean的初始化方法,初始化方法在构造器执行之后执行-->
    <!--在bean标签中可以通过 destroy-method 属性指定当前bean的销毁方法,销毁方法在对象销毁之前执行-->

Bean

package com.liguoqing.ioc.bean;

public class Book {
    private int bookId;
    private String bookName;


        //初始化方法:在创建当前类对象时调用的方法,进行一些资源准备工作
    public void init(){
        System.out.println("__________________init");
        this.bookId = 1;
        this.bookName = "初始值";
    }
    
        //销毁方法:在Spring容器销毁对象时调用此方法,进行一些资源回收性的操作
    public void destory(){
        System.out.println("__________destory");
    }

}Spring配置文件

```java
   <bean id="book" class="com.liguoqing.ioc.bean.Book" scope="prototype"
          init-method="init" destroy-method="destory"></bean>

2.7 自动装配

<!--自动装配:Spring在实例化当前bean的时候从Spring容器中找到匹配的实例赋值给当前bean的属性-->
<!--  autowire="byName" 根据当前Bean的属性名,在Spring容器中寻找匹配的对象,如果根据Name找到了bean,但是类型不匹配则会抛出异常。 -->
<!--  autowire="byType" 根据当前Bean的属性类型,在spring容器中寻找匹配的对象,如果根据类型找打了多个类型匹配的bean,也会抛出异常-->
<bean id="stu3" class="com.liguoqing.ioc.bean.Student" autowire="byName"></bean>

2.8 SpringIoC 工作原理

在这里插入图片描述

三、SpringIoC——基于注解

SpringIoC的使用,需要我们通过XML将类声明给Spring容器进行管理,从而通过Spring工厂完成对象的创建及属性值的注入;
Spring除了提供基于XML的配置方式,同时提供了基于注解的配置;直接在实体类中添加注解声明Spring容器管理,以简化开发步骤。

3.1 SpringIoC框架部署

3.1.1 创建Maven项目

3.2.2 添加SpringIoC依赖

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

3.2.3 创建Spring配置文件

因为Spring容器初始化时,只会加载applicationContext.xml文件,那么我们在实体类中添加的注解就不会被Spring扫描,所以我们需要在applicationContext.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.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context.xsd">


<!--    声明使用注解配置-->
    <context:annotation-config/>
<!--    声明Spring工厂注解的扫描范围-->
    <context:component-scan base-package=""/>


</beans>

3.2 IoC常用注解

3.2.1 @Component(“stu”)

1、类的注解,声明此类被Spring容器进行管理,相当于bean标签的作用
2、@Component(value=“类的别名”) value属性用于制定当前bean的id属性,或者叫别名。value属性也可以省略,如果省略,那么当前类的id默认为当前类名首字母改小写
3、@Service、@Controller、@Repository这三个注解也可以将类声明给Spring管理,他们主要是语义上的区别
·@Controller注解主要声明将控制器类配置给Spring管理,例如Servlet
·@Service注解主要声明业务处理类配置给Spring管理,Service接口的实现类
·@Repository注解主要声明持久化类配置给Spring管理,DAO接口
·@Component除了控制器,Service,DAO之外的类一律使用此注解声明

3.2.2 @Scope(value = “prototype”)

·类注解,用于声明当前类单例模式还是非单例模式,相当于bean标签的scope属性
·@Scope(“propotype”) 表示当前类为非单例模式(默认单例模式)

3.2.3 @Lazy

·类注解,用于声明一个单例模式的bean是否为懒汉模式
·@Lazy(true) 表示声明为懒汉模式,默认为饿汉模式

3.2.4 @PostConstruct

·方法注解,声明一个方法为当前类的初始化方法(在构造器之后执行),相当于bean标签的init-method属性

3.2.5 @PreDestroy

·方法注解,声明一个方法为当前类的销毁方法(在对象从容器中释放之前执行),相当于bean标签的destory-method属性

3.2.6 @Autowired

1、属性注解、方法注解(set)声明当前属性自动装配,默认byType,默认必须(如果没有找到类型与属性类型匹配的bean则抛出异常)
2、@Autowired(required = false) 通过required属性设置当前自动装配是否为必须(默认必须——如果没有找到类型与属性类型匹配的bean则抛出异常)
·byType
·ref引用
·@Qualifier(“a”) 通过id

@Autowired(required = false)
public void setClazz(@Qualifier("a") Clazz clazz) {
    this.clazz = clazz;
}

3.2.7 @Resource

1、属性注解,也用于声明属性自动装配
2、默认装配方式为byName,如果根据byName没有找到相应的bean,则继续根据byType寻找对应的bean,根据byType如果没找到bean,或者找到不止一个类型匹配的bean则抛出异常。

四、代理设计模式

4.1 生活中的代理

在这里插入图片描述
代理设计模式的优点:将通用性的工作交由代理对象完成,被代理对象只需专注自己的核心业务。

4.2 静态代理

静态代理,代理类只能够为特定的类生产代理对象,不能代理任意类
在这里插入图片描述
使用代理的好处:
1、被代理类中只用关注核心业务的实现,将通用的管理型逻辑(事务管理、日志管理)和业务逻辑分离
2、将通用的代码放在代理类中实现,提高了代码的复用性
3、通过在代理类添加业务逻辑,实现对原有业务逻辑扩展(增强)

4.3 动态代理

动态代理,几乎可以为所有的类产生代理对象
动态代理的实现方式有两种:
JDK动态代理
CGLib动态代理
·JDK动态代理

package com.liguoqing.dao;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * JDK动态代理:是通过被代理对象实现的接口产生其代理对象
 * 1:创建一个类,实现InvocationHandler接口,重写invoke方法
 * 2:在类中定义一个Object类型的变量,用于传递被代理对象,并提供这个变量的有参构造器,用于将被代理对象传递进来
 * 3:创建getProxy方法,用于创建并返回代理对象
 * */
public class JDKMyDynamicProxy implements InvocationHandler {

    //被代理对象
    private Object obj;
    public JDKMyDynamicProxy(Object obj) {
        this.obj = obj;
    }

    //产生代理对象,返回代理对象
    public Object getProxy(){
        //1、获取被代理对象的类加载器
        ClassLoader classLoader = obj.getClass().getClassLoader();
        //2、获取被代理对象的类所实现的接口
        Class<?>[] interfaces = obj.getClass().getInterfaces();
        //3、产生代理对象(通过被代理对象的类加载器及实现的接口)
        //第一个参数:被代理对象的类加载器
        //第二个参数:被代理对象实现的接口
        //第三个参数:使用产生的代理对象调用方法时,用于拦截方法执行的处理器
        Object proxy = Proxy.newProxyInstance(classLoader, interfaces,this);
        return proxy;
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        begin();
        Object returnValue = method.invoke(obj,args);//执行method方法(入参)
        commit();
        return returnValue;
    }
    public void begin(){
        System.out.println("~~~~~~~开启事务");
    }
    public void commit(){
        System.out.println("~~~~~~~提交事务");
    }

}

----------------------------------------------------------------
测试类:
package com.liguoqing.test;

import com.liguoqing.dao.BookDAOImpl;
import com.liguoqing.dao.GenaralDAO;
import com.liguoqing.dao.JDKMyDynamicProxy;
import com.liguoqing.dao.StudentDAOImpl;

public class TestDynamicProxy {
    public static void main(String[] args) {
        //被代理对象
        BookDAOImpl bookDAO = new BookDAOImpl();
        StudentDAOImpl studentDAO = new StudentDAOImpl();

        //创建动态代理类对象,并将被代理对象传递到代理类中,赋值给obj
        JDKMyDynamicProxy jdkMyDynamicProxy = new JDKMyDynamicProxy(studentDAO);

        //proxy就是产生的代理对象,产生的代理对象可以强转成被代理对象实现的接口类型
        GenaralDAO proxy = (GenaralDAO) jdkMyDynamicProxy.getProxy();

        //使用代理对象调用方法,并不会执行调用的方法,而是进入到创建代理对象时指定的InvocationHandler类中的invoke方法
        //调用的方法作为一个Method参数,传递给了invoke方法
        proxy.delete();

    }
}

·CGLib动态代理
由于JDK动态代理时通过被代理类实现的接口来创建代理对象的,因此JDK动态代理只能代理实现了接口的类对象。如果一个类没有实现任何接口,该如何产生代理对象呢?——CGLib动态代理
CGLib动态代理,是通过创建被代理类的子类来创建代理对象的,因此即使没有实现任何接口的类也可以通过CGLib产生代理对象

CGLib动态代理不能为final修饰的类创建代理对象

·添加CGLib的依赖

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>

·CGLib动态代理代码实现

package com.liguoqing.dao;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * 1:添加cglib依赖
 * 2:创建一个类,实现MethodInterceptor接口,同时实现接口中的intercept方法
 * 3:在类中定义一个Object类型的变量,并提供这个变量的有参构造器,用于传递被代理对象
 * 4:定义getProxy方法创建并返回代理对象(代理对象是通过创建被代理类的子类来创建的)
 * */

public class CGLibDynamicProxy implements MethodInterceptor {

    private Object obj;

    public CGLibDynamicProxy(Object obj) {
        this.obj = obj;
    }

    public Object getProxy(){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(obj.getClass());
        enhancer.setCallback(this);
        Object proxy = enhancer.create();
        return proxy;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        begin();
        Object returnValue = method.invoke(obj,objects);//通过反射调用被代理类的方法
        commit();
        return returnValue;
    }
    public void begin(){
        System.out.println("~~~~~~~开启事务");
    }
    public void commit(){
        System.out.println("~~~~~~~提交事务");
    }
}
------------------------------------------------------------------------------------
测试类:


package com.liguoqing.test;

import com.liguoqing.dao.*;

public class TestDynamicProxy {
    public static void main(String[] args) {
        //创建被代理对象
        BookDAOImpl bookDAO = new BookDAOImpl();
        StudentDAOImpl studentDAO = new StudentDAOImpl();
        
        //通过cglib动态代理类创建代理对象
        CGLibDynamicProxy cgLibDynamicProxy = new CGLibDynamicProxy(bookDAO);
        //代理对象实际上是被代理对象子类,因此代理对象可以直接强转为被代理类类型
        BookDAOImpl proxy = (BookDAOImpl)cgLibDynamicProxy.getProxy();

        //使用代理对象调用方法,实际上并没有执行这个方法,而是执行了代理类中的intercept方法,将当前调用的方法以及方法中的参数
        //传递到intercept方法
        proxy.update();

    }
}

五、SpringAOP

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值