一,SSM整合-前后端分离(项目环境搭建)

⬅️ 上一讲: MyBatis缓存


🎉 欢迎来到本讲课程!今天我们将学习如何在SSM整合项目中进行项目环境的搭建。


在这里插入图片描述

项目介绍

SSM整合项目界面

技术栈 🛠️:

  • 🌐 前端框架: Vue
  • 🔧 后端框架: SSM (SpringMVC + Spring + MyBatis)
  • 🗄️ 数据库: MySQL
  • 📦 依赖管理: Maven
  • 📊 分页: pagehelper
  • 🛠️ 逆向工程: MyBatis Generator
  • 📋 其它

创建项目

前提: 搭建maven环境

1.创建furn-ssm项目, 这是一个maven-web项目

在这里插入图片描述

2.手动创建 javatest 目录

在这里插入图片描述

//修改编译版本

<properties>
  <project.build.sorceEncoding>UTF-8</project.build.sorceEncoding>
  <maven.compiler.source>1.8</maven.compiler.source>
  <maven.compiler.target>1.8</maven.compiler.target>
</properties>

//注销build配置, 暂时不需要, 需要时, 再打开/修改

<build>
  <finalName>furn-ssm</finalName>
</build>

3.引入项目依赖的jar包, 先引入基本的包. 开发中, 需要什么包再导入即可

<dependencies>
  <!--junit创建项目时自带的-->
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>3.8.1</version>
    <scope>test</scope>
  </dependency>
  <!--引入springmvc, 也会引入/导出spring的库/jar-->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.8</version>
  </dependency>
  <!--引入spring-jdbc, 支持事务相关-->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.8</version>
  </dependency>
  <!--引入spring aspects 切面编程需要的库/jar-->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.3.8</version>
  </dependency>
  <!--引入mybatis库/jar-->
  <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.7</version>
  </dependency>
  <!--引入druid数据库连接池-->
  <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.6</version>
  </dependency>
  <!--引入mysql驱动-->
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.49</version>
  </dependency>
</dependencies>

4.给项目配置 Tomcat, 参考 IDEA 2022.3开发JavaWeb工程

5.启动 Tomcat, 完成测试
在这里插入图片描述

项目全局配置web.xml

1.配置 src/main/webapp/WEB-INF/web.xml, 和项目全局相关.

<web-app>
    <display-name>Archetype Created Web Application</display-name>
    <!--
    1.配置启动spring容器
    2.主要配置和业务逻辑有关的. 比如数据源, 事务控制等
    -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>

    <!--
    ContextLoaderListener: 监听器
    1.ContextLoaderListener监听器作用是启动Web容器时, 自动装配ApplicationContext的配置信息
    2.它实现了ServletContextListener接口, 在web.xml配置该监听器, 启动容器时, 会默认执行它实现的方法
    -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!--
    1.配置前端控制器/中央控制器/分发控制器
    2.用户的请求都会经过它的处理
    3.因为我们没有指定springmvc的配置文件, 那么就会默认按照 servlet-name-servlet.xml 来读取
    -->
    <servlet>
        <servlet-name>springDispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--在web项目启动时, 就会自动加载DispatcherServlet-->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>springDispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
        <!--说明
        1.这里我们配置的url-pattern 是 /, 表示用户的请求都通过 DispatcherServlet
        -->
    </servlet-mapping>

    <!--配置Spring提供的过滤器, 解决中文乱码问题
    解读
    1.forceRequestEncoding 配置成true, 表示该过滤器会执行 request.setCharacterEncoding(encoding);
    2.forceResponseEncoding 配置成true, 表示该过滤器会执行 response.setCharacterEncoding(encoding);
    -->
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!--配置HiddenHttpMethodFilter
    1.作用是使用Rest风格的URI, 可以把页面发过来的post请求转为指定的delete或者put请求
    2.配置url-pattern 是 /* 表示请求都经过 hiddenHttpMethodFilter的过滤
    -->
    <filter>
        <filter-name>hiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>hiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

2.创建src/main/resources/applicationContext.xml空文件 参考

3.如果web.xml<web-app> 报红, 选择只是高亮 syntax 即可, 本身没有问题, 就是DTD的约束
在这里插入图片描述

SpringMVC配置

1.创建SpringMVC的配置文件dispatcher-servlet.xml: 主要包含网站跳转逻辑的控制.

2.创建src/main/webapp/WEB-INF/springDispatcherServlet-servlet.xml 参考

3.创建项目相关的包在这里插入图片描述

4.配置扫描com.zzw包 的控制器 springDispatcherServlet-servlet.xml
include-filter详解

<!--
1.扫描com.zzw包
2.use-default-filters="false" 表示不使用默认的 扫描机制/过滤机制
3.context:include-filter 表示只扫描com.zzw包及其子包下, 加入了@controller 注解的类
-->
<context:component-scan base-package="com.zzw" use-default-filters="false">
    <!--SpringMVC只是扫描Controller-->
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

5.配置视图解析器 springDispatcherServlet-servlet.xml

<!--配置视图解析器[默认视图解析器]-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!--配置属性prefix 和 suffix-->
    <property name="prefix" value="/WEB-INF/views/"/>
    <property name="suffix" value=".html"/>
</bean>

6.两个常规配置 springDispatcherServlet-servlet.xml

<!--加入两个常规配置-->
<!--支持SpringMVC的高级功能, 比如JSR303校验, 映射动态请求-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--将springmvc不能处理的请求, 交给tomcat处理, 比如css, js-->
<mvc:default-servlet-handler/>

7.完成测试
1)新建src/main/java/com/zzw/furn/controller/TestController.java

@Controller
public class TestController {
    @RequestMapping(value = "/hi")
    public String hi() {
        System.out.println("TestController-hi");
        return "hi";
    }
}

2)新建src/main/webapp/WEB-INF/views/hi.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>hi</title>
</head>
<body>
<h1>hi, 成功</h1>
</body>
</html>

3)启动Tomcat, 浏览器输入 http://localhost:8080/ssm/hi
默认视图解析器Debug流程
在这里插入图片描述

配置Spring和MyBatis, 并完成整合

1.1.创建Spring的配置文件 applicationContext.xml: 主要配置和业务逻辑有关的, 比如数据源, 事务控制等.

2.创建src/main/resources/applicationContext.xml 参考

3.配置扫描com.zzw包, 但是不扫描控制器, 控制器由springmvc管理, exclude-filter详解
1)一般只有include-filter才会使用 use-default-filters="false" 表示不使用默认的 扫描机制/过滤机制
2)exclude-filter 使用默认的 扫描机制/过滤机制

<!--
 1.扫描com.zzw.furn包及其子包
 2.use-default-filters="false" 表示不使用默认的 扫描机制/过滤机制
 2.表示过滤掉com.zzw.furn包及其子包下, 加入了@Controller 注解的类
-->
<context:component-scan base-package="com.zzw.furn">
    <context:exclude-filter type="annotation"
                            expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

4.创建src/main/resources/jdbc.properties, 配置连接mysql的信息

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/xxx ?useSSL=true&useUnicode=true&characterEncoding=UTF-8
jdbc.user=root
jdbc.pwd=zzw

5.配置数据源src/main/resources/applicationContext.xml

<!--引入外部的jdbc.properties文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置数据源对象-DataSource Druid数据源-->
<bean class="com.alibaba.druid.pool.DruidDataSource" id="pooledDataSource">
    <!--给数据源对象配置属性值-->
    <property name="username" value="${jdbc.user}"/>
    <property name="password" value="${jdbc.pwd}"/>
    <property name="driverClassName" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
</bean>

6.配置springmybatis的整合
1)修改pom.xml, 加入依赖

<!--引入mybatis整合spring的适配包-->
<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis-spring</artifactId>
  <version>2.0.6</version>
</dependency>

2)修改applicationContext.xml

<!--配置mybatis和spring整合
1.在项目中引入 mybatis 整合到spring的适配库/包
2.这里报红, 是因为你还没有相应的文件, 当有文件时, 就不会报红
-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
    <!--指定mybatis全局配置文件-->
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
    <!--指定数据源-->
    <property name="dataSource" ref="pooledDataSource"/>
    <!--指定mybatis的mapper文件[XxxMapper.xml]位置
    1.我们在开发中, 通常将mapper.xml放在类路径下 resources/mapper
    2.所以我们这里指定的value 是 classpath:mapper/*.xml
    -->
    <!--<property name="mapperLocations" value="classpath:mapper/*.xml"/>-->
</bean>

7.在类路径 resources 下创建 mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--目前是一个空文件-->
</configuration>

8.在类路径下创建mapper目录, 存放mapperxml文件在这里插入图片描述

9.配置将mybatis接口实现加入到ioc容器, 在applicationContext.xml配置在这里插入图片描述

    <!--
    1.配置扫描器, 将mybatis接口的实现加入到ioc容器中
    2.我们的mapper接口放在com.zzw.furn.dao
    3.mybatis就是处于DAO层, 操作DB
    -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--
        1.扫描所有的dao接口的实现, 加入到ioc容器
        2.这里dao接口, 就是mapper接口
        3.为什么要单独扫描, 因为dao接口没有@Repository注解
        -->
        <property name="basePackage" value="com.zzw.furn.dao"/>
    </bean>

10.配置事务控制, 在applicationContext.xml控制

<!--配置事务管理器-对象
1.DataSourceTransactionManager 这个对象是进行事务管理的
2.一定要配置数据源属性, 这样指定该事务管理器 是对哪个数据源进行事务控制
-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
    <property name="dataSource" ref="pooledDataSource"/>
</bean>

11.配置开启基于注解的事务(使用XML配置+切入表达式), 并指定切入点

<!--
解读
1.开启基于注解的事务, 并指定切入点
2.execution(* com.zzw.furn.service..*(..))
  表示对com.zzw.furn.service包所有类的所有方法控制事务
3.tx:advice 配置事务增强, 也就是指定事务如何切入
4.不需要背, 但是能看懂 
-->
<aop:config>
    <!--切入点表达式-->
    <aop:pointcut id="txPoint" expression="execution(* com.zzw.furn.service..*(..))"/>
    <!--配置事务增强/规则: 使用txAdvice, 指定规则对 txPoint进行切入-->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/>
</aop:config>
<!--配置事务增强(指定事务规则), 也就是指定事务如何切入-->
<tx:advice id="txAdvice">
    <!--*代表所有方法都是事务方法-->
    <tx:attributes>
        <tx:method name="*"/>
        <!--以get开始的所有方法, 我们认为是只读, 进行调优-->
        <tx:method name="get*" read-only="true"/>
    </tx:attributes>
</tx:advice>

12.完成测试: 创建src/test/java/com/zzw/test/furn/T1.java
在这里插入图片描述

public class T1 {
    @Test
    public void t1() {
        //看看spring配置的Bean是否可以获取到

        //1.获取到容器
        ApplicationContext ioc =
                new ClassPathXmlApplicationContext("applicationContext.xml");

        //2.获取数据源对象
        DruidDataSource dataSource = ioc.getBean("pooledDataSource", DruidDataSource.class);
        System.out.println("dataSource=" + dataSource);
        
        //3.获取sqlSessionFactory
        DefaultSqlSessionFactory sqlSessionFactory = ioc.getBean("sqlSessionFactory", DefaultSqlSessionFactory.class);
        System.out.println("sqlSessionFactory=" + sqlSessionFactory);
    }
}

创建表, 使用逆向工程生成Bean, XxxMapper和XxxMapper.xml

1.创建 furn_ssm 数据库 和furns

-- 创建 furn_ssm
DROP DATABASE IF EXISTS furn_ssm;
CREATE DATABASE furn_ssm;
USE furn_ssm;

-- 创建家居表
CREATE TABLE furn(
`id` INT(11) PRIMARY KEY AUTO_INCREMENT, # id
`name` VARCHAR(64) NOT NULL DEFAULT '', # 家居名
`maker` VARCHAR(64) NOT NULL DEFAULT '', # 厂商
`price` DECIMAL(11,2) NOT NULL DEFAULT 0, # 价格
`sales` INT(11) NOT NULL DEFAULT 0, # 销量
`stock` INT(11) NOT NULL DEFAULT 0, # 库存
`img_path` VARCHAR(255) NOT NULL # 图片路径
);

-- 添加数据
INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`) 
VALUES(NULL , '北欧风格小桌子' , '熊猫家居' , 180 , 666 , 7 , 'assets/images/product-image/6.jpg');
 
INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`) 
VALUES(NULL , '简约风格小椅子' , '熊猫家居' , 180 , 666 , 7 , 'assets/images/product-image/4.jpg');
 
INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`) 
VALUES(NULL , '典雅风格小台灯' , '蚂蚁家居' , 180 , 666 , 7 , 'assets/images/product-image/14.jpg');
 
INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`) 
VALUES(NULL , '温馨风格盆景架' , '蚂蚁家居' , 180 , 666 , 7 , 'assets/images/product-image/16.jpg');

-- 查询
SELECT * FROM furn;

-- 删除
DROP TABLE furn;

2.使用MyBatis Generator 逆向工程生成bean mapper接口和 mapper.xml, 当然也可以自己写
建议: 如果在开发中, 逆向工程生成的代码, 不能满足需求, 再自己编写
1)修改mybatis-config.xml, 增加 typeAliases 配置

<!--配置别名-->
<typeAliases>
    <!--
        如果一个包下有很多的类, 我们可以直接引入包
        , 这样该包下面的所有类名, 可以直接使用
    -->
    <package name="com.zzw.furn.bean"/>
</typeAliases>

2)引入MyBatis Generator 包, 在pom.xml配置

<!--引入mybatis逆向工程依赖包-->
<dependency>
  <groupId>org.mybatis.generator</groupId>
  <artifactId>mybatis-generator-core</artifactId>
  <version>1.4.0</version>
</dependency>

3)在项目根目录下创建mbg.xml, 并参考文档, 进行配置.
这里给出了一个模板xml, 在此基础上修改即可.
在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <context id="DB2Tables" targetRuntime="MyBatis3">
        <!--生成没有注释的bean-->
        <commentGenerator>
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>
        <!--配置数据库连接信息-->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/furn_ssm?characterEncoding=utf8"
                        userId="root"
                        password="zzw">
        </jdbcConnection>
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>
        <!--指定javaBean生成的位置-->
        <javaModelGenerator targetPackage="com.zzw.furn.bean" targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true"/>
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>
        <!--指定sql映射文件XML生成的位置-->
        <sqlMapGenerator targetPackage="mapper" targetProject=".\src\main\resources">
            <property name="enableSubPackages" value="true"/>
        </sqlMapGenerator>
        <!--指定dao接口生成的位置, 就是mapper接口-->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.zzw.furn.dao" targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>
        <!--指定要逆向生成的表和生成策略-->
        <table tableName="furn" domainObjectName="Furn"></table>
    </context>
</generatorConfiguration>

4)创建文件src/test/java/com/zzw/test/MBGTest.java, 生成相关bean, mapper接口和mapper.xml. 参考 官方文档 开修改, 并完成测试.
在这里插入图片描述

public class MBGTest {
    @Test
    public void generator() throws Exception, IOException {
        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        //这里要指定你自己配置的mbg.xml
        //如果这样访问, 需要将文件放在项目下
        File configFile = new File("mbg.xml");
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
        myBatisGenerator.generate(null);
    }
}

3.使用Junit测试SpringMyBatis是否整合成功, 能通过MyBatis添加furn到数据库
1)修改bean: Furn.java, 增加无参构造器, 全参构造器

2)创建src/test/java/com/zzw/test/furn/FurnMapperTest.java, 完成对furn表的crud操作

①取消applicationContext.xml的注释

<property name="mapperLocations" value="classpath:mapper/*.xml"/>

②修正jdbc.propertiesurl

jdbc.url=jdbc:mysql://localhost:3306/furn_ssm?useSSL=true&useUnicode=true&characterEncoding=UTF-8

mybatis-config.xml配置MyBatis自带的日志输出

<!--配置MyBatis自带的日志输出-查看原生的sql
    DOCTYPE规定 <settings/>节点/元素 必须在前面, 放在后面会报错
-->
<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

FurnMapperTest.java

public class FurnMapperTest {
    @Test
    public void insertSelective() {
        //1.获取到容器
        ApplicationContext ioc =
                new ClassPathXmlApplicationContext("applicationContext.xml");

        //2.获取FurnMapper
        FurnMapper furnMapper = ioc.getBean(FurnMapper.class);
        System.out.println("furnMapper--" + furnMapper.getClass());

        //添加数据
        Furn furn = new Furn(null, "你好", "你好", new BigDecimal(100), 123, 2, "image");
        int affected = furnMapper.insertSelective(furn);
        System.out.println("affected=" + affected);
    }
    @Test
    public void deleteByPrimaryKey() {
        //1.获取到容器
        ApplicationContext ioc =
                new ClassPathXmlApplicationContext("applicationContext.xml");

        //2.获取FurnMapper
        FurnMapper furnMapper = ioc.getBean(FurnMapper.class);

        int affected = furnMapper.deleteByPrimaryKey(6);
        System.out.println("affected=" + affected);
    }

    @Test
    public void updateByPrimaryKey() {
        //1.获取到容器
        ApplicationContext ioc =
                new ClassPathXmlApplicationContext("applicationContext.xml");

        //2.获取FurnMapper
        FurnMapper furnMapper = ioc.getBean(FurnMapper.class);

        Furn furn = new Furn();
        furn.setId(5);
        furn.setName("小沙发");

        //会修改所有的字段, 如果没有设置字段对应的属性值, 那么默认是空
        //int affected = furnMapper.updateByPrimaryKey(furn);

        //根据你设置属性对应字段, 生成sql语句
        int affected = furnMapper.updateByPrimaryKeySelective(furn);
        System.out.println("affected=" + affected);
    }

    @Test
    public void selectByPrimaryKey() {
        //1.获取到容器
        ApplicationContext ioc =
                new ClassPathXmlApplicationContext("applicationContext.xml");

        //2.获取FurnMapper
        FurnMapper furnMapper = ioc.getBean(FurnMapper.class);

        Furn furn = furnMapper.selectByPrimaryKey(5);
        System.out.println("furn=" + furn);
    }
}

注意事项和细节说明

1.insertSelectiveinsert的区别
1)insertSelective-选择行保存数据
比如User里面有四个字段: id, name, age, password,
但是只设置了一个字段:
User u = new User(); u.setName("张三"); insertSelective(u);
2)insertSelective执行对应的sql语句的时候, 只插入对应的name字段
insert into tb_user (id, name) values (null, "张三"); 主键是自动添加的, 默认插入为空
3)而insert则是不论设置多少字段, 统一都要添加一遍, 不论添加几个字段, 即使是一个
User u = new User(); u.setName("张三"); insert(u);
insert into tb_user (id, name, age, password) values (null, "张三", null, null);


在这里插入图片描述

🔜 下一篇预告 🔜
敬请期待:SSM项目-前后端分离(搭建Vue前端工程)


📚 目录导航 📚

  1. SSM整合-前后端分离(项目环境搭建)
  2. SSM项目-前后端分离(搭建Vue前端工程)
  3. SSM整合-前后端分离(实现增删改查)
  4. SSM整合-前后端分离(实现分页+前后端校验)

💬 读者互动 💬
在搭建SSM项目环境的过程中,你有哪些疑问或需要帮助的地方?欢迎在评论区留言,我们一起讨论。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

~ 小团子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值