Git
本章节我们来学习Git,因为Git是程序员的必备管理工具,因此学习SpringBoot之前,我们先来学习他
这里我们学习最重要的就是最后一点,就是使用idea来操作git
Git作用
我们Git的作用有以下四点
- 备份
- 代码还原
- 协同开发
- 追溯问题代码的编写人和编写时间
其之所以能够实现这些,是通过版本控制达到的,简单来说就是每个人写一份都添加一个版本号。
我们有两种版本控制器的方式,一种是集中式版本控制工具,比如说SVN和CVS,其原理是将代码都放到一个中心服务器中,然后程序员需要就往中心服务器里拿,但是这样的话,如果哪一天我们的中心服务器突然寄了就全完了,断网了也是全完了,不方便。
而我们的Git则是分布式版本控制工具,其没有什么中央服务器,每个人的电脑上都是完整的版本库,无需联网,多人协作时只要将各自的修改推送给对方就完了,任何两个人之间都可以互相推送自己的代码版本,不过虽然可以这样做,不过一般我们都还是有一个共享版本库来控制的。
最后我们来看看Git的特点
git工作流程简述
简单来说,git有远程仓库和本地仓库的概念,其他的我们先了解下就完了
git环境的配置与安装
接着我们来学习git环境的配置和安装,首先我们要提一嘴,我们安装过程中是会用到基本的Linux的命令的,因此我们提前将其列举了,请看下图
首先是安装,安装其实就是傻瓜式安装,我们一直安装就完了,如果安装成功了我们右键点击桌面可以看到Git GUI和Git Bash,前者是Git提供的图形界面工具,而后者是命令行工具
接着我们就到了经典的配置环节了,首先我们要配置我们的用户名和邮箱,因为Git每次提交都会使用该用户的信息,因此先将这个两个信息配置好非常重要。我们来看看我们设置的步骤
- 打开Git Bash
- 设置用户信息
git confifig --global user.name “Rolin”
git confifig --global user.email “2259286198@qq.com”
查看配置信息
git confifig --global user.name
git confifig --global user.email
这里值得一提的是,我们这里设置要使用的双引号是中文的双引号,而不是英文的,这点和我们之前的习惯背道而驰。同时,我们直接在里面打这些对应的命令总能出各种稀奇古怪的异常,我这里推荐的方法是在文本中先打好自己的命令代码,然后拷贝过去进行一个执行就完了
接着我们来整一个本地仓库,要使用git必须要搞这一步。我们在任意位置创建一个我们要将其作为仓库的文件夹,然后进入该文件夹中右键打开命令行工具,执行命令git init,然后在对应的仓库中看到其生成了一个隐藏文件夹.git,就说明此时我们的本地仓库已经设置成功了
git常用命令
接着我们就来讲讲git的常用命令,在讲解之前,我们要先来学习Git的工作状态。
首先我们要理解工作区的概念,除了我们的.git文件夹之外的文件都数去工作区。然后如果我们新创建一个文件,那么其是处于未跟踪状态的,我们可以用git add命令令其进入暂存区并处于已暂存状态,我们的暂存区是仓库之前的缓存区,我们可以再次调用git commit方法令其进入仓库中
而对于修改文件来说也是大同小异,如果我们修改文件,那么其首先处于工作区中且是未暂存状态,同样调用对应的方法能够令其进入暂存区并最终到达仓库中
git案例实现
那么接着我们就来做一个简单的案例,首先我们进入对应的仓库中然后创建一个文档文件,我们可以调用命令touch file01.txt,然后我们来查看我们的文件的状态,这需要调用git status,我们可以看到如下内容
$ git status
On branch master
No commits yet
Untracked files:
(use "git add ..." to include in what will be committed)
file01.txt
我们可以看到这里显示了一个未跟踪的文件,在里面可以看到我们的file01.txt,这就是我们的未跟踪的文件。
然后我们就令其进入我们的缓存区里工作,我们调用对应的git add .命令,这里的.意为所有,我们将所有未跟踪的文件都进入到暂存区中,实际上我们在后面输入对应的文件的名字也是可以的,我们这里贪方便直接令其提交所有了。然后我们再次调用查询状态的命令,可以看到如下内容
$ git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached ..." to unstage)
new file: file01.txt
可以看到我们这里显示的内容是准备提交的文件,里面就有我们的file01.txt,然后我们输入git commit -m "add file01",注意我们这里要输入-m的选项。而后面的双引号的内容纯粹是注释,便于我们后面查看所加入的,爱写什么写什么
然后我们调用git log方法
$ git log
commit 6631dc6073db65619138904ac0304ca47f86cc36 (HEAD -> master)
Author: “Rolin” “2259286198@qq.com”
Date: Mon May 16 15:29:10 2022 +0800
add file01
我们通过这个方法可以看到我们仓库内存放的内容的具体记录,其中commit是唯一标识,Author则是对应的我们的ID和邮箱,Date是提交的时间,最后是我们当初写入的注释
然后我们来讲解下我们的修改的情况,首先我们写入命令vi file01.txt,我们进入到对应的编辑位置中然后随便编辑点啥,接着点击保存,然后同样查看状态能看到如下内容
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: file01.txt
no changes added to commit (use "git add" and/or "git commit -a")
上面的内容就表示我们的改变了,但是还没有暂存的文件。然后我们同样进行原来的两个步骤,就可以在我们的git log中看到两个我们的历史文件了。
git-log讲解
最后我们来单独讲解下git-log这个参数,其下有很多选项,其中--all是显示所有分支,--graph是将信息以图的形式来显示,更多的命令及其作用请看下图
接下来我们一个个来讲解这些命令的具体的选项的作用,首先我们进入一个别人的开源项目里康康,我们使用git log,我们可以看到里面有一大堆仓库的提交记录,这他妈要一个个找就太折磨人了,我们希望其全部都显示到一行中,此时我们可以使用git log --pretty=oneline命令,让所有的提交信息显示为一行,但即使如此,我们的得到的信息展示页面还是太他妈乱了,这里最乱的是就是我们每次提交的唯一标识,它太长了主要,实际上,我们每次使用唯一标识的时候我们没必要每次都拿到其全部的长度,拿到其前五位的就可以了,一般也不会有重复。此时我们可以写入git log --pretty=oneline --abbrev-commit,abbrev代表优化,而后面的commit代表我们要优化的位置,这样我们在界面中得到的唯一标识就简短不多了,我们的页面也变得清爽不少
我们还可以在后面加入显示所有分支的--all,这样令其同时显示所有分支,其命令如下git log --pretty=oneline --abbrev-commit --all,其中分支以红色显示,当然分支是什么我们先按下不表。但是这时只是单纯的显示分支就不够直观,我们希望我们看到其合并分支时具体是哪些进行了合并并成为了一个分支,那么我们可以在后面再加上以图的形式来显示的--graph命令,其命令如下git log --pretty=oneline --abbrev-commit --all --graph,这样最后我们的得到的页面就会直观不少了。
我们以后查看仓库,我们都推荐用上面的命令,然而我们每次都输入这个玩意显然不太方便,因此我们要进行取别名的操作,先进入到我们的用户的文件夹中,然后创建.bashrc的文件,在内部写入代码如下
#用于输出git提交日志
alias git-log='git log --pretty=oneline --all --graph --abbrev-commit'
#用于输出当前目录所有文件及基本信息
alias ll='ls -al'
其代表的意思就是取别名,这里我们取了两个别名,一个是将git log --pretty=oneline --all --graph --abbrev-commit命令取为git-log,另一个也是如法炮制。然后这时如果我们想要获得更好的页面展示效果的话,我们就只需要输入git-log命令就可以了
版本回退
我们要使用版本回退就要使用git reset --hard commitID命令,其中commitID指的是我们的上传的内容或者是某一个操作的唯一标识,举个简单的例子,我们可以让我们的仓库的内容回到第一次添加的时候,那么我们的仓库就会回到那会,那时我们连修改都没有做。我们也可以再回退回来,只需要输入对应的修改之后的commitID就行了,这个东西我们可以通过之前我们的git log记录中查看到,但可能有一种情况,我们不但回退了,我们还将我们的屏幕给情况了,那我们此时应该怎么办才能将我们的仓库恢复到原来的样子呢?我们可以调用git reflog方法,调用该方法可以查看到我们的已经删除的提交记录,我们利用这个命令可以查看到我们回溯之前的状态的唯一标识,并利用这个唯一标识将我们的仓库恢复到那时的状态
添加文件到忽略列表
最后我们来说下我们如何添加文件到我们的忽略列表中,我们有这么一种需求,就是我们希望对大部分的文件进行同样的处理,但是我们同时又希望忽略掉一些文件,此时就需要使用到忽略列表
我们首先在工作目录中创建一个名为.gitignore的文件,然后写入对应的要忽略的文件名,如实*.a代表的就是忽略所有.a格式的文件,创建完毕并输入完毕之后,我们只要调用对应的执行所有的方法,我们就会发现这些方法自动都自动忽略了我们写入进忽略列表里的文件了。如果我们希望该忽略列表失去效果的话,给其改名或者是删除应该都是可以的。
最后我们再来做一套练习,具体请看下面的内容
#####################仓库初始化######################
创建目录(git_test01)并在目录下打开gitbash
略
初始化git仓库
git init
#####################创建文件并提交#####################
目录下创建文件 file01.txt
略
将修改加入暂存区
git add .
将修改提交到本地仓库,提交记录内容为:commit 001
git commit -m 'commit 001'
查看日志
git log
####################修改文件并提交######################
修改file01的内容为:count=1
略
将修改加入暂存区
git add .
# 将修改提交到本地仓库,提交记录内容为:update file01
git commit --m 'update file01'
查看日志
git log
以精简的方式显示提交记录
git-log
####################将最后一次修改还原##################
查看提交记录
git-log
找到倒数第2次提交的commitID
略
版本回退
git reset commitID --hard
Git分支
接着我们来学习Git里面的一个重要内容,分支。首先我们来介绍下什么是分支,我们之前说过协同开发,但是难道我们的协同开发到底是怎么实现的呢?其实我们一般是不动我们的主线开发,而是让不同的人开发不同的分支,然后最后确定功能没有问题了,我们再来将分支上的代码合并到我们的主线上。
所以分支简单来说就是你可以把你的工作从开发主线上分离开来进行重大的Bug修改、开发新的功能,以免影响开发主线。接着我们来讲讲我们的关于分支的常用方法
首先我们先来进行一个分支的页面的科普,我们输入git-log,可以看到如下内容
我们这里可以在最后我们的括号内看到master,这个master就代表的是当前分支,如果我们进入了其他分支,那么该名字也会正确转换为其他分支的名字,一般来说,我们的master是作为主支来使用的。其次我们可以在下面看到HEAD和是一个箭头,其代表的就是指向当前的使用的支点,目前我们使用的支点是master,因此其指向master
git分支中的常用方法
接着我们来正式讲分支中的常用方法,具体请看下图
其中我们最重要的要学习的命令就是创建分支和合并分支,这里我们值得一提的是,如果我在主支上创建了一个文件,又在分支上创建了一个文件,那么这两个分支上在仓库上是否显示与否取决于我们是否切换到了对应的分支,否则其就不显示(但主支上原来就存在的文件会在分支上同样创建一次然后能在分支中查看到同名的文件,但是两者的修改却是不一样的)。
如果我们都希望其能在一个分支上进行显示的话,就要进行分支的合并,一般我们都是将我们的其他分支加到我们的master分支上,一般我们是切换到master分支中,然后写入git merge 分支名称,这样的命令,这样就可以将我们的其他分支合并到当前分支中了,我们调用这个命令其会进入到一个提示页面,这个页面类似于vim,我们先按下不表,直接按shift+冒号,然后输入wq就完了
那么合并之后我们查看其提交记录的显示样式如下
我们可以看到我们执行的合并操作,并且其下合并的分支就有dev01
解决冲突
接着我们来学习解决冲突,什么是冲突呢?简单来说,就是当两个分支上都对同一个文件的同一行进行了修改之后,又对其进行了合并,那git根本就不知道你到底要用哪个,此时就会出现冲突,git往往会将这个冲突交给程序员自己来解决,他自己只负责合并,我们具体来看看其解决冲突的过程
当我们出现冲突后,其会提示合并失败,并提示出现冲突的位置,然后我们进入到冲突位置的文件中查看,可以看到其中已经有了git编辑器给我们写上的冲突位置的标记,HEAD到======代表的是我们当前的分支的代码,而======到后面的分支代表是我们要合并的分支上的代码,两者的差异就代表其冲突的位置,我们修改的方式也很简答,直接将对应的提示代码全删掉,然后将该行改为我们所需要的具体内容就可以了。
之后我们通过同样的添加和提交命令,其会自动识别出我们正在进行合并的动作,在提交完成后会将我们的文件的分支进行一个成功合并。同时如果我们的合并提交时没有使用-m命令,其会提示一个窗口,也类似于文本编辑器,我们直接wq就完了
注意我们这里的冲突一般都是同一行的冲突,如果不是同一行的那他就自动进行一个合并了
分支使用流程
接着我们来学习我们的分支开发的规范,实际上我们开发肯定不可能跟我们这里一样瞎几把生成分支是吧。我们先来讲一种最普遍的分支的使用情况,请看下图
首先我们的主支上有一个develop分支,develop分支下有许多的feature分支,develop分支主要用于进行阶段开发功能,开发完成之后我们会将这些功能合并到我们的主支上发布,开发功能当然不是在develop自身上开发的,而是在其下的feature分支上开发的,在该分支上不但开发功能,而且会经过测试,测试通过后就合成到develop分支上,然后为了节省空间,我们的feature分支就可以删除了。
在我们的主支上还有release分支,其不但可以用来标记每次上先的时间点,还可以从中生成新的子支用于bug修复,修复完的版本进行了开发之后要经过测试,测试完成之后就和主支合并,该动作意为bug修复,然后再将其合并到develop中,代表我们的新功能也在我们的develop中了,另外我们一般是不做主支里往分支里合并这种操作的,所以我们总是要从其他的用于bug修复的子支合并到develop上
当然实际的开发中还有一些其他的分支,比如用于测试的分支,或者是pre预上线分支等,不过这些分支是根据我们不同公司的情况自己设置的,我们这里就不赘述了
最后我们来做一些对应的练习
###########################创建并切换到dev01分支,在dev01分支提交
[master]创建分支dev01
git branch dev01
[master]切换到dev01
git checkout dev01
[dev01]创建文件file02.txt
略
[dev01]将修改加入暂存区并提交到仓库,提交记录内容为:add file02 on dev
git add .
git commit -m 'add file02 on dev'
[dev01]以精简的方式显示提交记录
git-log
###########################切换到master分支,将dev01合并到master分支
[dev01]切换到master分支
git checkout master
[master]合并dev01到master分支
git merge dev01
[master]以精简的方式显示提交记录
git-log
[master]查看文件变化(目录下也出现了file02.txt)
略
##########################删除dev01分支
[master]删除dev01分支
git branch -d dev01
[master]以精简的方式显示提交记录
git-log
强制删除分支
接着我们来讲下我们需要强制删除分支的场景,这个使用场景我们了解下就好了,并不做强制要求。最经典的应用场景就是我们新创建了一个分支并写入了信息,然后提交,接着我们回到主支上删除这个分支(注意分支不能自己删除自己,因此要删除分支必须到其他分支中),此时普通删除就会提示你还没有合并这个分支而无法删除,因为git认为你这个分支创建出来但是又什么都还没有干,所以你这个很可能是误操作,因此提供这个信息,相当于是要你确定删除这个分支,此时如果我们一定要删除这个分支,我们就直接用-D的选项调用命令来删除这个分支就可以了。其命令是git branch -D 分支名
最后我们可以来做一个小结
合并的快进模式
最后我们再补充一个内容,我们先来说这么一种场景,假如我们我们在主干上创建了一个分支,然后切换到分支上创建了一个文件,此时我们将分支提交,我们此时查看我们的提交记录会发现,从上往下,第一个提交记录是我们的分支的提交记录,第二个记录是我们的原来的主干的提交记录,此时我们的第一个提交和第二个提交只差了只是第一个提交的内容有变动而已,此时如果我们再执行合并操作的话,git会智能的识别到我们要做的事,此时就会启动合并的快进模式,直接将主干的位置移动到分支上,此时的结构就不会有原来的那种树型结构了,而是在主干上创建了一个分支的结构,虽然结构不一样,但是他们的作用是一样的
合并之后的结构图如下所示
当我们的一个分支做了修改另一个没有做且要对这两个分支进行合并时git会自动启动合并的快进模式,反过来,如果我们的两个分支都做了修改,此时要合并,那就只能使用普通方式来进行合并了
仓库托管
我们之前说过我们的git除了有本地仓库之外还有远程仓库,我们一般现在已经把本地仓库的内容给讲完了,接着我们来讲远程仓库的内容。
首先,我们的远程仓库有以下三种
前面我们已经知道了Git中存在两种类型的仓库,即本地仓库和远程仓库。那么我们如何搭建Git远程仓库
呢?我们可以借助互联网上提供的一些代码托管服务来实现,其中比较常用的有GitHub、码云、GitLab等。
gitHub(
地址:github.com/ )是一个面向开源及私有软件项目的托管平台,因为只支持
Git 作为唯一的版本库格式进行托管,故名gitHub
码云(地址: gitee.com/ )是国内的一个代码托管平台,由于服务器在国内,所以相比于
GitHub,码云速度会更快
GitLab (地址: about.gitlab.com/ )是一个用于仓库管理系统的开源项目,使用Git作
为代码管理工具,并在此基础上搭建起来的web服务,一般用于在企业、学校等内部网络搭建git私服。
我们这里使用码云来作为我们的远程仓库,因为码云是国内的一个平台,速度比较快。
首先我们进行一个码云的注册,注册过程就不谈了,注册完之后我们创建仓库
创建完之后我们要将我们的仓库推到我们的git中,我们应该要怎么做呢?一种通用的方式就是使用SSH公钥来推送,首先我们按照下面的步骤在我们的git窗口中配置我们的公钥,接着获取公钥并复制
然后我们将ssh的公钥的内容全部赋值下来,然后我们进入码云的个人首页的设置页面中,选择SSH公钥,接着复制进去就完了,上面的名字随便写
然后我们在在git窗口中输入,ssh -T git@gitee.com,然后输入yes(第一次访问要输入yes),然后就能看到对应的欢迎文本了,此时就说明我们的ssh已经配置成功了,我们已经可以连接到码云中了
远程仓库添加
接着我们就来正式添加我们的远程仓库,要添加远程仓库,首先我们要进入我们码云中的设置,复制其中的SSH,然后我们调用命令 git remote add <远端名称> <仓库路径>
然后我可以通过git remote命令来查看我们当前所连接的远程仓库
这里值得一提的是,我们的一个本地仓库是可以连接多个远程仓库的,不过一般我们都连接一个,不贪多。
然后我们来细说一下我们的推送本地仓库到远程仓库的方法,其完整方法是git push [-f] [--set-upstream] [远端名称 本地分支名 ]
但如果远程分支名和本地分支名相同,我们可以只写本地分支。这里我们就要搞清楚,本地仓库有多个分支,远程仓库也是能有多个分支的,一般来说我们第一个指定的名称是远程仓库的名称,然后我们要指定我们这里要推送的本地仓库的分支,接着是要更新的远程仓库的分支
而-f表示的是强制覆盖,不过这个用不太上,因为一般情况下我们都是只允许添加代码而不允许覆盖代码,要不然公司来个小白把代码覆盖了那就全完了
然后是我们的--set-upstream选项,其代表的是推送到远端的同时建立起推送的本地仓库的分支和远端仓库的关联关系,使用了这个代码,我们就可以在推送本地仓库的同时建立起相关的联系,这样我们进入到对应的分支,如果该分支已经建立了联系,那我们直接输入git push就可以直接将该分支推送到我们的远程仓库中去了
另外使用git branch -vv命令可以查看到本地仓库和远程仓库的联系
从远程仓库克隆
实际开发的时候,往往是多个人的本地仓库要连接到一个远程仓库的,而且其要进行开发,其就需要对远程仓库做一个内容上的复制,将远程仓库的东西下载到自己的仓库上来,此时就需要用到仓库克隆的指令
其命令是git clone <仓库路径> [本地目录],仓库路径就是在码云中仓库旁边点击克隆后看到的ssh,里面的内容就是我们的远程仓库的路径,本地目录如果不指定其会在当前目录下自动生成,且名字与远程仓库的仓库名保持一致
抓取和拉取
在我们实际的协同开发里,是有多个人操纵一个远程仓库的,那不可能远程仓库更新一次我们本地仓库就克隆一次吧?这不折磨人么,所以一般来说,我们都是使用一次克隆,后续我们的仓库更新了,我们就在本地仓库中做对应的更新就可以了。
接着我们就来讲更新本地仓库的两种方法,抓取和拉取。前者只是将更新的内容下载到本地,而没有对本地的对应内容进行一个覆盖更新,如果我们要进行覆盖更新的话,还需要自己手动进行合并操作,后者则是将下载和更新操作合二为一
介绍这两种方法之前我们先要认识到一件事情,那就是当我们的将本地的修改推送到我们的远程仓库中时,此时我们可以认为本地仓库和远程仓库为两个分支,此时我们就只对我们的本地仓库做了更新,远程仓库则没有。而我们所谓的推送其实就是合并,那么当我们将我们的本地仓库与远程仓库合并时,同样也会启用快进模式,反过来当我们将远程仓库和本地仓库合并时,也会开启快速合并(这个是我边看边猜测的,不保证对)
我们这里抓取的命令是将仓库中的更新都抓取到本地,不会进行合并,当然,我们这里需要指定我们的远端名称和分支名,用/隔开,如果不指定就抓取所有分支,不过一般来说我们抓取之后可能会需要合并,那我们还有手动进行一个merge命令,这样就太麻烦了,因此我们又拉取命令,这个命令就等同于我们的抓取+合并,同样的如果不指定远端名称和分支名则默认抓取所有并更新当前分支
远程冲突解决
有这么一种业务情况,A和B用户都修改了同一个文件的同一行代码,此时如果我们进行合并就会发生合并冲突。举个简单的例子,我修改了A文件中的代码,此时我要将其更新都云端,然后我发现云端仓库更新了,那么我们就应该先更新云端仓库,后提交,但是我们执行对应的pull指定的时候,其报了合并异常错误,那么此时我们就还是跟之前学习的一样,进入对应的发生冲突的文件,然后将该文件的发生冲突的内容修改为我们最后要保留的内容,保存之后进行添加和提交我们就合并成功了,之后我们再往云端中推入我们的本地仓库就完了
最后我们提一嘴,我们即使执行了fetch操作,也可以执行pull操作,这个不影响,无非是其发现远程仓库里没什么需要更新的,会直接执行合并操作就完了
最后我们来做一下对应的练习
##########################1-将本地仓库推送到远程仓库
完成4.1、4.2、4.3、4.4的操作
略
[git_test01]添加远程仓库
git remote add origin git@gitee.com/ / .git
[git_test01]将master分支推送到远程仓库,并与远程仓库的master分支绑定关联关系
git push --set-upstream origin master
###########################2-将远程仓库克隆到本地
将远程仓库克隆到本地git_test02目录下
git clone git@gitee.com/ / .git git_test02
[git_test02]以精简的方式显示提交记录
git-log
###########################3-将本地修改推送到远程仓库
[git_test01]创建文件file03.txt
略
[git_test01]将修改加入暂存区并提交到仓库,提交记录内容为:add file03
git add .
git commit -m 'add file03'
[git_test01]将master分支的修改推送到远程仓库
git push origin master
###########################4-将远程仓库的修改更新到本地
[git_test02]将远程仓库修改再拉取到本地
git pull
以精简的方式显示提交记录
git-log
查看文件变化(目录下也出现了file03.txt)
略
IDEA配置Git
本章节我们来学习如何用IDEA来配置Git,这就是重量级内容了,也是我们学习Git中最为重要的一部分
添加忽略
我们要导入我们的项目,我们这里导入的项目是我们的SpringMVC的利用注解进行整合的Maven项目,这里要注意的是,对于Maven项目而言,在idea中不可使用导入,否则容易出问题,我们要直接用Open,打开对应的文件夹就可以了,我们选择用This Windows,因为我们原来的窗口已经没有作用了。接着我们选择file选项里的settings,搜索git,可以看到里面的第一行会自动寻找我们的git的目录并自动指定,如果没有自定指定的话我们就需要手动指定了。然后我们创建对应的.gitignore文件到项目中,在其中添加如下代码
*.class
.mtj.tmp/
*.jar
*.war
*.ear
*.zip
has_err_pid
.idea
*.iml
*.bak
*.class
*.rar
*.log
.project
.settings
.classpath
target
lib
*.DS_Store
.gradle
build
out
log
这些代码都是我们上传时要忽略掉的代码,以后我们要做项目的话也可以直接将这些代码构建一个忽略项目的文件,然后直接拿来用就行了。
具体配置过程
然后我们在码云中创建对应的仓库,接着复制其ssh,以后我们会用得上。
最后我们点击上面的VCS,直接选择Create Git Repository,选择初始化我们的git仓库,这里我们的git仓库就选择是我们的项目的原地址就行了。然后我们的项目就成为了一个本地仓库了,同时再我们的右上角会显示对应的提交与否的选项
这里的勾选即是我们的在git命名行中的提交功能,选中之后其会提示我们那些文件是想要提交的,我们一般来说直接勾选全部就可以了,因为我们事先做好过忽略文件,此时选择提交全部其会自动帮我们忽略掉那些我们本就想忽略掉而不去提交的文件。
同时我们在上方的窗口选项中会发现有对应的git选项卡,我们点击之后可以使用其中提供给我们的各种功能,如果我们想要将我们的本地仓库推送到我们的远程仓库的话,我们就要使用Git选项卡里的push操作,如果其中出现Empty(即远程仓库显示Empty,且无法操作)问题,我们只要到我们对应的项目的目录中,打开Git Bash命令行,然后输入git commit -m 'xxx'然后运行之后就能解决这个问题了。之后我们如法炮制,就会看到我们的远程仓库等待指定
我们点击Define remote,其会自动给我们提供一个origin的名字,我们在下面的url中黏贴我们的之前复制的ssh,然后点击下面的Push,此时我们就可以正确推送到我们的远程仓库中了,中间还会有不少的提示,我们全部都选是就完了。
idea克隆更新解决冲突
然后我们来学习idea的克隆,克隆首先要进入我们的码云对应的仓库,然后复制克隆下面的ssh链接,然后我们点击idea上面的git,接着选择clone...选项,然后我们复制ssh链接到url上然后选择clone就完了,目录可以自己选,接着我们开启一个新的窗口。然后我们接着要解决更新和冲突的问题,我们两个窗口对一个远端仓库进行更新,自然也会发生冲突问题
我们要做的事情是对同一个文件的代码上添加对应的代码,然后两个窗口上我们就进行提交和推送,然后第二次推送的时候idea就会提示我们这里出现了冲突问题并弹出一个窗口,一般来说我们可以直接将问题在这个窗口中解决,但是我们作为初学者,可以先不使用这种方式,我们先将窗口关闭掉,然后进入我们的文件中,我们会看到冲突的文件都报红了,我们进入对应的报红文件,可以看到里面发生冲突的内容也被添加了对应的分支提示,我们将对应的分支提示删除掉,只留下我们想要保留的代码,然后右键选中我们的报红的文件,然后选择git-add,这就相当于是将未跟踪的文件加入到待提交中去,接着我们再执行对应的commit and push就可以正确执行更新了
而另一个用户想要获得更新的话,只需要选择Git框中的第一个↙的箭头就可以完成更新了。最后这里再提一嘴,我们如果想要查看我们的Git提交的历史记录,只需要点击下面的Git框,然后选择log就完了
分支操作
接着我们要学习分支操作,我们只记住一个就可以了,就是我们进入git选项卡里的log,查看其中的提交记录,然后我们可以在对应的提交记录上右键选择New Branch...,然后我们就可以创建对应的分支了,我们只记住这个就可以了
如果我们想要合并的话,那就没法了,只能选择右下角的分支选项卡,然后选择对应的分支接着选择Merge into Current合并分支就完了
idea快速操作入口
我们接着来讲一些idea进行快速操作的入口选项,不过我们的全新版本的idea,可能图里的选项已经过时了就是,到时候对不上,不过没关系,我么也可以先看看,然后在对应的位置自己选就行
更新的idea里上端窗口里没有VCS选项卡,但是有Git选项卡,我们可以在里面做对应的操作,下端也是,没有Version Control选项卡,但是有Git
第二个窗口是我们的另外的选项,简而言之就是替代我们第一张图的选项,我们可以从二张图中挑一个适合自己的,这里我们只做了解就可以了
场景分析
接着我们来做对应的使用Git的场景分析,直接看图吧
几条铁令
这个内容直接主要是为了防止我们新手搞这个把我们的代码搞丢了,所以我们要记住这几条铁令。第一条是切换分支钱要先提交本地的修改,这一点很重要,瞎几把切换分支会导致我们的代码disappear了,所以别乱切换分支,一定要注意先提交本地的修改,提交完了之后再切换。其他的铁令自己看吧
最后是我们的Terminal选项卡,也就是idea下面的选项卡是我们的命令窗口,默认设置的cmd的命令窗口,其位置就在我们的当前的目录的位置,我们如果想要在此处使用Git Bash的命令窗口,只要进入我们的File中选择Setting,然后选择Terminal,在Shell path中将路径配置成我们的GitBash的路径就可以了
指令速查
接着我们来看看在我们的工作流程上的常用的指令
最后的内容无非是一些练习的内容,以及讲解使用Git的好处,这里就不提了
Git问题总结
想要执行命令时出现提示
$ git remote
fatal: detected dubious ownership in repository at 'D:/Rolin的学习笔记'
To add an exception for this directory, call:
git config --global --add safe.directory 'D:/Rolin的学习笔记'
Set the environment variable GIT_TEST_DEBUG_UNSAFE_DIRECTORIES=true and run
again for more information.
简单来说就是提示我们这个仓库是不安全的仓库,解决方法: 参考这篇文章 blog.csdn.net/yxzone/arti…
现在我们来正式学习我们的Swagger,首先我们先来介绍下Swagger的由来
Swagger简介
这个简单来说就是我们的现在的时代都是前端和后端是分离开来的,这里会有两个团队,分别是前端团队和后端团队,而前端和后端的功能是分开的,是松耦合的形式的。即使没有后端,前端自己也是可以独立跑起来的
而我们的前后端甚至可以部署在不同的服务器上,一般来说,我们是前端用于向用户提供服务,而后端则是向前端提供对应的数据。这里我们的前端获取后端数据的方式一般是通过后端提供的对应的接口来获取的,而前端则会通过利用postman等工具来测试后端提供的接口。这其实也就是我们常说的前后端分离
但是这个时候可能就会出现一个沟通上的问题,就是我们的前端和后端的人员怎么知道他们的前后端是正好能合适的呢?早期为了解决这个问题一般会制定对应的计划文档并实时更新,但是对于一些更加大型的项目来说,这也不好用。此时我们的Swagger就应用而生,其就是为了解决前后端的联调问题的
其本身是一个RestFul的Api文档的在线自动生成工具,支持多种语言且可以直接运行,并在线测试我们的API接口,这样前后端都只使用这一个软件就可以解决其联调问题了
最后我们值得一提的是,如果我们想要在项目下使用Swagger,那么我们就要导入对应的jar包,其中以swagger2和swaggerui的jar包最为重要,这两个是我们的核心jar包
SpringBoot集成Swagger
接着我们来正式学习如何在我们的SpringBoot中集成Swagger,首先我们要引入对应的坐标的依赖,其代码如下
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
</dependencies>
这引入需要等待的时间还是蛮久的,说明里面需要引用的jar包还是挺多的。另外这里还有一点值得一提的是,由于我们的课程是2019年的,因此我们的springboot的版本也不能太新,我们要用2.5.6的版本,这个版本我们才可以正确使用的,如果用太新的版本,那就会无法启动我们的服务器
然后我们引入对应的jar包之后还需要对我们的swagger进行集成,也就是将我们的swagger在我们启动服务器的时候跟着启动,我们可以创建对应的Swagger配置类并写入其代码如下
package com.itheima.config;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2 //开启Swagger2
public class SwaggerConfig {
}
我们这里的Configuratino注解就可以理解为是Component注解,但其不完全相同,具体有啥区别,我也忘了。有时间去复习或者直接百度看看到底有啥不同吧
当这些配置完成之后,我们的swagger服务就启动了,我们进入其对应的网址http://localhost:8080/swagger-ui.html,就可以查看到如下界面
我们可以将界面分为四部分,如上图所示,我们接下来就直接开始做我们的案例,这里我们要经常查看源码,因为这玩意没啥正经教程,太简单了,所以咱们要学那还得看源码啊
配置Swagger
首先我们要搞清楚,我们之前做的是集成,而不是配置,那么我们现在就要来正式配置我们的Swagger了。我们首先要知道我们的Swagger的bean实例的对象是Docket,所以我们首先定义一个方法,其返回值为Docket,然后其上写入Bean注解,我们先简单整一个令其返回一个新的Docket对象,但是我们会发现其构造方法里会需要传入参数,我们点进去里面的源码里去看看
点进去源码,可以看到我们的这个类是实现了DocumentationPlugin分页组件的,然后其下的第一个成员变量就是默认的成员变量,其字符串的内容为default,其实这个default也对应我们最开始进入对应的页面里的右上角的选组的第一个default
public class Docket implements DocumentationPlugin {
public static final String DEFAULT_GROUP_NAME = "default";
然后我们往下拉,会看到这么一个构造方法
public Docket(DocumentationType documentationType) {
this.apiInfo = ApiInfo.DEFAULT;
this.groupName = "default";
this.enabled = true;
this.genericsNamingStrategy = new DefaultGenericTypeNamingStrategy();
this.applyDefaultResponseMessages = true;
this.host = "";
this.pathMapping = Optional.absent();
this.apiSelector = ApiSelector.DEFAULT;
this.enableUrlTemplating = false;
this.vendorExtensions = Lists.newArrayList();
this.documentationType = documentationType;
}
我们可以看到其下要先传入一个ApiInfo对象,我们点进去这个对象里面,可以看到里面有一个静态代码块,而且其属性变量里还有一个ApiInfo的成员变量
static {
DEFAULT = new ApiInfo("Api Documentation", "Api Documentation", "1.0", "urn:tos", DEFAULT_CONTACT, "Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0", new ArrayList());
}
可以看到这个静态代码块就是给成员变量的对应属性赋予一个默认值的,也就是默认创建的对象,这里显示的放入的内容就会在我们的Swagger信息里进行显示,也就是我们的页面的左侧。
然后我们重新看我们的Docket的构造方法的源码,此时我们就理解了一切。我们可以看到我们这里第一个传入的是我们的左侧要显示的数据的对象,然后我们默认给其设置其默认组名为default,接着我们打开这个展示,令其变为true等等等等。而完成这些事情,只需要传入一个DocumentationType对象就可以了,然后我们再进去这个对象里看看源码
public class DocumentationType extends SimplePluginMetadata {
public static final DocumentationType SWAGGER_12 = new DocumentationType("swagger", "1.2");
public static final DocumentationType SWAGGER_2 = new DocumentationType("swagger", "2.0");
public static final DocumentationType SPRING_WEB = new DocumentationType("spring-web", "1.0");
private final MediaType mediaType;
可以看到里面有三个成员变量,分别对应的不同的swagger版本,显然,我们要使用的是2.0的版本,而且由于其是静态方法,我们直接点就可以使用了
所以我们可以想当然的往我们的Docket的构造方法里传入我们的该静态变量所创造出来的对应对象。但是此时,我们的ApiInfo使用的还是默认的,但我们要使用的肯定是希望用我们自己设定的,所以我们要自己创建一个ApiInfo对象然后将其设置到对应的对象中,我们可以使用链式方法直接在后面通过.的形式往里面传入我们的info对象
那么我们当然要首先构造出这个info对象,我们新定义一个方法,令这个方法返回ApiInfo对象就完了,这里我们直接拿这个对象中的静态代码块里的代码来用,new一个新的,往里面传入对应的数据,不过我们可以看到这里我们的DEAFULT_CONTACT是报红的,这是因为这里要传入一个对象,而在我们自己创建的配置类里是没有这个对象的,我们直接进入到ApiInfo中点进入该对象中,查看源码,可以看到这里要求传入三个字符串,分别是名字,地址和邮箱,其实这个就是作者的对应信息而已,我们按照其方法创建出来就好了
public class Contact {
private final String name;
private final String url;
private final String email;
那么最终我们可以构造出我们的配置代码如下
package com.itheima.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
@Configuration
@EnableSwagger2 //开启Swagger2
public class SwaggerConfig {
//配置了Swagger的Docket的bean实例
@Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo());
}
//配置Swagger的信息apiInfo
private ApiInfo apiInfo(){
//作者信息
Contact contact = new Contact("Rolin","https://www.bilibili.com/","2259286198@qq.com");
return new ApiInfo(
"Rolin的SwaggerAPI文档",
"千里之行,始于足下",
"1.0",
"https://space.bilibili.com/33169381?spm_id_from=333.1007.0.0",
contact,
"Apache 2.0",
"http://www.apache.org/licenses/LICENSE-2.0",
new ArrayList());
}
}
我们这里就令其返回一个对应的Docket对象,只不过这个Docket对象是经过了我们自己的设置的。最后我们这里值得一提的是,我们的ApiInfo对象是没有提供set方法的,我们要创建对应的对象只能通过构造方法,所以即使我们什么都不想写,我们也得放点参数进去,要不然没法整出这个对象来
Swagger配置扫描接口
然后现在我们看我们的页面下的内容,可以看到我们这里有显示对应的不同请求(还有我们自己写的hello的请求),请求下还有各种请求方式,但是我们这里可以看到,不但我们的请求有,而且其他的请求,比如错误请求一类的也有,而这个错误请求其实并不是我们一开始想扫描到的,所以我们这里要学习我们自定义扫描包的路径的方法,这样我们就可以让我们的页面上只显示我们想要令其显示的请求了
我们要设定我们的包,就需要使用到其对应的方法select(),这里同样需要使用链式的操作,我们首先在我们的Docket对象的后面写入.select(),可以看到里面需要一个build,那么我们就给其一个build,往下继续调用build()方法,调用了该方法之后我回去看我们的select()方法,就会发现其下只有paths和apis两个方法可以调用,我们先来看看apis方法,其需要传入一个RequestHandlerSelectors对象,该对象里可以指定我们的扫描接口的方式,我们先选择其下的basePackage方法,其就是指定包进行扫描的方法,往下只需要写入指定的要扫描的包的路径就可以了
那么我们可以将我们的代码改造如下
//配置了Swagger的Docket的bean实例
@Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.itheima.controller"))
.build();
}
此时我们启动服务器就会发现,我们下面剩下的能扫描的包只剩下我们的自己的包了,此时就说明我们的自定义扫描就已经成功了
接着我们还有一些用于了解的内容,具体请看下面的注释,这里就不再赘述了
//RequestHandlerSelectors,配置要扫描接口的方式
//basePackage:指定要扫描的包
//any():扫描全部
//none():不扫描
//withClassAnnotation:扫描类上的注解,参数是一个注解的反射对象,其实就是注解的class文件
//withClassAnnotation:扫描方法上的注解,参数是一个注解的反射对象,其实就是注解的class文件
然后接着我们来讲下Paths,所谓的的pahts方法,其实就是一个过滤器,可以过滤掉一些不符合我们的条件的接口,令其不被扫描。那么假设我们写入代码如下
.apis(RequestHandlerSelectors.basePackage("com.itheima.controller"))
//paths(),过滤什么路径
.paths(PathSelectors.ant("/Rolin/**"))
.build();
我们这里调用了paths方法进行过滤,我们查看其源码会发现其括号内需要传入一个PathSelectors对象,我们调用其下的ant方法,通过该方法我们可以指定我们只需要哪些路径的方法,比如我们这里指定我们只要路径在Rolin下的方法,但实际上在我们自定义的类中,是没有这种类型的方法的,所以最后我们的结果一定是什么都得不到。同时该对象下还有ant()全不过滤以及none()全过滤的方法,这个作为了解就好了
配置是否启动Swagger
这个就更加简单了,还记得我们当初看Docket源码时的enabled成员属性吗?只要该属性为真,那么我们的Swagger就是启动的,如果为flase,那么直接就打不开了
我们可以直接在我们的apiInfo()方法下直接调用enable方法的属性,将该属性赋值为false,然后我们打开我们的服务器时,就会发现对应的页面已经无法访问了。最后提一嘴,我们的将其赋为false的链式方法也可以最后做,我测试过了,没什么影响
这里我们值得一提的是,我们的的页面地址是http://localhost:8080/swagger-ui.html,并不是其本来就是这样的,而是因为我们的页面实际就是预先放在对应的资源路径下,预先提供好给我们的,对于这点我们需要知其所以然
然后我们要完成一个需求,我们希望我们的Swagger在生成环境中展示,而在正式环境中不展示,那么我们应该要怎么办呢?
我们可以将我们的代码改造如下,首先我们需要让我们的方法接受一个Environment对象,然后我们调用这个对象的acceptsProfiles()方法,该方法可以判断我们当前的环境是否是我们所指定的环境,我们指定其环境需要传入一个Profiles对象,那我们就new这么一个对象,我们点进去该对象中查看其源码,可以发现其有Profiles.of方法,该方法可以指定多个字符串进去,简单来说该方法的作用就是可以返回一个Profiles对象,我们往里面指定的字符串都是我们希望其进行展示的环境
最后我们得到返回的布尔类型的结果,然后将结果赋予给其对应的是否启动Swagger的属性就可以了
//配置了Swagger的Docket的bean实例
@Bean
public Docket docket(Environment environment){
//设置要显示的Swagger环境
Profiles profiles = Profiles.of("dev","test");
//通过environment.acceptsProfiles(profiles)
boolean flag = environment.acceptsProfiles(profiles);
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.enable(flag) //enable为是否启动Swagger,如果为False则不启动,反之则启动
.select()
.apis(RequestHandlerSelectors.basePackage("com.itheima.controller"))
.build();
}
配置API文档的分组
接着我们来将API文档的分组,分组的方法很简单,就是直接利用链式方法调用其下的groupName()方法,然后往括号内传入我们要指定的字符串就行了,然后其就会正确在我们的对应的页面的右上方展示了,如果我们希望我们右上方的下拉框有多个分组的话,就只需要自己定义多个Bean,令其返回多个Docket就可以了,然后我们切换到对应的分组上就可以令其显示对应的数据了
Model实体类的注入
接着我们来看看我们的页面里的Models框,该框下有我们后台定义的实体类,这个实体类里有对应的描述,那么我们要如何将我们的定义的实体类加入到这里来呢?其实我们根本就不用考虑这些有的没的,他都帮我们做好了,只要我们返回了实体类,那么该实体类就会被加载到Models中
接着还存在的问题是,我们虽然返回了对应的实体类,但是其下还没有任何注释,这样前端人员看到这个内容会一头雾水的。此时我们就需要Api的一类注释
package com.itheima.pojo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
//@Api(注释)
@ApiModel("用户实体类")
public class User {
@ApiModelProperty("用户名")
public String username;
@ApiModelProperty("密码")
public String password;
}
比如在我们上面的代码中,ApiModel是用于给我们的整个实体类加注释的,而ApiModelProperty则是给对应的属性加注释的,对应的注释直接写在小括号内就行了
讲完了Models之后,我们再来讲讲我们的方法的栏框。
具体请看下面的代码
package com.itheima.controller;
import com.itheima.pojo.User;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
//Operation接口
@RestController
public class HelloController {
//@RequestMapping("/hello")
@GetMapping(value = "/hello")
public String hello(){
return "Hello World";
}
//只要我们的接口中,返回值中存在实体类,其就会被扫描到Swagger中
@PostMapping(value = "/user")
public User user(){
return new User();
}
//Operation接口,不是放在类上,是方法上
@ApiOperation("Hello控制类")
@GetMapping(value = "/hello2")
public String hello(@ApiParam("用户名") String username){
return "hello"+username;
}
}
比如我们这里使用了ApiOperation该注解,其可以给我们的对应的请求方法加上注释,注意其只能放到方法上,放到类上会失去效果
然后是ApiParam注解,该注解可以给我们的对应的形参加上注释。最后我们要提一点,如果我们的方法是指定了某一种请求方式的,那么在我们页面的请求框中就只会显示对应的请求方式,且只有一个,但如果我们指定其方法是RequestMapping注解的话,那么其就会给我们展示许多种请求的方式,包括但不限于get和post请求
最后我们在我们的对应的Swagger页面里面,我们可以做很多测试,非常方便,我们需要输入什么数据,其都会自动模拟用户的窗口给我们使用
最后我们来总结一下Swagger的好处
- 后端程序员可以给Swagger一些比较难理解的属性或者接口添加注释信息
- 接口文档是在线实时更新的,而且支持多人开发
- 其支持非常方便快捷的在线测试
最后我们要注意的是,出于安全考虑,我们在我们的项目正式项目发布的时候,一定要关闭Swagger