-
2.EasyCode使用
-
- 2.1 下载Easy Code插件
-
2.2 创建一个SpringBoot项目
-
2.3 配置数据源
-
2.4 自定义生成模板
-
2.5 以user表为例,根据你定义的模板生成代码,文章的最后贴出我使用的自定义的模板
-
新创建一个分组Lombok,可以在生成实体类的时候使用Lombok注解
-
多个分组的切换
-
easycode使用说明推荐两篇文章
-
Java开发神器Lombok使用说明推荐两篇文章
===================================================================
mysql
mysql-connector-java
runtime
com.baomidou
mybatis-plus-boot-starter
3.4.3.1
导入上面这个增强版依赖之后,下面两个依赖无需再次导入:
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.1.4
org.springframework.boot
spring-boot-starter-jdbc
spring:
datasource: #是否使用安全连接
#mysal 8驱动不同com.mysql.cj.jdbc.Driver,还需要增加时区的配置 serverTimezone=GMT%2B8
url: jdbc:mysql://localhost:3306/tx?userSSL=false&useUnicode=true&characterEncoding=utf-8
username: root
password: 126433
driver-class-name: com.mysql.jdbc.Driver
//继承BaseMapper基本类,接口里面已经写了很多的crud方法
@Mapper
public interface UserMapper extends BaseMapper
{}
@SpringBootTest
class SpringBootDaoApplicationTests
{
//继承了BaseMapper,所有的方法都来自于父类,我们页可以编写自己的扩展方法
@Autowired
UserMapper userMapper;
@Test
void contextLoads()
{
//参数是一个Wrapper,条件构造器,这里我们先不使用 null
//查询全部用户
List users = userMapper.selectList(null);
users.forEach(System.out::println);
}
}
======================================================================================
1、@Mapper注解:
作用:在接口类上添加了@Mapper,在编译之后会生成相应的接口实现类
添加位置:接口类上面
@Mapper
public interface UserDAO {
//代码
}
如果想要每个接口都要变成实现类,那么需要在每个接口类上加上@Mapper注解,比较麻烦,解决这个问题用@MapperScan
2、@MapperScan
作用:指定要变成实现类的接口所在的包,然后包下面的所有接口在编译之后都会生成相应的实现类
添加位置:是在Springboot启动类上面添加,
@SpringBootApplication
@MapperScan(“com.winter.dao”)
public class SpringbootMybatisDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootMybatisDemoApplication.class, args);
}
}
添加@MapperScan(“com.winter.dao”)注解以后,com.winter.dao包下面的接口类,在编译之后都会生成相应的实现类
===================================================================
#开启日志功能
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #控制台输出
=========================================================================
这个注解表示表的主键
@NoArgsConstructor
@AllArgsConstructor
@Data
public class User
{
String name;
Integer age;
@TableId(value=“id”,type = IdType.AUTO)
Integer id;
}
接受两个参数
value = 主键列名
type = 主键类型
可选类型:
**使用@TableId注解时,类型为: type = IdType.AUTO 数据库也需要设置自增
否则会出 Cause: java.sql.SQLException: Field ‘id’ doesn’t have a default value**
===================================================================
//测试插入
@Test
public void testInsert(){
User user = new User();
user.setName(“淮城一只猫”);
user.setAge(5);
user.setEmail(“2424496907@qq.com”);
int result = userMapper.insert(user); //自动生成id
System.out.println(result); //受影响的行数
System.out.println(user); //发现id自动回填
}
=====================================================================
默认使用ID_WORKER,全局唯一id----雪花算法
snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。可以保证几乎全球唯一!
我们需要配置注解自增:
-
实体类字段上:@TableId(type = IdType.AUTUO)
-
数据库字段一定要自增
public enum IdType {
AUTO(0), //id自增
NONE(1), //未设置主键
INPUT(2), //手动输入,一旦手动输入后,就需要自己去写id,否则id为空
ID_WORKER(3), //默认值,全局唯一id
UUID(4), //全局唯一id,uuid
ID_WORKER_STR(5); //ID_WORKER的字符串表示法
}
===================================================================
//测试更新
@Test
public void testUpdate(){
User user = new User();
user.setId(6L);
user.setName(“我的博客叫:淮城一只猫”);
user.setAge(6);
user.setEmail(“2424496907@qq.com”);
//注意:updateById参数是一个对象
int result = userMapper.updateById(user); //自动生成id
System.out.println(result); //受影响的行数
}
===================================================================
创建时间、修改时间!这些操作一般自动化完成的,我们不希望手动更新!
阿里巴巴开发手册:所有的数据库表:gmt_create、gmt_modified几乎所有表都要配置上!而且需要自动化!
1.在表中新增字段create_time、update_time
2. 再次测试插入方法,需要先把实体类同步!
private Date creteTime;
private Date updateTime;
MySQL-datetime类型的列设置默认值为CURRENT_TIMESTAMP
MySQL中datetime字段的默认值CURRENT_TIMESTAMP
1.删除数据库中的默认值、更新操作
2.在实体类字段属性上需要注释
//字段必须声明TableField注解,属性fill选择对应策略,该声明告知Mybatis-Plus需要预留注入SQL字段
// 注意!这里需要标记为填充字段
@TableField(fill = FieldFill.INSERT)
private Date creteTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
策略枚举如下:
public enum FieldFill {
/**
- 默认不处理
*/
DEFAULT,
/**
- 插入填充字段
*/
INSERT,
/**
- 更新填充字段
*/
UPDATE,
/**
- 插入和更新填充字段
*/
INSERT_UPDATE
}
3.自定义实现类 MyMetaObjectHandler(实现元对象处理器接口)
@Slf4j
@Component//填充处理器MyMetaObjectHandler在 Spring Boot 中需要声明@Component或@Bean注入
public class MyMetaObjectHandler implements MetaObjectHandler
{
//插入时填充策略
@Override
public void insertFill(MetaObject metaObject) {
log.info(“start insert fill …”);
this.setFieldValByName(“createTime”,new Date(),metaObject);
this.setFieldValByName(“updateTime”,new Date(),metaObject);
}
//更新时填充策略
@Override
public void updateFill(MetaObject metaObject) {
log.info(“start update fill …”);
this.setFieldValByName(“updateTime”,new Date(),metaObject);
}
}
4.测试
@SpringBootTest
class SpringBootDaoApplicationTests
{
//继承了BaseMapper,所有的方法都来自于父类,我们页可以编写自己的扩展方法
@Autowired
UserMapper userMapper;
@Test
void contextLoads()
{
User user=new User();
user.setAge(18);
user.setName(“hhhh”);
int ret = userMapper.insert(user);
System.out.println(user);
}
}
==================================================================
乐观锁:顾名思义乐观,它总是认为不会出现问题,无论干什么都不去上锁!如果出现问题,再次更新值测试
悲观锁:顾名思义悲观,它总是认为会出现问题,无论干什么都会加上锁!再去操作
乐观锁实现方式:
-
取出记录时,获取当前version
-
更新时,带上这个version
-
执行更新时, set version = newVersion where version = oldVersion
-
如果version不对,就更新失败
1.数据库中添加version字段,默认值为1
2.在实体类的字段上加上@Version注解
@Version //乐观锁注解
private Integer version;
说明:
-
支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
-
整数类型下 newVersion = oldVersion + 1
-
newVersion 会回写到 entity 中
-
仅支持 updateById(id) 与 update(entity, wrapper) 方法
-
在 update(entity, wrapper) 方法下, wrapper 不能复用!!!
3.配置插件
@MapperScan(“com.Mapper”)
@EnableTransactionManagement //自动管理事务(默认也是开启的)
@Configuration //配置类
public class MybaitsPlusConfig {
//注册乐观锁插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mybatisPlusInterceptor;
}
}
4.测试
@SpringBootTest
class SpringBootDaoApplicationTests
{
//继承了BaseMapper,所有的方法都来自于父类,我们页可以编写自己的扩展方法
@Autowired
UserMapper userMapper;
//测试乐观锁成功!
@Test
public void testOptimisticLocker1() {
//1.查询用户信息
User user = userMapper.selectById(276);
//2.修改用户信息
user.setName(“aaa”);
user.setAge(18);
//3.执行更新操作
userMapper.updateById(user);
}
//测试乐观锁失败!多线程下
@Test
public void testOptimisticLocker2() {
//线程1
User user1 = userMapper.selectById(276);
user1.setName(“aaa”);
//模拟另外一个线程执行插队操作
User user2 = userMapper.selectById(276);
user2.setName(“bbb”);
userMapper.updateById(user2);
//自旋锁多次操作尝试提交
userMapper.updateById(user1);
}
}
===================================================================
@SpringBootTest
class SpringBootDaoApplicationTests
{
//继承了BaseMapper,所有的方法都来自于父类,我们页可以编写自己的扩展方法
@Autowired
UserMapper userMapper;
//测试查询
@Test
public void testSelectById() {
User user = userMapper.selectById(276);
System.out.println(“===========================”);
System.out.println(user);
}
//测试批量查询
@Test
public void testSelectByBatchId()
{
List users = userMapper.selectBatchIds(Arrays.asList(270, 271, 272));
System.out.println(“===========================”);
users.forEach(System.out::println);
}
//条件查询之一 使用map操作
@Test
public void testSelectBatchIds() {
HashMap<String, Object> map = new HashMap<>();
//自定义查询,同时满足map集合中的所有条件
map.put(“name”,“bbb”);
map.put(“age”,“18”);
List users = userMapper.selectByMap(map);
System.out.println(“===========================”);
users.forEach(System.out::println);
}
}
===================================================================
分页在网站中使用非常多!
-
原始limit进行分页
-
pageHelper第三方插件
-
MP其实也内置了分页插件
//Spring boot方式
@Configuration
@MapperScan(“com.baomidou.cloud.service..mapper”)
public class MybatisPlusConfig {
// 旧版
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false
// paginationInterceptor.setOverflow(false);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
// paginationInterceptor.setLimit(500);
// 开启 count 的 join 优化,只针对部分 left join
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
return paginationInterceptor;
}
// 最新版
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
return interceptor;
}
}
-
setMaxLimit: 设置最大分页数
-
setOverflow:是否对超过最大分页时做溢出处理,默认false不处理,即直接丢弃溢出的数据,为true时,怎么把溢出数据放入到下一个分页中。
-
setDbType:设置数据库类型以匹配不同方言
-
setDialect: 设置分页方言
-
setOptimizeJoin:优化Join分页语句,默认true
//测试分页查询
@Test
public void testPage() {
//参数一:当前页
//参数二:页面大小
//使用了分页插件之后,所有的分页操作页变得简单了
Page page = new Page<>(1,5);
//第二个参数是查询条件
userMapper.selectPage(page,null);
page.getRecords().forEach(System.out::println);
System.out.println(page.getTotal());
}
分页查询常用的方法:
@Test
public void testPage() {
Page page = new Page<>(1,1);
List emps =
employeeMapper.selectPage(page, null);
System.out.println(emps);
System.out.println(“=获取分页相关的一些信息========”);
System.out.println(“总条数:” +page.getTotal());
System.out.println("当前页码: "+ page.getCurrent());
System.out.println(“总页码:” + page.getPages());
System.out.println(“每页显示的条数:” + page.getSize());
System.out.println("是否有上一页: " + page.hasPrevious());
System.out.println("是否有下一页: " + page.hasNext());
//将查询的结果封装到page对象中
page.setRecords(emps);
}
如果返回类型是IPage,那么分页返回的IPage对象与传入IPage的对象是同一个
好用的工具===》Arrays用法总结
===================================================================
//通过id删除
@Test
public void testDeleteById() {
userMapper.deleteById(174);
}
//通过id批量删除
@Test
public void testDeleteBatchId() {
userMapper.deleteBatchIds(Arrays.asList(177, 178));
}
//通过map删除
@Test
public void testDeleteMap() {
HashMap<String, Object> map = new HashMap<>();
map.put(“name”, “大忽悠1号”);
userMapper.deleteByMap(map);
}
物理删除:从数据库中直接移除
逻辑删除:在数据库中没有被移除,而是通过一个变量来让他失效!delete=0 => delete=1
管理员可以查看被删除的记录!防止数据的丢失,类似于回收站!
说明
这里mybaitsPlus提供的逻辑删除功能,只对其默认提供给我们的增删改查的slq语句生效,如果是我们自己写的,则不会拥有其功能
只对自动注入的sql起效:
插入: 不作限制
查找: 追加where条件过滤掉已删除数据,且使用 wrapper.entity 生成的where条件会忽略该字段
更新: 追加where条件防止更新到已删除数据,且使用 wrapper.entity 生成的where条件会忽略该字段
删除: 转变为 更新
例如:
删除: update user set deleted=1 where id = 1 and deleted=0 (如果走了自动注入sql的删除操作,那么其实执行的是更新操作,即将deleted值变为1,即逻辑删除掉了)
查找: select id,name,deleted from user where deleted=0 (查找的时候只会查找出没有被逻辑删除掉的用户)
字段类型支持说明:
支持所有数据类型(推荐使用 Integer,Boolean,LocalDateTime)
如果数据库字段使用datetime,逻辑未删除值和已删除值支持配置为字符串null,另一个值支持配置为函数来获取值如now()
附录:
逻辑删除是为了方便数据恢复和保护数据本身价值等等的一种方案,但实际就是删除。
如果你需要频繁查出来看就不应使用逻辑删除,而是以一个状态去表示。
测试一下:
1.在数据表中增加deleted字段
2. 实体类中同步属性
//逻辑删除字段
@TableLogic
private Integer deleted;
3. 配置
mybatis-plus:
global-config:
db-config:
logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
4.测试
@SpringBootTest
class SpringBootDaoApplicationTests
{
//继承了BaseMapper,所有的方法都来自于父类,我们页可以编写自己的扩展方法
@Autowired
UserMapper userMapper;
//通过id删除
@Test
public void testDeleteById()
{
userMapper.deleteById(174);
}
}
===============================================================================
我们在开发中,会遇到一些慢sql,我们有必要把它揪出来 。测试!druid…
MP也提供性能分析插件,如果超过这个时间就停止运行!官方3.1.0以上版本推荐使用p6spy!
P6Spy 是一个可以用来在应用程序中拦截和修改数据操作语句的开源框架。 通过 P6Spy 我们可以对 SQL 语句进行拦截,相当于一个 SQL 语句的记录器,这样我们可以用它来作相关的分析,比如性能分析。
注意:该插件有性能损耗,建议平时开发测试时使用,不建议生产环境使用。
p6spy
p6spy
3.9.0
2.接着编辑 application.properties 文件,更换数据库连接驱动
P6Spy 工作原理:P6Spy 通过劫持 JDBC 驱动,在调用实际 JDBC 驱动前拦截调用的目标语,达到 SQL 语句日志记录的目的。
spring:
datasource: #是否使用安全连接
#mysal 8驱动不同com.mysql.cj.jdbc.Driver,还需要增加时区的配置 serverTimezone=GMT%2B8
url: jdbc:p6spy:mysql://localhost:3306/tx?userSSL=false&useUnicode=true&characterEncoding=utf-8
username: root
password: 123456
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
profiles:
active: dev #激活开发环境
3.最后创建 spy.properties 配置文件即可:
#3.2.1以上使用
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
#3.2.1以下使用或者不配置
#modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
使用日志系统记录 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
设置 p6spy driver 代理
deregisterdrivers=true
取消JDBC URL前缀
useprefix=true
配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
日期格式gui
dateformat=yyyy-MM-dd HH:mm:ss
实际驱动可多个
#driverlist=org.h2.Driver
是否开启慢SQL记录
outagedetection=true
慢SQL记录标准 2 秒
outagedetectioninterval=2
4.测试
@SpringBootTest
class SpringBootDaoApplicationTests
{
//继承了BaseMapper,所有的方法都来自于父类,我们页可以编写自己的扩展方法
@Autowired
UserMapper userMapper;
//通过id删除
@Test
@Profile({“dev”,“test”})
public void testDeleteById()
{
User user = userMapper.getUserId(174);
System.out.println(user);
}
}
注意,插件会影响性能,建议开发和测试环境下使用
@Profile注解详解
1. 上面的样例配置,我们是直接将日志输出到控制台,我们可以修改 appender 配置属性,使用日志系统记录 sql:
2. 再次执行查询,可以看到现在改用日志系统记录 sql 了:
也可以修改 appender 配置属性,将日志输出到文件中:
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
![img](https://i-blog.csdnimg.cn/blog_migrate/c43a51a8ef7c1e5892f6943d01913916.jpeg)
最后
既已说到spring cloud alibaba,那对于整个微服务架构,如果想要进一步地向上提升自己,到底应该掌握哪些核心技能呢?
就个人而言,对于整个微服务架构,像RPC、Dubbo、Spring Boot、Spring Cloud Alibaba、Docker、kubernetes、Spring Cloud Netflix、Service Mesh等这些都是最最核心的知识,架构师必经之路!下图,是自绘的微服务架构路线体系大纲,如果有还不知道自己该掌握些啥技术的朋友,可根据小编手绘的大纲进行一个参考。
如果觉得图片不够清晰,也可来找小编分享原件的xmind文档!
且除此份微服务体系大纲外,我也有整理与其每个专题核心知识点对应的最强学习笔记:
-
出神入化——SpringCloudAlibaba.pdf
-
SpringCloud微服务架构笔记(一).pdf
-
SpringCloud微服务架构笔记(二).pdf
-
SpringCloud微服务架构笔记(三).pdf
-
SpringCloud微服务架构笔记(四).pdf
-
Dubbo框架RPC实现原理.pdf
-
Dubbo最新全面深度解读.pdf
-
Spring Boot学习教程.pdf
-
SpringBoo核心宝典.pdf
-
第一本Docker书-完整版.pdf
-
使用SpringCloud和Docker实战微服务.pdf
-
K8S(kubernetes)学习指南.pdf
另外,如果不知道从何下手开始学习呢,小编这边也有对每个微服务的核心知识点手绘了其对应的知识架构体系大纲,不过全是导出的xmind文件,全部的源文件也都在此!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
也可以修改 appender 配置属性,将日志输出到文件中:
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。[外链图片转存中…(img-GbuNKxk8-1713467507450)]
[外链图片转存中…(img-b96cnmGj-1713467507451)]
[外链图片转存中…(img-ggRCJuxV-1713467507451)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
![img](https://i-blog.csdnimg.cn/blog_migrate/c43a51a8ef7c1e5892f6943d01913916.jpeg)
最后
既已说到spring cloud alibaba,那对于整个微服务架构,如果想要进一步地向上提升自己,到底应该掌握哪些核心技能呢?
就个人而言,对于整个微服务架构,像RPC、Dubbo、Spring Boot、Spring Cloud Alibaba、Docker、kubernetes、Spring Cloud Netflix、Service Mesh等这些都是最最核心的知识,架构师必经之路!下图,是自绘的微服务架构路线体系大纲,如果有还不知道自己该掌握些啥技术的朋友,可根据小编手绘的大纲进行一个参考。
[外链图片转存中…(img-Xf5f63Pn-1713467507452)]
如果觉得图片不够清晰,也可来找小编分享原件的xmind文档!
且除此份微服务体系大纲外,我也有整理与其每个专题核心知识点对应的最强学习笔记:
-
出神入化——SpringCloudAlibaba.pdf
-
SpringCloud微服务架构笔记(一).pdf
-
SpringCloud微服务架构笔记(二).pdf
-
SpringCloud微服务架构笔记(三).pdf
-
SpringCloud微服务架构笔记(四).pdf
-
Dubbo框架RPC实现原理.pdf
-
Dubbo最新全面深度解读.pdf
-
Spring Boot学习教程.pdf
-
SpringBoo核心宝典.pdf
-
第一本Docker书-完整版.pdf
-
使用SpringCloud和Docker实战微服务.pdf
-
K8S(kubernetes)学习指南.pdf
[外链图片转存中…(img-FQpb05bO-1713467507452)]
另外,如果不知道从何下手开始学习呢,小编这边也有对每个微服务的核心知识点手绘了其对应的知识架构体系大纲,不过全是导出的xmind文件,全部的源文件也都在此!
[外链图片转存中…(img-5UDbuC4M-1713467507452)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!