Spring学习笔记

目录

简介:

组成:

拓展:

IOC理论推导

HelloSpring

IOC创建对象的方式

Spring配置

别名(Alias)

Bean的配置

import

依赖(DI)注入

构造器注入

Set方式注入

拓展方式注入

p命名空间注入

 c命名空间注入(通过构造器注入)

 Bean作用域(Bean Scopes)

单例模式(spring默认就是单例)

原型模式

Bean的自动装配

1、手动显式装配

2、ByName自动装配

3、ByType自动装配

 使用注解实现自动装配

 使用注解开发

注解大全:

使用Java配置Spring

代理模式

静态代理

动态代理

AOP(面向切面编程)

整合Mybatis

mybatis

Mybatis-spring

声明式事务

Spring中的事务管理

声明式事务:AOP(常用)

编程式事务:需要在代码中声明事务的管理


Spring就是一个轻量级的控制反转和面向切面编程的框架

最全的XML配置

简介:

spring:春天----->给软件行业带来了春天

Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。 

优点:

  • 开源免费框架。
  • 轻量级非入侵式框架。
  • 控制反转(IOC),面向切面编程(AOP)
  • 支持事务的处理,对框架整合的支持。

官方:

https://spring.io/

2002年首次推出了Spring的雏形  interface21

2004年发布spring1.0版本  

创作者Rod Johnson

SSH:Struct2 + Spring + Hibernate

SSM: SppringMVC + Spring + Mybatis

Spring下载:

https://repo.spring.io/ui/repos/tree/General/libs-release-local%2Forg%2Fspringframework%2Fspring%2F3.2.0.RELEASE%2Fspring-framework-3.2.0.RELEASE-dist.zip

Maven需要的导入包

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



<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.9</version>
</dependency>

组成:

七大模块

博客https://blog.csdn.net/qq_33082731/article/details/75066956

拓展:

 SpringBoot:

        快速开发单个微服务的框架。

        约定大于配置!

Spring Cloud:

        基于Spring Boot实现的

大多数公司都在使用springBoot进行快速开发,学习Spring Boot的前提需要完全掌握Spring 和SpringMVC

弊端:发展太久之后,违背了原来的理念!配置十分繁琐。

IOC理论推导

控制反转(IOC)

使用一个Set接口实现

void getUserdao(UserDao  userdao){
    this.userdao=userdao;
}

这个理念只是一个原型。

HelloSpring

1.创建实体类

public class Hello {
    private String name;

    public String getName() {
        return name;
    }

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

2.创建配置文件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">
<!--使用Spring来创建对象,在Spring这些都成为bean
    Hello hello= new Hello();

    id=实例名   property给参数赋值
    <bean id="hello" class="com.song.pojo.Hello">
        <property name="name" value="Spring"/>
    </bean>
    ref=引用spring容器中创建好的对象

-->
    <bean id="hello" class="com.song.pojo.Hello">
        <property name="name" value="Spring"/>
    </bean>

</beans>

3.测试

public static void main(String[] args) {
        //获取spring的上下文对象
        ApplicationContext context= new ClassPathXmlApplicationContext("ApplicationContext.xml");
        //我们的对象现在都在spring中管理,我们要使用,直接去取就可以
        Hello hello=(Hello) context.getBean("hello");
        System.out.println(hello.toString());
    }

IOC创建对象的方式

1、使用无参构造创建对象(默认)

<bean id="hello" class="com.song.pojo.Hello">
        <property name="name" value="Spring"/>
    </bean>

2、使用有参构造创建

<!--1.使用下标赋值,第一个参数为0,类推-->
    <bean id="hello2" class="com.song.pojo.Hello">
        <constructor-arg index="0" value="songyi"/>
    </bean>
    <!--2.类型式赋值(不建议使用)-->
    <bean id="hello3" class="com.song.pojo.Hello">
        <constructor-arg type="java.lang.String" value="songyi"/>
    </bean>
    <!--3.直接通过参数名来设置-->
    <bean id="hello4" class="com.song.pojo.Hello">
        <constructor-arg name="name" value="songyi"/>
    </bean>

配置文件加载的时候,里边的所有实例就已经初始化了

Spring配置

别名(Alias)

<alias name="hello1" alias="h1"/>
    <bean id="hello1" class="com.song.pojo.Hello">
        <property name="name" value="Spring"/>
    </bean>

Bean的配置

<!--id:唯一标识符,即对象名
        class:对象所对应的全限定名:包名  +   类名
        name:也是别名  分隔符可以为空格   逗号  分号
        -->
    <bean id="hello1" class="com.song.pojo.Hello" name="user1   u2,u3;u4">
        <property name="name" value="Spring"/>
    </bean>

import

假设项目有多个人开发,多个ApplationContext.xml文件

<import resource="Beans2.xml"/>

 使用的时候直接使用总xml文件就行

依赖(DI)注入

构造器注入

即上边IOC创建对象的方式

Set方式注入

依赖注入:Set注入

依赖:bean对象的创建依赖于容器

注入:bean对象中的所有属性,由容器来注入

环境搭建

1.复杂类型

public class Student {
    private String name;
    private Address address;
    private String[] books;
    private List<String> hobbys;
    private Map<String,String> card;
    private Set<String> games;
    private String wife;
    private Properties info;
}

2.配置实现

 <bean id="address" class="com.song.pojo.Address">
        <property name="address" value="郑州"/>
    </bean>
    <bean id="student" class="com.song.pojo.Student">
        <!--普通注入-->
        <property name="name" value="宋燚"/>
        <!--Bean注入,ref-->
        <property name="address" ref="address"/>
        <!--数组注入-->
        <property name="books">
            <array>
                <value>红楼梦</value>
                <value>金瓶梅</value>
                <value>三国演义</value>
            </array>
        </property>
        <!--List-->
        <property name="hobbys">
            <list>
                <value>听歌</value>
                <value>打游戏</value>
            </list>
        </property>
        <!--Map-->
        <property name="card">
            <map>
                <entry key="身份证" value="657576575757565"/>
                <entry key="qq" value="687545768"/>
            </map>
        </property>
        <!--Set-->
        <property name="games" >
            <set>
                <value>LOL</value>
                <value>COC</value>
                <value>BOB</value>
            </set>
        </property>
        <!--Null-->
        <property name="wife">
            <null/>
        </property>
        <!--Properties-->
        <property name="info">
            <props>
                <prop key="学号">201705050137</prop>
                <prop key="性别">男</prop>
            </props>
        </property>
     </bean>

拓展方式注入

p和c命名需要在配置中导入约束

       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"

p命名空间注入

可以直接注入属性的值(适用于简单的属性)

 使用:

 c命名空间注入(通过构造器注入)

c输入的是构造器参数

 Bean作用域(Bean Scopes)

单例模式(spring默认就是单例)

scope='singleton'

    <bean id="user" class="com.song.pojo.User" p:age="12" name="宋燚" scope="singleton"/>

原型模式

每一次去get的时候都产生一个新的对象

scope="prototype"

    <bean id="user" class="com.song.pojo.User" p:age="12" name="宋燚" scope="prototype"/>

其余的request、session、application只能在web开发中使用

Bean的自动装配

有三种装配方式

1、在xml中显式的配置

2、在java中显式配置

3、隐式地自动装配Bean(重要)

测试:

1、手动显式装配

<bean id="cat" class="com.song.pojo.Cat"/>
    <bean id="dog" class="com.song.pojo.Dog"/>
    <bean id="people" class="com.song.pojo.People">
        <property name="name" value="宋燚"/>
        <property name="dog" ref="dog"/>
        <property name="cat" ref="cat"/>
    </bean>

2、ByName自动装配

3、ByType自动装配

 使用注解实现自动装配

jdk1.5支持的注解,spring2.5支持注解

使用xml开发太过麻烦,故使用注解开发

使用注解须知:

1.导入约束

2.配置注解的支持

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        https://www.springframework.org/schema/context/spring-context.xsd">
  <context:annotation-config/>
</beans>

3、测试

@Autowired标记在属性上侧,即自动寻找xml文件内的相对属性(可以忽略set,get方法使用)

id与字段名需一致(即默认先使用ByType方式自动装配)

 @Nullable 字段标记这个注解,表名字段可以为null

可以显式这为他指定一个值

使用@Qualifier(value="id")

 另一种方法

使用java内置注解@Resurce

可传参或不传参

 使用注解开发

1、bean

2、属性如何注入

@Component
public class User {
    @Value("宋燚")
    public String name;
}

3、衍生的注解

@Component有三个衍生注解,功能相同,用在不同的层(MVC三层)

  • @Repository(dao)
  • @Service(service)
  • @Controller(controller)

这四个注解功能都是一样的,都代表将某个类装配到spring容器中

4、自动装配置

上边有

5、作用域

@Scope

注解大全:

 @Autowired:自动装配;通过类型 再 名字

         @Autowired(value="id")

@Resource:自动装配;通过名字 再 类型

        @Resource(name="id")

@Qualifier:指定具体的名字

@Nullable:字段标记了这个注解,说明这个字段可以为null

@Component:类上标记该注解,等价于在xml中配置bean,名字默认类的小写 user

        @Component有三个衍生注解,功能相同,用在不同的层(MVC三层)

  •  @Repository(dao)
    • @Service(service)
    • @Controller(controller)
      • @RestController(返回的为资源,不跳转页面)
@Value("字符串"):加在属性上(或属性的set方法上)相当于给属性赋值

@Scope:设置作用域,标记在类上

        @Scope("singleton"):标注为单例模式

        @Scope("prototype"):标注为原型模式

使用Java配置Spring

要完全不使用xml配置,全部使用java来实现

JavaConfig是Spring的一个子项目,在Spring4之后,他成为了一个核心功能

配置类

//在一个类上加该注解类似于<beans>标签
//配置类,他和ApplitaionContext.xml的作用是一样的
@Configuration
//导入另一个配置类
@ComponentScan("com.song.pojo")
@Import(com.song.service.config2.class)
public class config {
    //相当于注册一个bean
    //方法名 即为 id
    //返回值 即为 class
    //return就是返回要注入到bean的对象
    @Bean
    public User getuser(){
        return new User();
    }
}

测试

public static void main(String[] args) {
              //如果完全使用配置类做,就需要AnnotationConfig来获取容器,通过配置的class对象加载
              ApplicationContext context=  new AnnotationConfigApplicationContext(config.class);
              //取的话使用配置中的方法名
              User geruser = (User)context.getBean("getuser");
              System.out.println(geruser.toString());
    }

纯Java的配置方式,在Spring Boot中随处可见

代理模式

中介;代理模式是SpringAOP的底层

静态代理

        抽象角色:一般会使用接口或抽象类来解决

        真实角色:被代理的角色

        代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作

        客户:访问代理对象的人

代理模式的好处:

  • 可以使真实角色的操作更加纯粹,不需要做其他事情
  • 公共业务交给代理角色,实现了业务的分工
  • 公共业务发生扩展的时候,方便集中管理

缺点:

        一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率会变低

动态代理

动态代理和静态代理角色一致

动态代理类是动态生成的,不是直接写好的

动态代理分为两大类:1.基于接口的动态代理,2.基于类的动态代理

        基于接口---jdk动态代理

        基于类:cglib

        java字节码实现:javasist

需要了解两个类:Proxy(动态代理类)和InvocationHandler(调用处理程序)

InvocationHandler

       是一个接口,有一个invoke方法

Proxy

        提供了创建动态代理类和实例的静态方法

例子:

Rent:接口

Host:房东

Handler处理动态代理

client:用户

Rent:

public interface Rent {
    //租房
    void rent();
}

Host:

package com.song.demo01;
//房东
public class Host implements Rent{
    public void rent() {
        System.out.println("房东要出租房子");
    }
    //租房

}

Hanlder:

package com.song.demo01;
//用这个类自动生成代理类

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

public class Handler implements InvocationHandler {
    private Host host;

    public void setHost(Host host) {
        this.host = host;
    }

    public Object gerProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),host.getClass().getInterfaces(),this);
    }
    //处理代理实例,返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       Object result=method.invoke(host,args);

        return result;
    }
}

client:

public class Client {
    public static void main(String[] args) {
        //生成代理类
        //真实角色
        Host host = new Host();
        //代理角色:
        Handler handler = new Handler();
        //通过调用程序处理角色   来处理我们要调用的接口对象
        handler.setHost(host);
        Rent handler1 =(Rent) handler.gerProxy();
        handler1.rent();
    }
}

万能代理类:

public class Handler implements InvocationHandler {
    private Object target;

    public void setHost(Object target) {
        this.target=target;
    }

    public Object gerProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }
    //处理代理实例,返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       Object result=method.invoke(target,args);
        return result;
    }
}

AOP(面向切面编程)

aop:面向切面编程

导包:

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.6</version>
    <scope>runtime</scope>
</dependency>
 

方式一:

使用spring的API接口实现AOP

配置文件:

 <bean id="userService" class="com.song.service.UserServiceImpl"/>
    <bean id="log" class="com.song.log.log"/>
    <bean id="afterLog" class="com.song.log.AfterLog"/>
    <!--方式一:使用原生springAPI接口-->
    <!--配置AOP:导入aop约束-->
    <aop:config>
        <!--配置切入点(在那个地方执行)expression(要执行的位置:):表达式-->
        <!--该例子中,由于方法是给系统添加日志,故Log方法要添加在所有的方法中,使用*(..)表示-->
        <aop:pointcut id="pointcut" expression="execution(* com.song.service.UserServiceImpl.*(..))"/>
        <!--执行环绕增强-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>

测试类:

public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        //动态代理代理的是接口
        UserService userService=(UserService)context.getBean("userService");
        userService.add();


    }

execution表达式

https://www.cnblogs.com/gdwkong/p/8660027.html

方式二(自定义实现,主要是切面定义):

<!--方式二:-->
    <bean id="diy" class="com.song.Diy"/>
    <aop:config>
        <!--自定义切面,ref 要引用的类-->
        <aop:aspect ref="diy">
            <!--切入点-->
            <aop:pointcut id="point" expression="execution(* com.song.service.UserServiceImpl.*(..))"/>
            <!--通知(插入方法)-->
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>

方式三:注解实现AOP

导包

<dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.6</version>
            <scope>runtime</scope>
        </dependency>

<dependency>
            <groupId>aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.5.4</version>
        </dependency>

配置文件

<!--方式三-->
    <bean id="zhujie" class="com.song.Zujie"/>
    <!--开启注解支持-->
    <aop:aspectj-autoproxy/>

类:

//使用注解实现AOP
@Aspect//标注这是一个切面
public class Zujie {
    @Before("execution(* com.song.service.UserServiceImpl.*(..))")//方法执行前
    public void  before(){
        System.out.println("前");
    }

@After("execution(* com.song.service.UserServiceImpl.*(..))")//方法执行后
    public void an(){

}
//环绕
@Around("execution(* com.song.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("环绕前");
    System.out.println(joinPoint.getSignature());//获得签名
    Object o=joinPoint.proceed();//执行方法
    System.out.println("环绕后");
    System.out.println(o);
}
}

整合Mybatis

1.导入相关jar包

  • junit
  • mybatis
  • mysql
  • spring
  • aop
  • mybatis-spring
  <dependencies>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.3.9</version>
    </dependency>
        <!-- https://mvnrepository.com/artifact/junit/junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.6</version>
            <scope>runtime</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.6</version>
        </dependency>

    </dependencies>

2.编写配置文件配置

3.测试

mybatis

1.编写实体类

2.编写核心配置文件

3.编写接口

4.编写mapper

5.测试

Mybatis-spring

导包

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.6</version>
</dependency>

1.编写数据源配置

2.sqlSessionFactory

3.sqlSessionTemplate

4.需要给接口加实现类

5.将自己写的实现类注入到spring中

xml

<?xml version="1.0" encoding="GBK"?>
<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"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    <!--DataSource:使用Spring的 数据源替换Mybatis的配置 c3p0 dbcp druid-->
    <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;serverTimezone=UTC&amp;useUnicode=true&amp;characterEncoding=UTF8"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
    <!--sqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="datasource"/>
        <!--绑定Mybatis配置文件-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/song/mapper/*.xml"/>
    </bean>
    <!--SqlSessionTemplate就是代码里的sqlSession-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!--只能使用构造器注入,因为他没有set方法-->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>

    <bean id="userMapper" class="com.song.mapper.UserMapperImpl">
        <property name="sqlsession" ref="sqlSession"/>
    </bean>
</beans>

mapper接口

public interface UserMapper {

    public List<User> selectUser();
}

mapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.song.mapper.UserMapper">
    <select id="selectUser" resultType="user">
        select * from `user`;
    </select>
</mapper>

mapperImpl

package com.song.mapper;

import com.song.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;

import java.util.List;

public class UserMapperImpl implements UserMapper{
    //以前所有操作都使用sqlsession执行,现在使用sqlsessionTemplate
    private SqlSessionTemplate sqlsession;

    public void setSqlsession(SqlSessionTemplate sqlsession) {
        this.sqlsession = sqlsession;
    }

    public List<User> selectUser() {
        UserMapper userMapper=sqlsession.getMapper(UserMapper.class);
        return userMapper.selectUser();
    }
}

第二种mapperimpl2:

public class UserMapper2 extends SqlSessionDaoSupport implements UserMapper{

    public List<User> selectUser() {
        SqlSession sqlSession = getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}

配置:

<bean id="userMapper2" class="com.song.mapper.UserMapper2">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

声明式事务

事务:

        要么都成功,要么都失败

        事务在项目开发中十分的重要,涉及到数据的一致性的问题,不能马虎

        确保完整性和一致性

事务ACID原则:

        A(原子性):确保都成功或都失败

        C(一致性):确保都成功或都失败

        I(隔离性):多个独立的事务之间相同资源不被互相影响

        D(持久性):事务完成,数据长久存在

Spring中的事务管理

声明式事务:AOP(常用)

   
    <!--配置声明事务-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <constructor-arg ref="datasource"/>
    </bean>
    <!--结合AOP进行事务的织入-->
    <!--配置事务通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--给哪些方法配置事务-->
        <tx:attributes>
            <tx:method name="add" propagation="REQUIRED"/>
            <tx:method name="delete" propagation="REQUIRED"/>
            <tx:method name="update" propagation="REQUIRED"/>
            <tx:method name="query" read-only="true"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
        
    <!--配置事务切入-->
    <aop:config>
        <aop:pointcut id="txPointCut" expression="execution(* com.song.mapper.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
     </aop:config>

编程式事务:需要在代码中声明事务的管理

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值