diff --git a/README.md b/README.md index 3c271a7..cc068d8 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,27 @@ 在Lucky的博客的基础上进行了简单的修改,基本上采用Lucky的模板 ## 展示 -[TianZD的主页](www.tianzd.cn) +demo:[TianZD的主页](www.tianzd.cn) ![首页](./source/assets/readme/首页.png) ![推荐文章](./source/assets/readme/推荐文章.png) ![](./source/assets/readme/屏幕截图%202022-05-23%20090338.png) ![](./source/assets/readme/分类.png) -demo:www.tianzd.cn ## 使用 ### 安装nodejs +[nodejs](https://nodejs.org/) ### 安装hexo +```bash +npm install hexo-cli g +``` ### 克隆仓库代码 ```bash git clone git@github.com:tianzhendong/hexo_blog.git ``` -### 安装组件 +### 安装插件 ```bash npm i ``` diff --git a/source/_posts/C_C++/C++.md b/source/_posts/C_C++/C++.md deleted file mode 100644 index 60b07b8..0000000 --- a/source/_posts/C_C++/C++.md +++ /dev/null @@ -1,158 +0,0 @@ ---- -title: C++ -date: 2022年4月28日 -author: TianZD -top: true -cover: true -toc: true -mathjax: false -summary: C++使用记录 -tags: - - C++ - - 编程 -categories: - - C++ -abbrlink: 5751eea2 -reprintPolicy: cc_by -coverImg: -img: -password: ---- - -[toc] - -# C++ - -## C/C++错误集锦(VS2015):错误 C2131 表达式的计算结果不是常数 - -### 问题 - -```c++ -//juzicode.com;vx:桔子code -//vs2015 -#include < iostream > -using namespace std; -int main(void) -{ - int len; - cin >> len; - int array[len]; - while (len--){ - cin >> array[len]; - } - for (int a : array) { - cout << a << " "; - } - return 0; -} -``` - -![img](https://gitee.com/tianzhendong/img/raw/master//images/202202181643956.png) - -### 原因 - -1、编译期间要根据给定长度为数组分配内存空间,如果数组长度是变量就无法判断该使用多大的空间 - -### 解决 - -1、使用动态分配内存的方式: - -```c++ -//juzicode.com;vx:桔子code -//vs2015 -#include < iostream > -using namespace std; -int main(void) -{ - int len; - cin >> len; - int *array = new int[len];//动态分配内存 - int i = len; - while (i--) { - cin >> array[i]; - } - for (int i = 0; i < len;i++) { - cout << array[i] << " "; - } - return 0; -} -``` - -## c++在一个cpp文件中调用另一个cpp文件的函数的两种方法 - -[blog.csdn.net](https://blog.csdn.net/weixin_43350361/article/details/106455331) - -有时候写代码为了简便,会将一些函数方法单独在一个cpp的源文件中定义,然后在另一个源文件中需要用到自定义的函数时直接调用就可以了!学过c++的人可能最熟悉的方法还是利用头文件来进行调用,其实还有一种c语言当中的方法一般情况下同样可以适用与c++中! - -### 方法1:创建头文件 - -举个简单的例子: -首先创建一个method.h头文件,声明一个求最大值的函数max(int x,int y),然后再创建一个同名的源文件method.cpp,在该文件中实现函数max(int x,int y)的功能。代码如下: - -```c++ -//method.h -#include -using namespace std; -int max(int x, int y); //在头文件中声明函数 -``` - - -​ - -```c++ -//method.cpp -#include -#include"method.h" -using namespace std; -int max(int x, int y) //在源文件中实现函数方法 -{ - return x > y ? x : y; -} -``` - -然后再创建一个主函数的源文件,包含定义函数的头文件就可以在这个源文件中调用max函数,运行结果素130,正确!主函数源文件的代码如下: - -```c++ -#include -#include"method.h" //包含头文件 -using namespace std; - -int main() -{ - int a = 13, b = 130; - cout << "最大值为:" << max(a, b) << endl;//调用头文件中定义的函数 - system("pause"); - return 0; -} -``` - - -​ -​ - -### 方法2:调用前声明函数 - -这个方法在c语言中有介绍过,就是不用创建头文件,直接在method.cpp源文件中定义并实现好max函数后,在主函数文件中调用max函数前声明一下就可以噜!主函数代码如下,method.cpp的内容不变! - -```c++ -#include -using namespace std; - -int main() -{ - int max(int x, int y);//调用前声明一下max函数 - int a = 13, b = 130; - cout << "最大值为:" << max(a, b) << endl; - system("pause"); - return 0; -} -``` - - -​ ​ - -结果还是一样的,这是因为在同一个project里面的源文件之间的函数一般情况下是可以互相调用的,前提是要声明以及不限制它的作用域! - - - -[查看原网页: blog.csdn.net](https://blog.csdn.net/weixin_43350361/article/details/106455331) diff --git a/source/_posts/C_C++/QT.md b/source/_posts/C_C++/QT.md deleted file mode 100644 index e00ba31..0000000 --- a/source/_posts/C_C++/QT.md +++ /dev/null @@ -1,2151 +0,0 @@ ---- -title: QT -date: 2022年4月28日 -author: TianZD -top: true -cover: true -toc: true -mathjax: false -summary: QT的安装教程,以及一些使用过程记录 -tags: - - C++ - - QT -categories: - - C++ -abbrlink: 8e7feb07 -reprintPolicy: cc_by -password: -coverImg: -img: ---- - -[toc] - -# QT -## QT 安装 - -中国科学技术大学:http://mirrors.ustc.edu.cn/qtproject/ -清华大学:https://mirrors.tuna.tsinghua.edu.cn/qt/ -北京理工大学:http://mirror.bit.edu.cn/qtproject/ -中国互联网络信息中心:https://mirrors.cnnic.cn/qt/ - -![在这里插入图片描述](https://gitee.com/tianzhendong/img/raw/master//images/202202181559349.png) - -![在这里插入图片描述](https://gitee.com/tianzhendong/img/raw/master//images/202202181559285.png) - -各模块意思 -Qt Charts是二维图表模块,用于绘制柱状图、饼图、曲线图等常用二维图表。在制作一些需要绘制表格的软件的时候经常会用到,建议勾选 -Qt Quick 3D模块初探,这个是技术预览勾不勾都可以,Quick 3D提供了用于基于Qt Quick创建3D内容或UI的高级API。提供了对现有Qt Quick场景图的扩展,以及对该扩展场景图的渲染器。不过还是在测试阶段,因为我可能会用到,所以我勾了。 -Qt Data Visualization 是三维数据图表模块,用于数据的三维显示,如散点的三维空间分布、三维曲面等。这个如果你勾选了Qt Charts的话,这个基本也要用得到,建议勾选。 -QT lottie animation 这个主要是用来实现复杂的动画效果,如果要用来制作动画或者制作游戏的话菜肴勾选。 -Qt Purchasing、Qt WebEngine、Qt Network Auth(TP)等其他模块,括号里的 TP 表示技术预览。(技术预览就是就算你勾选了,也用不了,就是让你看看而已,哈哈哈) -Qt Scritp(Deprecated)是脚本模块,括号里的“Deprecated”表示这是个已经过时的模块。 -接下来是tool的选择 - ------------------------------------------------- -版权声明:本文为CSDN博主「流楚丶格念」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 -原文链接:https://blog.csdn.net/weixin_45525272/article/details/113062352 - -## 解决Qt-至少需要一个有效且已启用的储存库 问题 - -[blog.csdn.net](https://blog.csdn.net/asdsa12311/article/details/101935605) - -问题如图所示 - -![image-20220218155724951](https://gitee.com/tianzhendong/img/raw/master//images/202202181557049.png) - -解决方法: - -1、选择左下角的设置,进入如图界面,然后选择“临时存储库” - -![image-20220218155742235](https://gitee.com/tianzhendong/img/raw/master//images/202202181557304.png) - -2、手动添加临时储存库要定位一个储存有QT在线安装镜像的地址,这个地址可以从这里选择http://download.qt.io/static/mirrorlist/ - -在这个网站,显示了各国的qt镜像站点,中国有四个,随便选一个中科大的站点击HTTP会进入一个网络文件夹。 - -以此进入如下路径:/online/qtsdkrepository/windows\_x86/root/qt/ 。然后把该路径添加到临时存储库中。然后就可以增删组件了。 - -最终路径如下所示 - -`https://mirrors.tuna.tsinghua.edu.cn/qt/online/qtsdkrepository/windows_x86/root/qt/` - -## Qt程序运行时CMD窗口 - -Linux 下都是带终端的. -Windows 下面如果 pro 文件中 config+=console,就会带一个 cmd 窗口 - -## Qt Creator中显示的汉字变为乱码 - -[blog.csdn.net](https://blog.csdn.net/liuweilhy/article/details/82321105) - -### 问题是什么? - -在学习Qt编程的过程中,大多数人都遇到过中文乱码的问题。总结起来有三类: - -1\. Qt Creator中显示的汉字变为乱码,编辑器上方有“Could not [decode](https://so.csdn.net/so/search?q=decode&spm=1001.2101.3001.7020) "..." with "UTF-8"-encoding. Editing not possible.”的错误提示。此时,出现乱码的文档是不可编辑的。如下图所示,“你好中文!”这5个中文字符变成了乱码: - -![image-20220218160928755](https://gitee.com/tianzhendong/img/raw/master//images/202202181609841.png) - -2\. Qt Creator中显示的汉字正常,但编译的时候会出现“常量中有换行符”等一系列错误报警。其实,这也是文字编码的问题。如下图所示: - -![image-20220218160959524](https://gitee.com/tianzhendong/img/raw/master//images/202202181609646.png) - -3\. 编译时未报错,但生成的程序中文乱码。如下图所示: - -![image-20220218161014007](https://gitee.com/tianzhendong/img/raw/master//images/202202181610061.png) - -其中,第3条是网上提问的最多的,几乎是所有使用MSVC的初学者都会碰到的问题。很多回答是针对Qt4版本的,Qt5中不可用。 - -### 为什么会出现这些问题? - -在解决问题之前,字符编码知识是必需的。你要知道ASCII、GB2312、GBK、Unicode、UTF-8、UTF-16、BOM是怎么回事。此外,你还要明白源码字符集、执行字符集是什么。详细内容可以在网上搜索一下,俯拾即是。 - -1\. Qt Creator的编辑器默认使用**UTF-8**(代码页65001)编码来读取文本文件。而Visual Studio保存文件时默认采用的是本地编码,对于简体中文的Windows操作系统,这个编码就是GB2312(代码页936)。如果使用Qt Creator读取由Visual Studio创建的文件,那么编辑器就会以UTF-8编码格式读取GB2312编码格式的文件,出现中文乱码,因为这两套编码系统对汉字编码是不同的。至于英文部分不会乱码,是因为UTF-8和GB2312在单字节字符部分是兼容的。 - -2\. MSVC在编译时,会根据源代码文件有无BOM来定义源码字符集。如果有BOM,则按BOM解释识别编码;如果没有,则使用本地字符集,对于简体中文的Windows操作系统就是GB2312。那么,当MSVC遇到一个没有BOM的UTF-8编码的文件时,它通常会把文件看作GB2312的来处理。如果文件全是英文没有问题,但如果包含中文,编译器就会出现误读。这种情况下,Qt Creator编辑器是正常的。但对于MSVC编译器,原代码会被它认识成下图这个样子: - -![image-20220218161038546](https://gitee.com/tianzhendong/img/raw/master//images/202202181610648.png) - -这是我用EverEdit指定本地编码重读后的结果,可以看到汉字出错,末端的引号也没了。 - -在UTF-8中,一个中文字符(汉字或标点符号)占用3个字节,“你好中文!”这5个中文字符共占用15个字节;而在GB2312中,一个中文字符(汉字或标点符号)占用2个字节,这时,MSVC把UTF-8编码的15个字节加上后面1个字节的英文引号合成16个字节当作8个中文字符处理。之后,MSVC在这一行里直到末尾换行符出现都没有找到下一个引号,它以为你把字符串在这里敲回车换行了,于是报警称“常量中有换行符”,并引出一系列的错误。 - -不过,当以无BOM的UTF-8编码的字符串正好凑够偶数个字节时(比如偶数个汉字,或奇数个汉字加奇数个英文字母),编译器通常不会报警,因为它以为用GB2312编码读出的是正确的。 - -3\. 不管源文件是何种编码,只要MSVC能够正确识别,就可以通过编译。但MSVC的执行字符集默认是本地字符集。对我们来说,它生成的可执行文件中的文字是GB2312编码的。而生成的Qt程序以UTF-8编码来识别GB2312编码的文字,对于“你好中文!”这几个字符,采用GB2312编码后再以UFT-8编码来读取,就会变成如下的乱码: - -![image-20220218161057717](https://gitee.com/tianzhendong/img/raw/master//images/202202181610774.png) - -当以无BOM的UTF-8编码的字符串正好凑够偶数个字节时(比如偶数个汉字,或奇数个汉字加奇数个英文字母),反而不会出现乱码。那是因为,编译器用GB2312编码读出的乱码本身就是UTF-8编码的,现在又用UTF-8解读,自然就正确了。这纯粹是歪打正着。 - -### 怎么解决这些问题? - -首先,你要确定采用哪种源码字符集。你有两个选择: - -1\. 采用本地编码字符集(不推荐,跨平台时会比较麻烦,但在Visual Studio环境下配合Add-in工具编程比较方便); - -2\. 采用UTF-8编码字符集(推荐,适合跨平台)。 - -#### “采用本地编码字符集”方案,解决方法如下: - -首先,要把项目中所有的头文件和源文件全都转换成GB2312编码保存。 - -1\. 第1个问题:在Qt Creator中打开项目,点击左侧工具栏“项目”,在“编辑器”选项卡中把“默认编码”改成“GB2312”。如下图所示: - -![image-20220218161128126](https://gitee.com/tianzhendong/img/raw/master//images/202202181611248.png) - -话说回来,既然选择本地字符集,大致上是放弃跨平台了。与其用轻量级的Qt Creator,不如用Visual Studio作开发环境更好。 - -2\. 第2个问题:“常量中有换行符”等一系列报警已不存在了。 - -3\. 第3个问题:在字符串常量上加QStringLiteral宏或QString::fromLocal8Bit函数,如: - - QString str = "你好中文!"; - -改为: - - QString str = QStringLiteral("你好中文!"); - -或者: - - QString str = QString::fromLocal8Bit("你好中文!"); - -不过,在这两种形式下,你都无法用tr方法来创建翻译了。 - -#### “采用UTF-8编码字符集”方案,解决方法如下: - -**注意:加上后,qdebug输出中文正常,cout输出中文乱码,窗口名称输出中文乱码** - -首先,要把项目中所有的头文件和源文件全都转换成UTF-8+BOM编码保存。 - -![image-20220218172128013](https://gitee.com/tianzhendong/img/raw/master//images/202202181721125.png) - -1\. 第1个问题不存在了。 - -2\. 第2个问题也不存在了。 - -3\. 第3个问题,你也可以用上个方案中的方法来解决,但有更好的方法。那就是要用到中文字符的头文件和源文件开头加上MSVC的一个宏: - -```c++ -#if _MSC_VER >= 1600 -#pragma execution_character_set("utf-8") -#endif -``` - -这个宏告诉MSVC,执行字符集是UTF-8编码的,别瞎整成GB2312的!还有个好处,就是能用tr包中文,方便日后的翻译。 - - - -或者在pro文件中加入: - -```properties -msvc { - QMAKE_CFLAGS += /utf-8 - QMAKE_CXXFLAGS += /utf-8 -} -``` - - - -最终效果如下: - -![image-20220218161244977](https://gitee.com/tianzhendong/img/raw/master//images/202202181612073.png) - -[查看原网页: blog.csdn.net](https://blog.csdn.net/liuweilhy/article/details/82321105) - -## 回顾C++基础 - -### 类和对象 - -```C++ -//新建类 -class student{ -public: - string name; - int age; - void test(); -}; -void student::test(){//类外定义 - cout<age<age = 11;//通过箭头访问 - stu2->test(); -``` - -### 重载 - -**重载**:函数名相同,但是参数不同 - -### 构造函数和析构函数 - -**析构函数**:对象被删除或者生命周期结束时触发 - -**构造函数** :对象被创建的时候触发、 - -```c++ -student::student(){ - cout<<"hello,构造函数"<btnLogin,SIGNAL(clicked()),this,SLOT(on_btnRegister_clicked()));` - -```c++ -Widget::Widget(QWidget *parent) : - QWidget(parent), - ui(new Ui::Widget) -{ - ui->setupUi(this); - connect(ui->btnRegister,SIGNAL(clicked()),this,SLOT(on_btnRegister_clicked()));//添加的关联函数 -} -``` - -其中`ui`为指向整个窗口的指针,`btnLogin`为要关联的控件A,`SIGNAL()`函数中指定**信号**B,`SLOT()`函数中为**槽函数**C - -当A发出B信号时,会触发槽函数C - -## 添加图片 - -[icon网址](http://www.easyicon.net) - -[图标之家](http://www.icosky.com/) - -### 添加资源目录 - -![image-20220122193234441](https://s2.loli.net/2022/01/22/XYjrqiCbU6MzaTV.png) - -工程会出现Resources文件夹 - -### 将图片放入QT工程目录下 - -1. 右键Resources文件夹下的文件, - -![image-20220122193716310](https://s2.loli.net/2022/01/22/4MWZv3ltOqam1ds.png) - -2. 添加前缀 - -前缀设置成`/`,并保存 - -![image-20220122193812191](https://s2.loli.net/2022/01/22/dl8oAMyxzrYV7hJ.png) - -3. 点击`添加`并选择添加的图片 - -### 引用文件 - -1. 在ui页面添加`label`控件(也支持按钮控件) -2. 删掉控件中的文字,并右键——改变样式表——添加资源右侧的箭头——选择border-image——点击左侧——选择文件 - -![image-20220122194046904](https://s2.loli.net/2022/01/22/Ridprh2zasqc1lx.png) - -![image-20220122194138559](https://s2.loli.net/2022/01/22/524AtPKZs1dMBTf.png) - -## UI界面布局 - -> 页面布局不会改变任何代码 - -### 概述 - -**用途**:防止因不同设备、不同缩放、不同分辨率导致布局错乱问题 - -**QT布局样式:** - -* 水平布局:对应1 -* 垂直布局:对应2 -* 栅格布局:对应3 - -![image-20220122194911328](https://s2.loli.net/2022/01/22/3URe1THBVsWoD2i.png) - -### 使用 - -选中两个控件,点击对应的布局按钮即可 - -#### 弹簧控件Spacers - -弹簧用来当缩放UI时,周边间距进行自动变化 - -![image-20220122195329642](https://s2.loli.net/2022/01/22/CdmcWSjxwBlhVpY.png) - -#### 最终 - -![image-20220122195549478](https://s2.loli.net/2022/01/22/gCZIDWbJYyOQ9St.png) - -## 界面切换 - -### 创建一个新的界面 - -右键工程——添加新文件——QT——QT设计师界面类——widget——输入名字,这里为`form` - -会自动在Headers文件夹下生成`form.h`头文件 - -![image-20220122195907707](https://s2.loli.net/2022/01/22/EeoYpPWuvG7byaI.png) - -### 关联控件 - -修改`widget.cpp`文件,添加**头文件**和关联槽函数 - -```c++ -#include "form.h" //添加头文件 -//省略 -void Widget::on_btnLogin_clicked() -{ - //qDebug("register"); - Form *form1 = new Form;//创建一个Form对象 - form1->setGeometry(this->geometry());//设置form.ui的尺寸为当前ui的尺寸 - form1->show();//展示 -} -``` - -### 关闭新界面 - -1. 新UI界面控件改名 -2. 关联信号 -3. 设置关闭槽函数 - -```c++ -void Form::on_pushButton_clicked() -{ - this->close(); -} -``` - -## 获取界面输入 - -```c++ -void Widget::on_btnLogin_clicked() -{ - //获取输入的参数 - QString name = ui->inputName->text(); - QString psd = ui->inputPassword->text(); - if(name == "admin" && psd == "123123"){ - Form *form1 = new Form;//创建一个Form对象 - form1->setGeometry(this->geometry());//设置form.ui的尺寸为当前ui的尺寸 - form1->show();//展示 - } -} -``` - -## QT GUI设计 - -### 窗口置顶 与 取消置顶 - -```c -void MainWindow::on_windowTopButton_clicked() -{ -  if (m_flags == NULL) -  { -    m_flags = windowFlags(); -    setWindowFlags(m_flags | Qt::WindowStaysOnTopHint); -    this->show(); -  } -  else -  { -    m_flags = NULL; -    setWindowFlags(m_flags); -    this->show(); -  } -} -``` - - - -回到顶部 - -### 全屏显示 与 退出全屏 - -首先,在QT中对于窗口显示常用的有这么几个方法可以调用: - -```c -showFullScreen() // Qt全屏显示函数 -showMaximized() // Qt最大化显示函数 -showMinimized() // Qt最小化显示函数 -resize(x, y) // Qt固定尺寸显示函数 -setMaximumSize(w, h) // Qt设置最大尺寸函数 -setMinimumSize(w, h) // Qt设置最小尺寸函数 -``` - -但是 `showFullScreen()` 和 `showNormal()` 只对顶级窗口有效果,对子窗口无效。要将子窗口全屏显示可用以下方法: - -- 将要全屏的 Qt 子窗口调用 `setWindowFlags(Qt::Window)` 将其类型提升为顶级窗口模式,然后调用 `showFullScreen()` 函数将子窗口全屏显示。 -- 当然全屏后还要恢复正常,即调用 `setWindowFlags(Qt::subwindow)` 将子窗口设置为非顶级窗口,再调用 `showNormal()` 还原子窗口显示。 - -```c -// 全屏显示: -VideoWidget->setWindowFlags(Qt::Window); -VideoWidget->showFullScreen(); - -// 退出全屏时: -VideoWidget->setWindowFlags(Qt::SubWindow); -VideoWidget->showNormal(); -``` - - - -回到顶部 - -### 窗口的 "最大化\最小化\关闭" 按钮设置 - -1、在 QDialog 中添加 "最大化" 和 "最小化"按钮 - -在使用 QDialog 时,默认情况下只有 “这是什么” 和 “关闭” 按钮,但是我们习惯有最大化和最小化按钮。这里介绍如何在该模式下如何设置。 - -```c -this->setWindowFlags(Qt::Dialog | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint); -``` - -其中 `WindowMinMaxButtonsHint` 设置的就是最大和最小按钮,但是只这样设置,关闭按钮就不可用了,因此需要再添加 `WindowCloseButtonHint` 一项。 - -2、只禁止最大化按钮 - -```c -this->setWindowFlags(windowFlags() &~ Qt::WindowMaximizeButtonHint); -``` - -`Qt::WindowMaximizeButtonHint` 为 `WindowType` 枚举变量,通过修改它可以选择禁止 "最小化" 或者 "关闭" 按钮。 - -回到顶部 - -### 禁止拖动窗口大小 - -```c -this->setFixedSize(this->width(),this->height()); -``` - - - -回到顶部 - -### 获取屏幕的宽度和高度 - -```c -QApplication::desktop()->width(); -QApplication::desktop()->height(); -``` - -即得到屏幕分辨率,如 1024*768。 - - - -### 去掉菜单栏 - -```c - //隐藏菜单栏 - this->setWindowFlags(Qt::FramelessWindowHint | - Qt::WindowSystemMenuHint | - Qt::WindowMinMaxButtonsHint); -``` - -去掉以后,发现窗口无法移动,无法放大缩小关闭 - -### 添加自定义最小化、最大化、关闭按钮 - -mainwindow.h - -```c -protected : - /** - * @brief closeEvent重写closeEvent - * @param event - */ - void closeEvent(QCloseEvent *event); -private: - //全屏显示标识 - bool fullScreenFlag = false; -``` - -mainwindow.cpp - -```c -/** - * @brief MainWindow::closeEvent 重写closeEvent: 确认退出对话框 - * @param event - */ -void MainWindow::closeEvent(QCloseEvent *event) -{ - QMessageBox::StandardButton button; - button=QMessageBox::question(this,tr("退出程序"),QString(tr("确认退出程序?")),QMessageBox::Yes|QMessageBox::No); - if(button==QMessageBox::No) - { - event->ignore(); // 忽略退出信号,程序继续进行 - } - else if(button==QMessageBox::Yes) - { - event->accept(); // 接受退出信号,程序退出 - } -} - - -/** - * @brief MainWindow::on_closeBTN_clicked 自定义关闭按钮 - */ -void MainWindow::on_closeBTN_clicked() -{ - this->close(); -} - -/** - * @brief MainWindow::on_showMaximizedBTN_clicked 最大化按钮 - */ -void MainWindow::on_showMaximizedBTN_clicked() -{ - if(!fullScreenFlag){ - this->showFullScreen(); - fullScreenFlag = true; - } - else{ - this->showNormal(); - fullScreenFlag = false; - } -} -/** - * @brief MainWindow::on_showMinBTN_clicked 最小化按钮 - */ -void MainWindow::on_showMinBTN_clicked() -{ - this->showMinimized(); -} - -``` - - - - - -### 拖拽窗口移动 - -mainwindow.h - -```c -protected : - /** - * @brief 拖拽窗口 - */ - void mousePressEvent(QMouseEvent *event); - void mouseMoveEvent(QMouseEvent *event); - void mouseReleaseEvent(QMouseEvent *event); -private: - Ui::MainWindow *ui; - //拖拽窗口 - bool m_bDrag; - QPoint mouseStartPoint; - QPoint windowTopLeftPoint; -``` - -mainwindow.cpp - -```c -/** - * @brief MainWindow::mousePressEvent 拖拽窗口移动操作 - * @param event - */ -void MainWindow::mousePressEvent(QMouseEvent *event) -{ - if(event->button() == Qt::LeftButton) - { - m_bDrag = true; - //获得鼠标的初始位置 - mouseStartPoint = event->globalPos(); - //mouseStartPoint = event->pos(); - //获得窗口的初始位置 - windowTopLeftPoint = this->frameGeometry().topLeft(); - } -} - -void MainWindow::mouseMoveEvent(QMouseEvent *event) -{ - if(m_bDrag) - { - //获得鼠标移动的距离 - QPoint distance = event->globalPos() - mouseStartPoint; - //QPoint distance = event->pos() - mouseStartPoint; - //改变窗口的位置 - this->move(windowTopLeftPoint + distance); - } -} - -void MainWindow::mouseReleaseEvent(QMouseEvent *event) -{ - if(event->button() == Qt::LeftButton) - { - m_bDrag = false; - } -} -``` - -### 设置空间透明 - -```mode->setFlat(true);``` - -//就是这句实现按钮透明的效果。 - -## QT串口调试工具 - -网络编程、串口编程、操作GPIO - -仿写 - -![image-20220122215752070](https://s2.loli.net/2022/01/22/wA6FI9foa1V2edh.png) - -### 串口调试助手UI界面 - -#### 添加控件 - -1. 设计UI界面大小`800*480` -2. 添加数据接收框`Plain Text Edit`,并在属性设置区勾选`read only` -3. 添加参数下拉选择输入框`Combo Box`和问题提示框`label` -4. 添加发送框`line Edit` -5. 添加按钮`push button` -6. 添加广告框`GroupBox`+`Label` - -![image-20220122225909615](https://s2.loli.net/2022/01/22/4w1sceg8J9ILXNC.png) - -#### 添加属性 - -1. 双击波特率、数据位、停止位、校验位下拉输入框,添加属性 - -![image-20220122230043510](https://s2.loli.net/2022/01/22/QfVq7xgpyjBc9rv.png) - -2. 通过属性`currentIndex`设置默认值 - -![image-20220122231854565](https://s2.loli.net/2022/01/22/TXhKWq7FQPzLB1C.png) - -#### 控件改名 - -添加库支持 - -在`.pro`文件中,增加`serialport` - -```c++ -QT += core gui serialport -``` - - - -#### 搜索串口并显示 - -`widget.cpp` - -```c++ -#include "widget.h" -#include "ui_widget.h" -#include //添加串口头文件 - -Widget::Widget(QWidget *parent) : - QWidget(parent), - ui(new Ui::Widget) -{ - ui->setupUi(this); - //获取串口号并显示 - QStringList serialPortNums; - foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) { - serialPortNums<serialPortNum->addItems(serialPortNums); -} - -Widget::~Widget() -{ - delete ui; -} - -``` - -### 实现逻辑功能 - -#### 创建串口对象 - -在widget.h中添加头文件,并声明串口对象指针 - -```c++ -#include//引入串口头文件 -public: - QSerialPort *serialPort;//声明串口类指针 -``` - -zai widget.cpp中创建对象 - -```c++ - serialPort = new QSerialPort(this);//创建串口对象 -``` - -#### 打开串口并初始化 - -1. 关联打开串口控件的信号和槽函数 - -2. 定义槽函数:定义串口数据——初始化 -3. 给当前串口对象赋值 - -```c++ -void Widget::on_btnOpenSerial_clicked()//打开串口-点击信号对应的槽函数 -{ - //声明变量 - QSerialPort::BaudRate baudRate; - QSerialPort::DataBits dataBits; - QSerialPort::StopBits stopBits; - QSerialPort::Parity parity; - //赋值,初始化 - //波特率 - if(ui->baudRate->currentText() == "4800"){ - baudRate = QSerialPort::Baud4800; - }else if(ui->baudRate->currentText() == "9600"){ - baudRate = QSerialPort::Baud9600; - }else if(ui->baudRate->currentText() == "115200"){ - baudRate = QSerialPort::Baud115200; - } - //数据位 - if(ui->dataBits->currentText()=="5"){ - dataBits = QSerialPort::Data5; - }else if(ui->dataBits->currentText()=="6"){ - dataBits = QSerialPort::Data6; - }else if(ui->dataBits->currentText()=="7"){ - dataBits = QSerialPort::Data7; - }else if(ui->dataBits->currentText()=="8"){ - dataBits = QSerialPort::Data8; - } - //停止位 - if(ui->stopBits->currentText()=="1"){ - stopBits = QSerialPort::OneStop; - }else if(ui->stopBits->currentText()=="1.5"){ - stopBits = QSerialPort::OneAndHalfStop; - }else if(ui->stopBits->currentText()=="2"){ - stopBits = QSerialPort::TwoStop; - } - //校验位 - if(ui->parity->currentText()=="none"){ - parity = QSerialPort::NoParity; - } - - //获取当前选择的串口号 - serialPort->setPortName(ui->serialPortNum->currentText()); - //设置串口参数 - serialPort->setBaudRate(baudRate); - serialPort->setDataBits(dataBits); - serialPort->setStopBits(stopBits); - serialPort->setParity(parity); -} -``` - -4. 判断串口是否打开成功 - -```c++ - //判断串口是否打开成功,并提示,需要添加头文件 - if(serialPort->open(QIODevice::ReadWrite) == true){ - QMessageBox::information(this,"提示","成功"); - }else { - QMessageBox::critical(this,"提示","失败"); - } -``` - -![image-20220123104230714](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123104230714.png) - -![image-20220123104241542](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123104241542.png) - -#### 关闭串口 - -1. 关联信号和槽函数 -2. 定义逻辑 - -```c++ -void Widget::on_btnCloseSerial_clicked() -{ - serialPort->close(); -} -``` - -#### 数据接收 - -1. 在`widget.h`中`private slots`下增加槽函数声明 - -```c++ -private slots: - void serialDataReadReady_Slot();//读数据槽函数声明 -``` - -2. 在`widget.cpp`中增加槽函数 - -```c++ -//读数据槽函数定义 -void Widget::serialDataReadReady_Slot(){ - QString buff;//暂存数据 - buff = QString(serialPort->readAll());//读取串口的数据 - ui->dataRCV->appendPlainText(buff);//将数据发送到接受区内 -} -``` - -3. 在`widget.cpp`中,添加关联函数 - -```c++ - //关联数据接收槽函数 - connect(serialPort,SIGNAL(readyRead()),this,SLOT(serialDataReadReady_Slot())); -``` - -#### 数据发送 - -```c++ -void Widget::on_btnSend_clicked()//数据发送槽函数 -{ - serialPort->write(ui->dataInput->text().toLocal8Bit().data()); -} -``` - -#### 清空 - -```c++ -void Widget::on_btnClear_clicked()//清空槽函数 -{ - ui->dataRCV->clear(); -} -``` - -## QT程序打包和部署 - -### 为什么 - -1. 把写好的程序给别人用 - -2. 源码不能随便给别人 - -### 怎么做 - -#### release模式 - -1. 把工程切换到release模式,然后编译 - -release模式,没有调试信息 - -debug模式,有很多调试信息 - -![image-20220123112932311](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123112932311.png) - - - -2. 找到release模式构建的文件夹,并进入release文件夹 - -![image-20220123113146728](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123113146728.png) - -![image-20220123113218111](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123113218111.png) - - - -**Serial.exe文件就是所需的文件,但是之间无法打开,需要动态库** - -#### 修改图标 - -[图标之家](http://www.icosky.com/) - -1. 下载图标,**格式为.ico**,并拷贝到工程目录下,注意,不是编译目录 - -这里图标为:`serial_ico.ico` - -![image-20220123114501066](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123114501066.png) - -2. 在.pro中增加图标,并编译 - -```properties -RC_ICONS = serial_ico.ico -``` - -编译可以看到左上角图标已经改变 - -![image-20220123114615068](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123114615068.png) - -同时release目录下的exe文件的图标也已经改变 - -![image-20220123114654768](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123114654768.png) - -#### 封包 - -1. 创建一个新的文件夹,不要有中文路径`C:\code\QT\Serial_deploy` - -2. 拷贝release下的exe文件到文件夹中 - -3. 进入QT控制台,并进入新建的文件夹 - -直接搜索QT,应用下面的就是 - -![image-20220123114858514](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123114858514.png) - -4. 输入`windeployqt Serial.exe`命令后自动封包,结果如下 - -![image-20220123115455018](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123115455018.png) - -5. 双击里面的Serial.exe文件可以直接运行 - -### 程序打成安装包 - -Inno Setup软件 - -[参考](https://www.cnblogs.com/linuxAndMcu/p/10974927.html) - -## QT网络编程-TCP通信 - -### TcpServer - -两个类: - -* QTcpServer -* QTcpSocket - -#### QTcpServer类 - -提供一个TCP基础服务类 继承自QObject - -这个类用来接收到来的TCP连接,可以**指定TCP端口**或者用QTcpServer自己挑选一个端口,可以**监听一个指定的地址或者所有的机器地址**。 - - 调用`listen()`来监听所有的连接,每当一个新的客户端连接到服务端就会**发射信号**`newConnection()` - -调用`nextPendingConnection()`来接受待处理的连接。返回一个连接的`QTcpSocket()`,我们可以用这个返回的套接字和客户端进行连接 - -如果有错误,`serverError()`返回错误的类型。调用`errorString()`来把错误打印出来。 - -当监听连接时候,可以调用`serverAddress()`和`serverPort()`来返回服务端的地址和端口。 - -调用`close()`来关闭套接字,停止对连接的监听。 - -| Header: | #include | -| ------------- | --------------------------------- | -| qmake: | QT += network | -| Inherits: | [QObject](../qtcore/qobject.html) | -| Inherited By: | [QSctpServer](qsctpserver.html) | - -**成员函数** - -| 函数 | 用途 | -| ------------------------------------------------------------ | ------------------------------------------------------------ | -| `void close() ` | 关闭服务,然后服务器讲不再监听任何连接 | -| `QString errorString()const` | 错误时候返回错误的字符串 | -| `bool hasPendingConnections()const` | 如果服务端有一个待处理的连接,就返回真,否则返回假 | -| `QTcpSocket* nextPendingConnection()` | 返回一个套接字来处理一个连接,这个套接字作为服务端的一个子对象,意味着当QTcpServer对象销毁时候,这个套接字也自动删除,当使用完后明确的删除这个套接字也好,这样可以避免内存浪费。当没有可处理的连接时候,这个函数返回0。 | -| `void incomingConnection(int socketDescriptor)[virtualprotected]` | 这个函数新建一个QTcpSocket套接字,建立套接字描述符,然后存储套接字在一个整形的待连接链表中。最后发射信号newConnection() | -| `bool isListening()const` | 当服务端正在监听连接时候返回真,否则返回假 | -| `bool listen( const QHostAddress & address =QHostAddress::Any, quint16 port = 0 )` | 告诉服务端监听所有来自地址为address端口为Port的连接,如果Port为0,那么会自动选择,如果address是QHostAddress::Any,那么服务端监听所有连接,成功返回1,否则返回0 | -| `int maxPendingConnections()const` | 返回最大允许连接数。默认是30 | -| `void setMaxPendingConnections(int numConnections)` | 设定待处理的连接最大数目为numConnections,当超过了最大连接数后,客户端仍旧可以连接服务端,但是服务端不在接受连接,操作系统会把这些链接保存在一个队列中。 | -| `QNetworkProxy proxy()const` | 返回这个套接字的网络代理层。 | -| `void setProxy(const QNetworkProxy & networkProxy)` | 设置这个套接字的网络代理层,进制使用代理时候,使用QNetworkProxy::NoProxy类型,例如server->setProxy(QNetworkProxy::NoProxy); | -| `quint16serverPort()const serverAddress()` | 当服务端正在监听时候,返回服务端的端口和地址 | -| `bool waitForNewConnection(int msec=0,bool *timedOut=0)` | 最大等待msec毫秒或者等待一个新连接可用。如果一个连接可用,返回真,否则返回假。如果msec不等于0,那么超时将会被调用 | - -#### QTcpSocket类 - -QTcpSocket 类提供一个TCP套接字 - -TCP是一个面向连接,可靠的的通信协议,非常适合于连续不断的数据传递 - -QTcpSocket 是QAbstractSocket类非常方便的一个子类,让你创建一个TCP连接和数据流交流。 - -| Header: | #include | -| ------------- | ------------------------------------------------------------ | -| qmake: | QT += network | -| Inherits: | [QAbstractSocket](qabstractsocket.html) | -| Inherited By: | [QSctpSocket](qsctpsocket.html) and [QSslSocket](qsslsocket.html) | - -成员函数 - -| 函数 | 用途 | -| ----------------------------------------------- | ------------------------------------------------------------ | -| QTcpSocket::QTcpSocket ( QObject * parent = 0 ) | 以UnconnectedState态创建一个QTcpSocket对象 | -| QTcpSocket::~QTcpSocket () [virtual]‘ | 析构函数,销毁对象 | -| bool waitForConnected(int *msecs* = 30000) | 等待,直到套接字被连接,最高为msecs毫秒。如果连接已经建立,这个函数返回true;否则返回false。在返回false的情况下,可以调用error()来确定错误的原因。 | -| void connectToHost() | 尝试连接给定端口上的主机名。如果查找成功,则发出hostFound(), QAbstractSocket进入ConnectingState。然后,它尝试连接到查找返回的一个或多个地址。最后,如果连接建立,QAbstractSocket进入ConnectedState并发出connected()。 | -| [signal] void QAbstractSocket::connected() | 这个信号在调用connectToHost()并成功建立连接之后发出。 | -| bool disconnect() | 断开对象发送器中的信号与对象接收器中的方法。如果连接成功断开,返回true;否则返回false。 | -| void QAbstractSocket::disconnectFromHost() | 试图关闭socket。如果有挂起的数据等待写入,QAbstractSocket将进入ClosingState并等待,直到所有的数据都被写入。最终,它将进入UnconnectedState并发出disconnected()信号。 | - - - -#### TcpServer编写 - -##### 创建ui界面 - -![image-20220123152716836](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123152716836.png) - -##### 引入network和两个头文件 - -```properties -QT += core gui network -``` - -```c++ -#include -#include -``` - -##### 声明对象 - -```c++ - //声明tcp所用的对象 - QTcpServer *tcpServer; - QTcpSocket *tcpSocket; -``` - -##### 创建对象`widget.cpp` - -```c++ - //创建对象 - tcpServer = new QTcpServer(this); - tcpSocket = new QTcpSocket(this); -``` - -##### 编写槽函数 - -1. 打开服务器时开启监听 - -调用`listen()`来监听所有的连接,每当一个新的客户端连接到服务端就会**发射信号**`newConnection()` - -2. 有新连接时创建tcpsocket, - -调用`nextPendingConnection()`来接受待处理的连接。返回一个连接的`QTcpSocket()`,我们可以用这个返回的套接字和客户端进行连接 - -3. 读取数据到接收框中 - -`readyRead()`和`readAll()` - -4. 关闭服务器 - -`close()` - -5. 发送数据 - -`write()` - -#### 代码 - -`widget.h` - -```c++ -#ifndef WIDGET_H -#define WIDGET_H - -#include -#include -#include - -namespace Ui { -class Widget; -} - -class Widget : public QWidget -{ - Q_OBJECT - -public: - explicit Widget(QWidget *parent = 0); - ~Widget(); - - //声明tcp所用的对象 - QTcpServer *tcpServer; - QTcpSocket *tcpSocket; - - -private slots: - void on_btnOpenServer_clicked(); - //2.有新连接时创建tcpsocket - void newConnection_Slot(); - //3.读取就绪时读取数据 - void readyRead_SLOT(); - //4. 关闭服务器 - void on_btnCloseServer_clicked(); - //5. 发送数据 - void on_btnSendMessage_clicked(); - -private: - Ui::Widget *ui; -}; - -#endif // WIDGET_H - -``` - -`widget.cpp` - -```c++ -#include "widget.h" -#include "ui_widget.h" - -Widget::Widget(QWidget *parent) : - QWidget(parent), - ui(new Ui::Widget) -{ - ui->setupUi(this); - //创建对象 - tcpServer = new QTcpServer(this); - tcpSocket = new QTcpSocket(this); - - //2.有新连接时创建tcpSocket - connect(tcpServer,SIGNAL(newConnection()),this,SLOT(newConnection_Slot())); - -} - -Widget::~Widget() -{ - delete ui; -} - -//打开服务器 -void Widget::on_btnOpenServer_clicked() -{ - //1.打开监听,所有地址,端口由输入框设定 - tcpServer->listen(QHostAddress::Any,ui->tcpPort->text().toUInt()); -} - -//有新连接时创建tcpsocket -void Widget::newConnection_Slot(){ - //2.调用`nextPendingConnection()`来接受待处理的连接。返回一个连接的`QTcpSocket()` - tcpSocket = tcpServer->nextPendingConnection(); - //3.有数据时,读取tcpSocket数据 - connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(readyRead_SLOT())); -} - -//3.有数据时,读取tcpSocket数据 -void Widget::readyRead_SLOT(){ - QString buff; - buff = tcpSocket->readAll(); - ui->messageRCV->appendPlainText(buff); -} - -//4.关闭服务器 -void Widget::on_btnCloseServer_clicked() -{ - //tcpServer->close(); - tcpSocket->close(); -} - -//5.发送数据 -void Widget::on_btnSendMessage_clicked() -{ - //toLocal8Bit()转化为字符数组,data()转化为字符指针 - tcpSocket->write(ui->editMessageSend->text().toLocal8Bit().data()); -} -``` - -#### 测试 - -![image-20220123161139876](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123161139876.png) - -### TcpClient - -#### QTcpSocket类 - -主要用到QTcpSocket类 - -#### QTcpClient编写 - -1. 添加支持 - -`.pro` - -```properties -QT += core gui network #引入network -``` - -2. 创建UI界面 - -![image-20220123171112644](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123171112644.png) - -3. 声明、创建QTcpSocket对象 - -创建QTcpSocket对象 - -4. 打开连接 - -`connectToHost()` - -5. 连接成功时触发槽函数 - -`connected()`这个信号在调用connectToHost()并成功建立连接之后发出。 - -6. 新数据时,槽函数 - -`readyRead()`有新数据到来时触发 - -7. 发送数据 - -`write()` - -`toLocal8Bit()`转化为字符数组,`data()`转化为字符指针 - -8. 关闭连接 - -`close()` - -#### 代码 - -##### `TcpClient.pro` - -略去 - -##### `widget.h` - -```c++ -#ifndef WIDGET_H -#define WIDGET_H - -#include -//只需要引入socket -#include - -namespace Ui { -class Widget; -} - -class Widget : public QWidget -{ - Q_OBJECT - -public: - explicit Widget(QWidget *parent = 0); - ~Widget(); - //1.socket对象 - QTcpSocket *tcpSocket; - -private slots: - void on_btnConnect_clicked(); - //3.连接成功槽函数 - void connected_Slot(); - //4.新数据到来时触发的槽函数 - void readyRead_Slot(); - //5.发送数据 - void on_btnSend_clicked(); - //6.关闭连接 - void on_btnClose_clicked(); - -private: - Ui::Widget *ui; -}; - -#endif // WIDGET_H -``` - -##### `widget.cpp` - -```c++ -#include "widget.h" -#include "ui_widget.h" -#include - -Widget::Widget(QWidget *parent) : - QWidget(parent), - ui(new Ui::Widget) -{ - ui->setupUi(this); - //1.socket对象 - tcpSocket = new QTcpSocket(this); - -} - -Widget::~Widget() -{ - delete ui; -} - -void Widget::on_btnConnect_clicked() -{ - //2.打开连接 - tcpSocket->connectToHost(ui->editIpInput->text(),ui->editPortInput->text().toUInt()); - if(tcpSocket->waitForConnected(1000)){ - QMessageBox::information(this,"提示","连接成功"); - }else{ - QMessageBox::critical(this,"提示","连接失败"); - } - //3.连接成功时连接函数 - //3.`connected()`这个信号在调用connectToHost()并成功建立连接之后发出。 - connect(tcpSocket,SIGNAL(connected()),this,SLOT(connected_Slot())); -} -//3.连接成功槽函数 -void Widget::connected_Slot(){ - //4.新数据时,连接函数 - //`readyRead()`有新数据到来时触发 - connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(readyRead_Slot())); -} -//4.新数据时槽函数 -//发送数据到接收框 -void Widget::readyRead_Slot(){ - QString buff; - buff = tcpSocket->readAll(); - ui->pteMessageRCV->appendPlainText(buff); -} - -//5.发送数据槽函数 -void Widget::on_btnSend_clicked() -{ - //toLocal8Bit()转化为字符数组,data()转化为字符指针 - tcpSocket->write(ui->editMessageSend->text().toLocal8Bit().data()); -} - - -//6.关闭连接槽函数 -void Widget::on_btnClose_clicked() -{ - tcpSocket->close(); - //tcpSocket->disconnectFromHost(); - QMessageBox::information(this,"提示","断开连接"); -} -``` - -#### 测试 - -![image-20220123170825078](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123170825078.png) - -![image-20220123172226132](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123172226132.png) - -## QT网络编程-UDP通信 - -### 概述 - -UDP不分客户端和服务端 - -只需要使用`QUdpSocket`类 - -### QUdpSocket类 - -#### 判断是否有数据等待读取 - -`bool QUdpSocket::hasPendingDatagrams() const` - -如果至少有一个数据报正在等待读取,则返回true;否则返回false。 - -#### 返回数据报的大小 - -`qint64 QUdpSocket::pendingDatagramSize() const` - -返回第一个挂起的UDP数据报的大小。如果没有可用的数据报,这个函数返回-1。 - -#### 读取数据 - -`qint64 QUdpSocket::readDatagram(char **data*, qint64 *maxSize*, QHostAddress **address* = Q_NULLPTR, quint16 **port* = Q_NULLPTR)` - -接收不大于maxSize字节的数据报,并将其存储在数据中。发送者的主机地址和端口存储在*address和*port中(除非指针为0)。 - -成功时返回数据报的大小;否则返回1。 - -如果maxSize太小,则数据报的其余部分将丢失。为了避免数据丢失,在尝试读取挂起的数据报之前,调用pendingDatagramSize()来确定它的大小。如果maxSize为0,该数据报将被丢弃。 - -#### 绑定端口 - -bool QAbstractSocket::bind(const [QHostAddress](qhostaddress.html) &*address*, [quint16](../qtcore/qtglobal.html#quint16-typedef) *port* = 0, [BindMode](qabstractsocket.html#BindFlag-enum) *mode* = DefaultForPlatform) - -使用BindMode模式在端口端口上绑定到地址。 - -将这个套接字绑定到地址地址和端口端口。 - -对于UDP套接字,绑定后,当UDP数据报到达指定的地址和端口时,就会发出信号QUdpSocket::readyRead()。因此,这个函数对编写UDP服务器很有用。 - -对于TCP套接字,这个函数可以用来指定出连接使用哪个接口,这在有多个网络接口的情况下很有用。 - -默认情况下,套接字使用DefaultForPlatform BindMode绑定。如果不指定端口,则选择随机端口。 - -成功时,函数返回true,套接字进入BoundState;否则返回false。 - -bool QAbstractSocket::bind([quint16](../qtcore/qtglobal.html#quint16-typedef) *port* = 0, [BindMode](qabstractsocket.html#BindFlag-enum) *mode* = DefaultForPlatform) - -这是一个重载函数。 -绑定到QHostAddress:任何端口端口,使用BindMode模式。 -默认情况下,套接字使用DefaultForPlatform BindMode绑定。如果不指定端口,则选择随机端口。 - -#### 发送数据 - -[q](../qtcore/qtglobal.html#qint64-typedef)int64 QUdpSocket::writeDatagram(const char **data*, [qint64](../qtcore/qtglobal.html#qint64-typedef) *size*, const [QHostAddress](qhostaddress.html) &*address*, [quint16](../qtcore/qtglobal.html#quint16-typedef) *port*) - -将数据报以数据大小发送到主机地址在端口的地址端口。返回成功时发送的字节数;否则返回1。 - -数据报总是写成一个块。数据报的最大大小高度依赖于平台,但可以低至8192字节。如果数据报太大,这个函数将返回-1,error()将返回DatagramTooLargeError。 - -发送大于512字节的数据报通常是不建议的,因为即使它们成功发送,它们也可能在到达最终目的地之前被IP层分片。 - -警告:在一个连接的UDP套接字上调用这个函数可能会导致错误和没有发送数据包。如果您正在使用已连接的套接字,请使用write()发送数据报。 - -[q](../qtcore/qtglobal.html#qint64-typedef)int64 QUdpSocket::writeDatagram(const [QNetworkDatagram](qnetworkdatagram.html) &*datagram*) - -这是一个重载函数。 - -使用那里设置的网络接口和跳数限制,将数据报数据报发送到包含在数据报中的主机地址和端口号。如果没有设置目的地址和端口号,这个函数将发送到传递给connectToHost()的地址。 - -如果目的地址是IPv6,范围id非空,但与数据报中的接口索引不同,则操作系统将选择发送哪个接口是未定义的。 - -如果函数成功,则返回发送的字节数;如果遇到错误,则返回-1。 - -警告:在一个连接的UDP套接字上调用这个函数可能会导致错误和没有发送数据包。如果您正在使用已连接的套接字,请使用write()发送数据报。 - -[q](../qtcore/qtglobal.html#qint64-typedef)int64 QUdpSocket::writeDatagram(const [QByteArray](../qtcore/qbytearray.html) &*datagram*, const [QHostAddress](qhostaddress.html) &*host*, [quint16](../qtcore/qtglobal.html#quint16-typedef) *port*) - -这是一个重载函数。 -将数据报数据报发送到主机地址主机和在端口端口。 -如果函数成功,则返回发送的字节数;如果遇到错误,则返回-1。 - -#### 其他 - -` bool QUdpSocket::joinMulticastGroup(const QHostAddress&*groupAddress*)` - -加入操作系统选择的缺省接口上的groupAddress指定的组播组。套接字必须处于BoundState状态,否则将发生错误。 - -注意,如果你试图加入一个IPv4组,你的套接字不能使用IPv6(或双模式,使用QHostAddress::Any)绑定。你必须使用QHostAddress::AnyIPv4代替。 - -如果成功,此函数返回true;否则返回false并设置相应的socket错误。 - -`bool QUdpSocket::joinMulticastGroup(const QHostAddress &*groupAddress*, const QNetworkInterface &*iface*)` - -这是一个重载函数。 - -加入该接口的组播组地址groupAddress。 - -`bool QUdpSocket::leaveMulticastGroup(const QHostAddress &*groupAddress*)` - -将groupAddress指定的组播组保留在操作系统选择的缺省接口上。套接字必须处于BoundState状态,否则将发生错误。 - -如果成功,此函数返回true;否则返回false并设置相应的socket错误。 - -`bool QUdpSocket::leaveMulticastGroup(const QHostAddress &*groupAddress*, const QNetworkInterface &*iface*)` - -这是一个重载函数。 - -离开接口上的groupAddress指定的组播组。 - -`QNetworkInterface QUdpSocket::multicastInterface() const` - -返回组播数据报的出接口的接口。这对应于IPv4套接字的IP_MULTICAST_IF套接字选项和IPv6套接字的IPV6_MULTICAST_IF套接字选项。如果之前没有设置接口,这个函数返回一个无效的QNetworkInterface。套接字必须处于BoundState,否则返回无效的QNetworkInterface。 - -`QNetworkDatagram QUdpSocket::receiveDatagram(qint64 *maxSize* = -1)` - -接收一个不大于maxSize字节的数据报,并在QNetworkDatagram对象中返回它,以及发送方的主机地址和端口。如果可能,此函数还将尝试确定数据报的目的地址、端口和接收时的跳数。 - -失败时,返回一个报告无效的QNetworkDatagram。 - -如果maxSize太小,则数据报的其余部分将丢失。如果maxSize为0,该数据报将被丢弃。如果maxSize为-1(默认值),这个函数将尝试读取整个数据报。 - -`void QUdpSocket::setMulticastInterface(const QNetworkInterface &*iface*)` - -设置组播数据报的出接口为接口interface。这对应于IPv4套接字的IP_MULTICAST_IF套接字选项和IPv6套接字的IPV6_MULTICAST_IF套接字选项。套接字必须处于BoundState,否则这个函数什么都不做。 - - - -### 实例 - -#### 编写界面 - -![image-20220123173536182](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123173536182.png) - -#### 引入支持文件 - -qmake:`network` - -header:`\#include ` - -#### 代码 - -* 创建QUdpSocket对象 - -* 打开连接 - -`bind()` - -`readyRead()` - -* 读取数据 - -`hasPendDatagrams()` - -`hasPendDatagramsSize()` - -`readDatagrams()` - -* 发送数据 - -`writeDatagram()` - -* 关闭 - -##### `widget.h` - -```c++ -#ifndef WIDGET_H -#define WIDGET_H - -#include -//添加头文件Qudpsocket -#include - -namespace Ui { -class Widget; -} - -class Widget : public QWidget -{ - Q_OBJECT - -public: - explicit Widget(QWidget *parent = 0); - ~Widget(); - - //1.创建对象 - QUdpSocket *udpSocket; - -private slots: - //2.打开连接槽函数 - void on_btnOpen_clicked(); - //3.readyRead槽函数 - void readyRead(); - //4.发送 - void on_btnSend_clicked(); - //关闭 - void on_btnClose_clicked(); - -private: - Ui::Widget *ui; -}; - -#endif // WIDGET_H - -``` - -##### `widget.cpp` - -```c++ -#include "widget.h" -#include "ui_widget.h" -#include -#include - -Widget::Widget(QWidget *parent) : - QWidget(parent), - ui(new Ui::Widget) -{ - ui->setupUi(this); - //1.创建对象 - udpSocket = new QUdpSocket(this); - -} - -Widget::~Widget() -{ - delete ui; -} - -void Widget::on_btnOpen_clicked() -{ - //2.打开连接 - //判断是否成功 - if(udpSocket->bind(ui->editLocalPort->text().toUInt()) == true){ - QMessageBox::information(this,"提示","成功"); - }else{ - QMessageBox::critical(this,"提示","失败"); - }; - //3.连接成功触发函数 - connect(udpSocket,SIGNAL(readyRead()),this,SLOT(readyRead())); -} - -void Widget::readyRead(){ - //3.槽函数,读取数据 - while(udpSocket->hasPendingDatagrams()){ //判断是否有数据 - QByteArray ary; - ary.resize(udpSocket->pendingDatagramSize()); - udpSocket->readDatagram(ary.data(),ary.size()); - - QString buff; - buff = ary.data(); - ui->editMessageRCV->appendPlainText(buff); - } -} - -void Widget::on_btnSend_clicked() -{ - //4.发送数据 - //数据 - QString sendBuff; - sendBuff = ui->editMessageSend->text(); - //ip - QHostAddress addr; - addr.setAddress(ui->editTargetIP->text()); - //端口 - quint16 port; - port = ui->editTargetPort->text().toUInt(); - //发送数据 - udpSocket->writeDatagram(sendBuff.toLocal8Bit().data(),sendBuff.length(),addr,port); -} - -void Widget::on_btnClose_clicked() -{ - //5.关闭 - udpSocket->close(); -} - -``` - -#### 测试 - -![image-20220123182754858](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123182754858.png) - - - -## QT定时器 - -```c++ - QTimer *timer = new QTimer(this); - connect(timer, SIGNAL(timeout()), this, SLOT(update())); - timer->start(1000) -``` - - - -## Qt QMessageBox用法详解 - -[c.biancheng.net](http://c.biancheng.net/view/9421.html) - -QMessageBox 是 Qt 框架中常用的一个类,可以生成各式各样、各种用途的消息对话框,如图 1 所示。 - -![](https://gitee.com/tianzhendong/img/raw/master/images/202203240942453.gif) - -图 1 QMessageBox消息对话框 - -很多 GUI 程序都会用到消息对话框,且很多场景中使用的消息对话框是类似的,唯一的区别只是提示信息不同。为了提高程序员的开发效率,避免重复地“造轮子”,Qt 开发者设计好了几种通用的 QMessageBox 消息对话框,需要时可以直接使用。 - -### 通用的QMessageBox消息框 - -Qt 提供了 6 种通用的 QMessageBox 消息对话框,通过调用 QMessageBox 类中的 6 个静态成员方法,可以直接在项目中使用它们。 - -#### 1) information消息对话框 - -information 对话框常用于给用户提示一些关键的信息,它的外观如下图所示: - -![](https://gitee.com/tianzhendong/img/raw/master/images/202203240942112.gif) - -图 2 information 消息对话框 - -在项目中使用 information 消息对话框,直接调用 QMessageBox 类中的 information() 静态成员方法即可,该方法的语法格式如下: - -```c -StandardButton QMessageBox::information(QWidget *parent, - const QString &title, - const QString &text, - StandardButtons buttons = Ok, - StandardButton defaultButton = NoButton) -``` - - - -各个参数的含义是: - -* parent:指定消息对话框的父窗口,消息提示框会作为一个独立的窗口显示在父窗口的前面。消息提示框从弹出到关闭的整个过程中,用户无法操作父窗口,更不能删除父窗口; -* title:指定消息对话框的标题,即图 2 中的 Titile; -* text:指定消息对话框的具体内容,即图 2 中的 text; -* buttons:指定消息对话框中包含的按钮。默认情况下,消息对话框只包含一个按钮,即图 2 中显示的 "OK" 按钮。根据需要,我们可以用`|`按位或运算符在消息对话框中设置多个按钮,例如 `QMessageBox::Ok|QMessageBox::Cancel`; -* defaultButton:指定 Enter 回车键对应的按钮,用户按下回车键时就等同于按下此按钮。注意,defaultButton 参数的值必须是 buttons 中包含的按钮,当然也可以不手动指定,QMessageBox 会自动从 buttons 中选择合适的按钮作为 defaultButton 的值。 - -information() 函数会返回用户按下的按钮。StandardButton 是 QMessageBox 类中定义的枚举类型,每个枚举值代表一种按钮。StandardButton 类型中的值有很多,下表给大家罗列了几个常用的: - -表 1 QMessageBox::StandardButton 枚举类型值 - -| 枚举值 | 含 义 | -| -------------------- | ------------------------------------------------------------ | -| QMessageBox::Ok | 标有 "OK" 字样的按钮,通常用来表示用户接受或同意提示框中显示的信息。 | -| QMessageBox::Open | 标有 "Open" 字样的按钮。 | -| QMessageBox::Save | 标有 "Save" 字样的按钮。 | -| QMessageBox::Cancel | 标有 "Cancel" 字样的按钮。点击此按钮,通常表示用户拒绝接受提示框中显示的信息。 | -| QMessageBox::Close | 标有 "Close" 字样的按钮。 | -| QMessageBox::Discard | 标有 "Discard" 或者 "Don't Save" 字样的按钮,取决于运行平台。 | -| QMessageBox::Apply | 标有 "Apply" 字样的按钮。 | -| QMessageBox::Reset | 标有 "Reset" 字样的按钮。 | -| QMessageBox::Yes | 标有 "Yes" 字样的按钮。 | -| QMessageBox::No | 标有 "No" 字样的按钮。 | - -例如,使用 information() 函数实现图 2 所示的对话框,实现代码为: - - ```c - QMessageBox::StandardButton result = QMessageBox::information(&widget, "Title","text"); - ``` - - - - -其中,widget 是我们创建好的 QWidget 窗口,创建好的 information 对话框会显示在 widget 窗口的前面。通过用 result 接收 information() 函数的返回值,我们可以得知用户选择的是哪个按钮。 - -#### 2) critical消息对话框 - -critical 消息对话框常用于给用户提示“操作错误”或“运行失败”的信息,它的外观如下图所示: - -![](https://gitee.com/tianzhendong/img/raw/master/images/202203240943807.gif) - -图 3 critical 消息对话框 - -项目中使用 critical 消息对话框,直接调用 QMessageBox 类提供的 critical() 静态成员方法即可,该方法的语法格式为: - -```c -StandardButton QMessageBox::critical(QWidget *parent, - const QString &title, - const QString &text, - StandardButtons buttons = Ok, - StandardButton defaultButton = NoButton) -``` - - - -各个参数的含义以及返回值的含义,都与 information() 函数相同,这里不再重复赘述。 - -例如,使用 critical() 函数实现图 3 所示的对话框,实现代码为: - - ```c - QMessageBox::StandardButton result=QMessageBox::critical(&widget, "Title","text"); - ``` - - - - -其中,widget 是我们创建好的 QWidget 窗口,创建好的 critical 对话框会显示在 widget 窗口的前面。 - -#### 3) question消息对话框 - -question 对话框常用于向用户提出问题并接收用户的答案,它的外观如下图所示: - -![](https://gitee.com/tianzhendong/img/raw/master/images/202203240943988.gif) - -- 图 4 question消息对话框 - -项目中使用 question 对话框,可以直接调用 QMessageBox 类的 question() 静态成员方法,该方法的语法格式为: - -```c -StandardButton QMessageBox::question(QWidget *parent, - const QString &title, - const QString &text, - StandardButtons buttons = StandardButtons( Yes | No ), - StandardButton defaultButton = NoButton) -``` - -各个参数的含义以及返回值的含义,都与 information() 函数相同。 - -例如,使用 question() 函数实现图 4 所示的对话框,实现代码为: - - ```c - QMessageBox::StandardButton result\=QMessageBox::question(&widget, "Title","text"); - ``` - - - - -其中,widget 是我们创建好的 QWidget 窗口,创建好的 question 对话框会显示在 widget 窗口的前面。 - -#### 4) warning消息对话框 - -warining 对话框常用于向用户显示一些警告信息,它的外观如下图所示: - -![](https://gitee.com/tianzhendong/img/raw/master/images/202203240943182.gif) - -图 5 warning消息对话框 - -项目中使用 warning 对话框,可以直接调用 QMessageBox 类的 warning() 静态成员方法,该方法的语法格式为: - -```c -StandardButton QMessageBox::warning(QWidget *parent, - const QString &title, - const QString &text, - StandardButtons buttons = Ok, - StandardButton defaultButton = NoButton) -``` - -各个参数的含义以及返回值的含义,都与 information() 函数相同。 - -例如,使用 warning() 函数实现图 5 所示的对话框,实现代码为: - - ```c - QMessageBox::StandardButton result=QMessageBox::warning(&widget, "Title","text"); - ``` - - -其中,widget 是我们创建好的 QWidget 窗口,创建好的 warning 对话框会显示在 widget 窗口的前面。 - -#### 5) about和aboutQt对话框 - -about 对话框常常作为介绍某个产品或某项功能的临时窗口,它的外观如下图所示: - -![](https://gitee.com/tianzhendong/img/raw/master/images/202203240943218.gif) - -图 6 about消息对话框 - -注意,about 对话框没有固定的图标,它显示的图标可能来自父窗口、包含父窗口的顶层窗口等,也可能使用和 information 对话框相同的图标。 - -项目中使用 about 对话框,直接调用 QMessageBox 类提供的 about() 静态成员方法即可,该方法的语法格式如下: - -void QMessageBox::about(QWidget \*parent, const QString &title, const QString &text) - -各个参数的含义和与 information() 函数相同。和前面的几种对话框不同,about对话框中只包含一个默认的 Ok 按钮,且 about() 函数没有返回值。 - -aboutQt 可以看做是 about 对话框的一个具体实例,它只能显示 Qt 的介绍信息,如下图所示: - -![](https://gitee.com/tianzhendong/img/raw/master/images/202203240943559.gif) - -图 7 aboutQt对话框 - -项目中使用 aboutQt 对话框,直接调用 QMessageBox 类提供的 aboutQt() 静态成员方法即可,该函数的语法格式如下: - -void QMessageBox::aboutQt(QWidget \*parent, const QString &title = QString()) - -我们只能设置 aboutQt 对话框的 parent 父窗口和 title 标题,不能自定义它的内容。所以在实际场景中,aboutQt() 对话框很少使用。 - -### 自定义QMessageBox对话框 - -以上 6 种通用的 QMessageBox 对话框,界面上的图片无法修改,按钮上的文字也无法修改(例如无法将 OK、No 改成中文)。如果想修改它们,就需要自定义一个 QMessageBox 对话框。 - -#### QMessageBox对话框的创建 - -程序中创建 QMessageBox 对象,必须先引入``头文件。QMessageBox 类提供了两个构造函数,分别是: - -```c -QMessageBox::QMessageBox(QWidget *parent = Q_NULLPTR) -QMessageBox::QMessageBox(Icon icon, - const QString &title, - const QString &text, - StandardButtons buttons = NoButton, - QWidget *parent = Q_NULLPTR, - Qt::WindowFlags f = Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint) -``` - - - -第一个构造函数可以创建一个“空白”对话框,即对话框中不包含任何文本和按钮。当然,通过调用 QMessageBox 类提供的成员方法,可以向“空白”对话框中添加各种元素(图标、文本、按钮等)。 - -第二个构造函数中,各个参数的含义是: - -* icon:指定对话框中的图片。Icon 是 QMessageBox 类中定义的枚举类型,内部包含 QMessageBox::NoIcon、QMessageBox::Question、QMessageBox::Information、QMessageBox::Warning、QMessageBox::Critical 几个值,分别表示:不指定图片、question对话框的图片(图 4)、information对话框的图片(图 2)、warning对话框的图片(图 5)、critical对话框的图片(图 3)。 -* title:指定对话框的标题; -* text:指定对话框中显示的文本信息; -* buttons:指定对话框中包含的的按钮,可选值如表 1 所示。 -* parent:指定对话框的父窗口; -* f:指定对话框的属性。WindowFlags 是 Qt 提供的枚举类型,内部包含的值有很多,有的用来指定对话框的用途(比如 Qt::Dialog 表示对话框窗口),有的用来指定对话框的外观(比如 MSWindowsFixedSizeDialogHint 表示给对话框添加一个细的边框) - -举个简单的例子: - - ```c - #include - #include - int main(int argc, char *argv[]) - { - QApplication a(argc, argv); - //创建 QMessageBox 类对象 - QMessageBox MyBox(QMessageBox::Question,"Title","text",QMessageBox::Yes|QMessageBox::No); - //使 MyBox 对话框显示 - MyBox.exec(); - return a.exec(); - } - ``` - - - - -程序中创建了一个 MyBox 对话框,通过调用 QMessageBox 提供的 exec() 方法,可以使 MyBox 对话框弹出。运行程序可以发现,MyBox 对话框的外观和图 4 的 question 对话框完全一样。 - -#### QMessageBox对话框的使用 - -QMessageBox 类提供了很多功能实用的成员方法,方便我们快速地制作出实际场景需要的对话框。 - -下表给大家罗列了常用的一些 QMessageBox 类成员方法: - -表 2 QMessageBox 常用成员方法 - -| 成员方法 | 功 能 | -| ------------------------------------------------------------ | ------------------------------------------------------------ | -| void QMessageBox::setWindowTitle(const QString &title) | 设置对话框的标题。 | -| void setText(const QString &text) | 设置对话框中要显示的文本。 | -| void setIconPixmap(const QPixmap &pixmap) | 设置对话框中使用的图片。 | -| QAbstractButton \*QMessageBox::clickedButton() const | 返回用户点击的按钮。 | -| QPushButton \*QMessageBox::addButton(const QString &text, ButtonRole role) | 向对话框中添加按钮,text 为按钮的文本,role 是 QMessageBox::ButtonRole 枚举类型的变量,用于描述按钮扮演的角色,它的可选值有 QMessageBox::AcceptRole(同 OK 按钮)、QMessageBox::RejectRole(同 Cancel 按钮)等。 | -| int QMessageBox::exec() | 使当前对话框弹出,除非用户关闭对话框,否则对话框将一直存在。此外,当对话框中使用的都是 Qt 提供的按钮时,该方法可以监听用户点击的是哪个按钮,并将该按钮对应的枚举值返回;如果对话框中包含自定义按钮,需要借助 clickedButton() 方法确定用户点击的按钮。 | - -举个简单的例子: - -```c -#include -#include -#include -#include -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - - QMessageBox MBox; - MBox.setWindowTitle("QMessageBox自定义对话框"); - MBox.setText("这是一个自定义的对话框"); - MBox.setIconPixmap(QPixmap("C:\\Users\\xiexuewu\\Desktop\\icon_c.png")); - QPushButton *agreeBut = MBox.addButton("同意", QMessageBox::AcceptRole); - MBox.exec(); - if (MBox.clickedButton() == (QAbstractButton*)agreeBut) { - //在 Qt Creator 的输出窗口中输出指定字符串 - qDebug() << "用户点击了同意按钮"; - } - return a.exec(); -} -``` - - - - -程序运行结果如图 8 所示,点击“同意”按钮后,我们会在 Qt Creator 的输出窗口中看到“用户点击了同意按钮”。 - -![](https://gitee.com/tianzhendong/img/raw/master/images/202203240943937.gif) - -图 8 自定义的 QMessageBox 对话框 - -#### QMessageBox的信号和槽 - -操作 QMessageBox 对话框,最常用的信号函数是 buttonClicked() 函数,最常用的槽函数是 exec() 函数,它们的语法格式和功能如下表所示。 - -表 3 QMessageBox信号和槽 - -| 信号函数 | 功 能 | -| --------------------------------------------------------- | ------------------------------------------------------------ | -| void QMessageBox::buttonClicked(QAbstractButton \*button) | 当用户点击对话框中的某个按钮时,会触发此信号函数,该函数会将用户点击的按钮作为参数传递给槽函数。 | -| 槽函数 | 功 能 | -| int QMessageBox::exec() | 弹出对话框,直到用户手动关闭对话框,此对话框将一直存在。 | - -举个简单的例子: - -```c -//main.cpp -#include -#include -#include -#include -#include -QPushButton* agreeBut; -QPushButton* disagreeBut; -class MyWidget:public QWidget{ - Q_OBJECT -public slots: - void buttonClicked(QAbstractButton * butClicked); -}; - -void MyWidget::buttonClicked(QAbstractButton * butClicked){ - if(butClicked == (QAbstractButton*)disagreeBut){ - this->close(); - } -} - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - //创建主窗口 - MyWidget myWidget; - myWidget.setWindowTitle("主窗口"); - myWidget.resize(400,300); - - //创建消息框 - QMessageBox MyBox(QMessageBox::Question,"",""); - MyBox.setParent(&myWidget); - //设置消息框的属性为对话框,它会是一个独立的窗口 - MyBox.setWindowFlags(Qt::Dialog); - MyBox.setWindowTitle("协议"); - MyBox.setText("使用本产品,请您严格遵守xxx规定!"); - //自定义两个按钮 - agreeBut = MyBox.addButton("同意", QMessageBox::AcceptRole); - disagreeBut = MyBox.addButton("拒绝", QMessageBox::RejectRole); - - myWidget.show(); - //添加信号和槽,监听用户点击的按钮,如果用户拒绝,则主窗口随之关闭。 - QObject::connect(&MyBox,&QMessageBox::buttonClicked,&myWidget,&MyWidget::buttonClicked); - MyBox.exec(); - - return a.exec(); -} -//MyWidget类的定义应该放到 .h 文件中,本例中将其写到 main.cpp 中,程序最后需要添加 #include "当前源文件名.moc" 语句,否则无法通过编译。 -#include "main.moc" -``` - - -程序执行结果为: - -![](https://gitee.com/tianzhendong/img/raw/master/images/202203240943430.gif) - -[查看原网页: c.biancheng.net](http://c.biancheng.net/view/9421.html) diff --git a/source/_posts/C_C++/Socket编程.md b/source/_posts/C_C++/Socket编程.md deleted file mode 100644 index 2a2cb8a..0000000 --- a/source/_posts/C_C++/Socket编程.md +++ /dev/null @@ -1,404 +0,0 @@ ---- -title: socket编程-转载 -date: 2022年4月28日 -author: TianZD -top: true -cover: true -toc: true -mathjax: false -summary: >- - socket编程是网络常用的编程,我们通过在网络中创建socket关键字来实现网络间的通信,通过收集大量的资料,通过这一章节,充分的了解socket编程,文章用引用了大量大神的分析,加上自己的理解,做个总结性的文章【转载】 -tags: - - socket - - 编程 - - 网络编程 -categories: - - 网络编程 -abbrlink: 79a3d743 -reprintPolicy: cc_by -coverImg: -img: -password: ---- - -# socket技术详解(看清socket编程) - -转自[www.cnblogs.com](https://www.cnblogs.com/fengff/p/10984251.html) - -https://blog.csdn.net/weixin_39634961/article/details/80236161 - -socket编程是网络常用的编程,我们通过在网络中创建socket关键字来实现网络间的通信,通过收集大量的资料,通过这一章节,充分的了解socket编程,文章用引用了大量大神的分析,加上自己的理解,做个总结性的文章 - -## socket大致介绍 - - socket编程是一门技术,它主要是在网络通信中经常用到 - - 既然是一门技术,由于现在是面向对象的编程,一些计算机行业的大神通过抽象的理念,在现实中通过反复的理论或者实际的推导,提出了抽象的一些通信协议,基于tcp/ip协议,提出大致的构想,一些泛型的程序大牛在这个协议的基础上,将这些抽象化的理念接口化,针对协议提出的每个理念,专门的编写制定的接口,与其协议一一对应,形成了现在的socket标准规范,然后将其接口封装成可以调用的接口,供开发者使用 - - 目前,开发者开发出了很多封装的类来完善socket编程,都是更加方便的实现刚开始socket通信的各个环节,所以我们首先必须了解socket的通信原理,只有从本质上理解socket的通信,才可能快速方便的理解socket的各个环节,才能从底层上真正的把握 - -## TCP/IP协议 - - 要理解socket必须的得理解tcp/ip,它们之间好比送信的线路和驿站的作用,比如要建议送信驿站,必须得了解送信的各个细节。 - - TCP/IP协议不同于iso的7个分层,它是根据这7个分层,将其重新划分,好比打扫卫生,本来有扫帚,垃圾斗,抹布,涂料,盆栽等就好比OSI的标准几个分层,tcp/ip根据用途和功能,将扫帚,垃圾斗放到粗略整理层,抹布涂料放到中度整理层,盆栽放到最终效果层。这里TCP/IP也对OSI的网络模型层进行了划分:大致如下: - -OSI模型: - -![image-20220320151509611](https://gitee.com/tianzhendong/img/raw/master/images/202203240941729.png) - -TCP/IP协议参考模型把所有的TCP/IP系列协议归类到四个抽象层中 - -应用层:TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 等等 - -传输层:TCP,UDP - -网络层:IP,ICMP,OSPF,EIGRP,IGMP - -数据链路层:SLIP,CSLIP,PPP,MTU - -每一抽象层建立在低一层提供的服务上,并且为高一层提供服务,看起来大概是这样子的- - -![image-20220320151522484](https://gitee.com/tianzhendong/img/raw/master/images/202203240941951.png) - -通过上面的图形,由于底一层的需要向高一层的提供服务,我们大致的理解应用程序需要传输层的tcp和网络层的ip协议提供服务,但是我们这章要分析的socket它是在tcpip协议的那一部分呢,就好比,我们的通讯线路已经有明确的规定,我们的驿站要设计在哪个地方一样 - -## 回过头再来理解socket - - 到目前为止,大致的了解了应用程序和tcpip协议的大致关系,我们只是知道socket编程是在tcp/IP上的网络编程,但是socket在上述的模型的什么位置呢。这个位置被一个天才的理论家或者是抽象的计算机大神提出并且安排出来 - -![image-20220320151533041](https://gitee.com/tianzhendong/img/raw/master/images/202203240942241.png) - -我们可以发现socket就在应用程序的传输层和应用层之间,设计了一个socket抽象层,传输层的底一层的服务提供给socket抽象层,socket抽象层再提供给应用层,问题又来了,应用层和socket抽象层之间和传输层,网络层之间如何通讯的呢,了解这个之前,我们还是回到原点 - - 要想理解socket编程怎么通过socket关键词实现服务器和客户端通讯,必须得实现的了解tcp/ip是怎么通讯的,在这个的基础上在去理解socket的握手通讯 - - 在tcp/ip协议中,tcp通过三次握手建立起一个tcp的链接,大致如下 - - 第一次握手:客户端尝试连接服务器,向服务器发送syn包,syn=j,客户端进入SYN\_SEND状态等待服务器确认 - - 第二次握手:服务器接收客户端syn包并确认(ack=j+1),同时向客户端发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN\_RECV状态 - - 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手 - - 三次握手如下图: - -![image-20220320151547615](https://gitee.com/tianzhendong/img/raw/master/images/202203240942948.png) - - 根据tcp的三次握手,socket也定义了三次握手,也许是参考tcp的三次握手,一些计算机大神们画出了socket的三次握手的模型图 - - 模型图如下: - -![image-20220320151603528](https://gitee.com/tianzhendong/img/raw/master/images/202203240942520.png) - - 在上面图的基础上,如果我们得到上面的图形,需要我们自己开发一些接口,来满足上面的通讯的三次握手,问题就出来了,我们会需要开发哪些函数 - -## socket的一些接口函数原理 - - 通过上面的图,我们清楚,我们好比一些泛型的程序员,一些理论提供者提供给了我们上面的图形的理论,我们需要做的就是讲上面的图形的抽象化的东西具体化 - - 第一次握手:客户端需要发送一个syn j 包,试着去链接服务器端,于是客户端我们需要提供一个链接函数 - - 第二次握手:服务器端需要接收客户端发送过来的syn J+1 包,然后在发送ack包,所以我们需要有服务器端接受处理函数 - - 第三次握手:客户端的处理函数和服务器端的处理函数 - - 三次握手只是一个数据传输的过程,但是,我们传输前需要一些准备工作,比如将创建一个套接字,收集一些计算机的资源,将一些资源绑定套接字里面,以及接受和发送数据的函数等等,这些功能接口在一起构成了socket的编程 - - 下面大致的按照客户端和服务端将所需的函数详细的列举出来 - -![image-20220320151644780](https://gitee.com/tianzhendong/img/raw/master/images/202203240942432.png) - -![image-20220320151652988](https://gitee.com/tianzhendong/img/raw/master/images/202203240942458.png)- - -上面的两个图都概述了socket的通讯原理 - -## socket的一个例子,总结上述的问题 - -详细就不在说明,通过一段代码详细的解释 - -客户端的代码: - -```c -#include -#include -#pragma comment(lib,"ws2_32.lib") -int main() -{ - //SOCKET前的一些检查,检查协议库的版本,为了避免别的版本的socket,并且通过 - //WSAStartup启动对应的版本,WSAStartup的参数一个是版本信息,一个是一些详细的细节,注意高低位 - //WSAStartup与WSACleanup对应 - int err; - WORD versionRequired; - WSADATA wsaData; - versionRequired=MAKEWORD(1,1); - err=WSAStartup(versionRequired,&wsaData);//协议库的版本信息 - - //通过WSACleanup的返回值来确定socket协议是否启动 - if (!err) - { - printf("客户端嵌套字已经打开!\n"); - } - else - { - printf("客户端的嵌套字打开失败!\n"); - return 0;//结束 - } - //创建socket这个关键词,这里想一下那个图形中的socket抽象层 - //注意socket这个函数,他三个参数定义了socket的所处的系统,socket的类型,以及一些其他信息 - SOCKET clientSocket=socket(AF_INET,SOCK_STREAM,0); - - //socket编程中,它定义了一个结构体SOCKADDR_IN来存计算机的一些信息,像socket的系统, - //端口号,ip地址等信息,这里存储的是服务器端的计算机的信息 - SOCKADDR_IN clientsock_in; - clientsock_in.sin_addr.S_un.S_addr=inet_addr("127.0.0.1"); - clientsock_in.sin_family=AF_INET; - clientsock_in.sin_port=htons(6000); - - //前期定义了套接字,定义了服务器端的计算机的一些信息存储在clientsock_in中, - //准备工作完成后,然后开始将这个套接字链接到远程的计算机 - //也就是第一次握手 - - connect(clientSocket,(SOCKADDR*)&clientsock_in,sizeof(SOCKADDR));//开始连接 - - - char receiveBuf[100]; - - //解释socket里面的内容 - recv(clientSocket,receiveBuf,101,0); - printf("%s\n",receiveBuf); - - //发送socket数据 - send(clientSocket,"hello,this is client",strlen("hello,this is client")+1,0); - - //关闭套接字 - closesocket(clientSocket); - //关闭服务 - WSACleanup(); - return 0; -} - -``` - - -对应的服务端的代码 - -```c -#include -#include -#pragma comment(lib,"ws2_32.lib") -int main() -{ - //创建套接字,socket前的一些检查工作,包括服务的启动 - WORD myVersionRequest; - WSADATA wsaData; - myVersionRequest=MAKEWORD(1,1); - int err; - err=WSAStartup(myVersionRequest,&wsaData); - if (!err) - { - printf("已打开套接字\n"); - } - else - { - //进一步绑定套接字 - printf("嵌套字未打开!"); - return 0; - } - SOCKET serSocket=socket(AF_INET,SOCK_STREAM,0);//创建了可识别套接字 - //需要绑定的参数,主要是本地的socket的一些信息。 - SOCKADDR_IN addr; - addr.sin_family=AF_INET; - addr.sin_addr.S_un.S_addr=htonl(INADDR_ANY);//ip地址 - addr.sin_port=htons(6000);//绑定端口 - - bind(serSocket,(SOCKADDR*)&addr,sizeof(SOCKADDR));//绑定完成 - listen(serSocket,5);//其中第二个参数代表能够接收的最多的连接数 - - SOCKADDR_IN clientsocket; - int len=sizeof(SOCKADDR); - while (1) - { - //第二次握手,通过accept来接受对方的套接字的信息 - SOCKET serConn=accept(serSocket,(SOCKADDR*)&clientsocket,&len);//如果这里不是accept而是conection的话。。就会不断的监听 - char sendBuf[100]; - sprintf(sendBuf,"welcome %s to bejing",inet_ntoa(clientsocket.sin_addr));//找对对应的IP并且将这行字打印到那里 - //发送信息 - send(serConn,sendBuf,strlen(sendBuf)+1,0); - char receiveBuf[100];//接收 - recv(serConn,receiveBuf,strlen(receiveBuf)+1,0); - printf("%s\n",receiveBuf); - closesocket(serConn);//关闭 - WSACleanup();//释放资源的操作 - } - return 0; -} -``` - - - -## 上面例子用到的知识点 - -(摘抄carter大神文章):- - -服务器端: - -其过程是首先服务器方要先启动,并根据请求提供相应服务: - -(1)打开一通信通道并告知本地主机,它愿意在某一公认地址上的某端口(如FTP的端口可能为21)接收客户请求; - -(2)等待客户请求到达该端口; - -(3)接收到客户端的服务请求时,处理该请求并发送应答信号。接收到并发服务请求,要激活一新进程来处理这个客户请求(如UNIX系统中用fork、exec)。新进程处理此客户请求,并不需要对其它请求作出应答。服务完成后,关闭此新进程与客户的通信链路,并终止。 - -(4)返回第(2)步,等待另一客户请求。 - -(5)关闭服务器 - -客户端: - -(1)打开一通信通道,并连接到服务器所在主机的特定端口; - -(2)向服务器发服务请求报文,等待并接收应答;继续提出请求...... - -(3)请求结束后关闭通信通道并终止。 - -从上面所描述过程可知: - -(1)客户与服务器进程的作用是非对称的,因此代码不同。 - -(2)服务器进程一般是先启动的。只要系统运行,该服务进程一直存在,直到正常或强迫终止。 - -## 下面就介绍一些API函数:(摘抄carter大神文章): - -创建套接字──socket() - -应用程序在使用套接字前,首先必须拥有一个套接字,系统调用socket()向应用程序提供创建套接字的手段,其调用格式如下: - -```c -SOCKET PASCAL FAR socket(int af, int type, int protocol) -``` - - - -该调用要接收三个参数:af、type、protocol。参数af指定通信发生的区域:AF\_UNIX、AF\_INET、AF\_NS等,而DOS、WINDOWS中仅支持AF\_INET,它是网际网区域。因此,地址族与协议族相同。参数type 描述要建立的套接字的类型。这里分三种: - -(1)一是TCP流式套接字(SOCK\_STREAM)提供了一个面向连接、可靠的数据传输服务,数据无差错、无重复地发送,且按发送顺序接收。内设流量控制,避免数据流超限;数据被看作是字节流,无长度限制。文件传送协议(FTP)即使用流式套接字。 - -(2)二是数据报式套接字(SOCK\_DGRAM)提供了一个无连接服务。数据包以独立包形式被发送,不提供无错保证,数据可能丢失或重复,并且接收顺序混乱。网络文件系统(NFS)使用数据报式套接字。 - -(3)三是原始式套接字(SOCK\_RAW)该接口允许对较低层协议,如IP、ICMP直接访问。常用于检验新的协议实现或访问现有服务中配置的新设备。 - -参数protocol说明该套接字使用的特定协议,如果调用者不希望特别指定使用的协议,则置为0,使用默认的连接模式。根据这三个参数建立一个套接字,并将相应的资源分配给它,同时返回一个整型套接字号。因此,socket()系统调用实际上指定了相关五元组中的“协议”这一元。 - -指定本地地址──bind() - -当一个套接字用socket()创建后,存在一个名字空间(地址族),但它没有被命名。bind()将套接字地址(包括本地主机地址和本地端口地址)与所创建的套接字号联系起来,即将名字赋予套接字,以指定本地半相关。其调用格式如下: - -```c -int PASCAL FAR bind(SOCKET s, const struct sockaddr FAR \* name, int namelen); -``` - - - -参数s是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。参数name 是赋给套接字s的本地地址(名字),其长度可变,结构随通信域的不同而不同。namelen表明了name的长度。如果没有错误发生,bind()返回0。否则返回SOCKET\_ERROR。 - -建立套接字连接──connect()与accept() - -这两个系统调用用于完成一个完整相关的建立,其中connect()用于建立连接。accept()用于使服务器等待来自某客户进程的实际连接。 - -connect()的调用格式如下: - -```c -int PASCAL FAR connect(SOCKET s, const struct sockaddr FAR \* name, int namelen); -``` - - - -参数s是欲建立连接的本地套接字描述符。参数name指出说明对方套接字地址结构的指针。对方套接字地址长度由namelen说明。 - -如果没有错误发生,connect()返回0。否则返回值SOCKET\_ERROR。在面向连接的协议中,该调用导致本地系统和外部系统之间连接实际建立。 - -由于地址族总被包含在套接字地址结构的前两个字节中,并通过socket()调用与某个协议族相关。因此bind()和connect()无须协议作为参数。 - -accept()的调用格式如下: - -```c -SOCKET PASCAL FAR accept(SOCKET s, struct sockaddr FAR* addr, int FAR* addrlen); -``` - - - -参数s为本地套接字描述符,在用做accept()调用的参数前应该先调用过listen()。addr 指向客户方套接字地址结构的指针,用来接收连接实体的地址。addr的确切格式由套接字创建时建立的地址族决定。addrlen 为客户方套接字地址的长度(字节数)。如果没有错误发生,accept()返回一个SOCKET类型的值,表示接收到的套接字的描述符。否则返回值INVALID\_SOCKET。 - -accept()用于面向连接服务器。参数addr和addrlen存放客户方的地址信息。调用前,参数addr 指向一个初始值为空的地址结构,而addrlen 的初始值为0;调用accept()后,服务器等待从编号为s的套接字上接受客户连接请求,而连接请求是由客户方的connect()调用发出的。当有连接请求到达时,accept()调用将请求连接队列上的第一个客户方套接字地址及长度放入addr 和addrlen,并创建一个与s有相同特性的新套接字号。新的套接字可用于处理服务器并发请求。 - -四个套接字系统调用,socket()、bind()、connect()、accept(),可以完成一个完全五元相关的建立。socket()指定五元组中的协议元,它的用法与是否为客户或服务器、是否面向连接无关。bind()指定五元组中的本地二元,即本地主机地址和端口号,其用法与是否面向连接有关:在服务器方,无论是否面向连接,均要调用bind(),若采用面向连接,则可以不调用bind(),而通过connect()自动完成。若采用无连接,客户方必须使用bind()以获得一个唯一的地址。 - -监听连接──listen() - -此调用用于面向连接服务器,表明它愿意接收连接。listen()需在accept()之前调用,其调用格式如下: - -```c -int PASCAL FAR listen(SOCKET s, int backlog); -``` - - - -参数s标识一个本地已建立、尚未连接的套接字号,服务器愿意从它上面接收请求。backlog表示请求连接队列的最大长度,用于限制排队请求的个数,目前允许的最大值为5。如果没有错误发生,listen()返回0。否则它返回SOCKET\_ERROR。 - -listen()在执行调用过程中可为没有调用过bind()的套接字s完成所必须的连接,并建立长度为backlog的请求连接队列。 - -调用listen()是服务器接收一个连接请求的四个步骤中的第三步。它在调用socket()分配一个流套接字,且调用bind()给s赋于一个名字之后调用,而且一定要在accept()之前调用。 - -数据传输──send()与recv() - -当一个连接建立以后,就可以传输数据了。常用的系统调用有send()和recv()。 - -send()调用用于s指定的已连接的数据报或流套接字上发送输出数据,格式如下: - -```c -int PASCAL FAR send(SOCKET s, const char FAR \*buf, int len, int flags); -``` - - - -参数s为已连接的本地套接字描述符。buf 指向存有发送数据的缓冲区的指针,其长度由len 指定。flags 指定传输控制方式,如是否发送带外数据等。如果没有错误发生,send()返回总共发送的字节数。否则它返回SOCKET\_ERROR。 - -recv()调用用于s指定的已连接的数据报或流套接字上接收输入数据,格式如下: - -```c -int PASCAL FAR recv(SOCKET s, char FAR \*buf, int len, int flags); -``` - - - -参数s 为已连接的套接字描述符。buf指向接收输入数据缓冲区的指针,其长度由len 指定。flags 指定传输控制方式,如是否接收带外数据等。如果没有错误发生,recv()返回总共接收的字节数。如果连接被关闭,返回0。否则它返回SOCKET\_ERROR。 - -输入/输出多路复用──select() - -select()调用用来检测一个或多个套接字的状态。对每一个套接字来说,这个调用可以请求读、写或错误状态方面的信息。请求给定状态的套接字集合由一个fd\_set结构指示。在返回时,此结构被更新,以反映那些满足特定条件的套接字的子集,同时, select()调用返回满足条件的套接字的数目,其调用格式如下: - -```c -int PASCAL FAR select(int nfds, fd\_set FAR \* readfds, fd\_set FAR \* writefds, fd\_set FAR \* exceptfds, const struct timeval FAR \* timeout); -``` - - - -参数nfds指明被检查的套接字描述符的值域,此变量一般被忽略。 - -参数readfds指向要做读检测的套接字描述符集合的指针,调用者希望从中读取数据。参数writefds 指向要做写检测的套接字描述符集合的指针。exceptfds指向要检测是否出错的套接字描述符集合的指针。timeout指向select()函数等待的最大时间,如果设为NULL则为阻塞操作。select()返回包含在fd\_set结构中已准备好的套接字描述符的总数目,或者是发生错误则返回SOCKET\_ERROR。 - -关闭套接字──closesocket() - -closesocket()关闭套接字s,并释放分配给该套接字的资源;如果s涉及一个打开的TCP连接,则该连接被释放。closesocket()的调用格式如下: - -```c -BOOL PASCAL FAR closesocket(SOCKET s); -``` - - - -参数s待关闭的套接字描述符。如果没有错误发生,closesocket()返回0。否则返回值SOCKET\_ERROR。 - -[查看原网页: www.cnblogs.com](https://www.cnblogs.com/fengff/p/10984251.html) \ No newline at end of file diff --git a/source/_posts/C_C++/assets/dll生成与使用/20200303151508985-16500273210973.png b/source/_posts/C_C++/assets/dll生成与使用/20200303151508985-16500273210973.png deleted file mode 100644 index 8c2564d..0000000 Binary files a/source/_posts/C_C++/assets/dll生成与使用/20200303151508985-16500273210973.png and /dev/null differ diff --git a/source/_posts/C_C++/assets/dll生成与使用/20200303151508985.png b/source/_posts/C_C++/assets/dll生成与使用/20200303151508985.png deleted file mode 100644 index 8c2564d..0000000 Binary files a/source/_posts/C_C++/assets/dll生成与使用/20200303151508985.png and /dev/null differ diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220415081755593.png b/source/_posts/C_C++/assets/dll生成与使用/202204161800737.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/image-20220415081755593.png rename to source/_posts/C_C++/assets/dll生成与使用/202204161800737.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220415104232406.png b/source/_posts/C_C++/assets/dll生成与使用/202204161800779.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/image-20220415104232406.png rename to source/_posts/C_C++/assets/dll生成与使用/202204161800779.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220415080306293.png b/source/_posts/C_C++/assets/dll生成与使用/202204161800789.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/image-20220415080306293.png rename to source/_posts/C_C++/assets/dll生成与使用/202204161800789.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220415082005386.png b/source/_posts/C_C++/assets/dll生成与使用/202204161800823.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/image-20220415082005386.png rename to source/_posts/C_C++/assets/dll生成与使用/202204161800823.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220415210911123.png b/source/_posts/C_C++/assets/dll生成与使用/202204161801001.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/image-20220415210911123.png rename to source/_posts/C_C++/assets/dll生成与使用/202204161801001.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZyZWVraW5nMTAx,size_16,color_FFFFFF,t_70-16500290923565.png b/source/_posts/C_C++/assets/dll生成与使用/202204161801007.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZyZWVraW5nMTAx,size_16,color_FFFFFF,t_70-16500290923565.png rename to source/_posts/C_C++/assets/dll生成与使用/202204161801007.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220415212305795.png b/source/_posts/C_C++/assets/dll生成与使用/202204161801247.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/image-20220415212305795.png rename to source/_posts/C_C++/assets/dll生成与使用/202204161801247.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220415082456066.png b/source/_posts/C_C++/assets/dll生成与使用/202204161801274.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/image-20220415082456066.png rename to source/_posts/C_C++/assets/dll生成与使用/202204161801274.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220415212042047.png b/source/_posts/C_C++/assets/dll生成与使用/202204161801281.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/image-20220415212042047.png rename to source/_posts/C_C++/assets/dll生成与使用/202204161801281.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220415211932141.png b/source/_posts/C_C++/assets/dll生成与使用/202204161801293.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/image-20220415211932141.png rename to source/_posts/C_C++/assets/dll生成与使用/202204161801293.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220415210130710.png b/source/_posts/C_C++/assets/dll生成与使用/202204161801369.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/image-20220415210130710.png rename to source/_posts/C_C++/assets/dll生成与使用/202204161801369.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220415205923849.png b/source/_posts/C_C++/assets/dll生成与使用/202204161801582.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/image-20220415205923849.png rename to source/_posts/C_C++/assets/dll生成与使用/202204161801582.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220415211216385.png b/source/_posts/C_C++/assets/dll生成与使用/202204161801637.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/image-20220415211216385.png rename to source/_posts/C_C++/assets/dll生成与使用/202204161801637.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220415205304326-16500273243314.png b/source/_posts/C_C++/assets/dll生成与使用/202204161801655.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/image-20220415205304326-16500273243314.png rename to source/_posts/C_C++/assets/dll生成与使用/202204161801655.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220415131004040.png b/source/_posts/C_C++/assets/dll生成与使用/202204161801691.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/image-20220415131004040.png rename to source/_posts/C_C++/assets/dll生成与使用/202204161801691.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220416163646551.png b/source/_posts/C_C++/assets/dll生成与使用/202204161802040.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/image-20220416163646551.png rename to source/_posts/C_C++/assets/dll生成与使用/202204161802040.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220416171503348.png b/source/_posts/C_C++/assets/dll生成与使用/202204161802235.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/image-20220416171503348.png rename to source/_posts/C_C++/assets/dll生成与使用/202204161802235.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220416172129620.png b/source/_posts/C_C++/assets/dll生成与使用/202204161802293.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/image-20220416172129620.png rename to source/_posts/C_C++/assets/dll生成与使用/202204161802293.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220416172258532.png b/source/_posts/C_C++/assets/dll生成与使用/202204161802366.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/image-20220416172258532.png rename to source/_posts/C_C++/assets/dll生成与使用/202204161802366.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220415183057359.png b/source/_posts/C_C++/assets/dll生成与使用/202204161802422.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/image-20220415183057359.png rename to source/_posts/C_C++/assets/dll生成与使用/202204161802422.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZyZWVraW5nMTAx,size_16,color_FFFFFF,t_70-16500292084187.png b/source/_posts/C_C++/assets/dll生成与使用/202204161802483.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZyZWVraW5nMTAx,size_16,color_FFFFFF,t_70-16500292084187.png rename to source/_posts/C_C++/assets/dll生成与使用/202204161802483.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220416172328609.png b/source/_posts/C_C++/assets/dll生成与使用/202204161802541.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/image-20220416172328609.png rename to source/_posts/C_C++/assets/dll生成与使用/202204161802541.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220416163629764.png b/source/_posts/C_C++/assets/dll生成与使用/202204161802629.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/image-20220416163629764.png rename to source/_posts/C_C++/assets/dll生成与使用/202204161802629.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220416172227912.png b/source/_posts/C_C++/assets/dll生成与使用/202204161802638.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/image-20220416172227912.png rename to source/_posts/C_C++/assets/dll生成与使用/202204161802638.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220416175017723.png b/source/_posts/C_C++/assets/dll生成与使用/202204161802804.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/image-20220416175017723.png rename to source/_posts/C_C++/assets/dll生成与使用/202204161802804.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220415183130534.png b/source/_posts/C_C++/assets/dll生成与使用/202204161802830.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/image-20220415183130534.png rename to source/_posts/C_C++/assets/dll生成与使用/202204161802830.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220416163559637.png b/source/_posts/C_C++/assets/dll生成与使用/202204161802876.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/image-20220416163559637.png rename to source/_posts/C_C++/assets/dll生成与使用/202204161802876.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220416172524995.png b/source/_posts/C_C++/assets/dll生成与使用/202204161808242.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/image-20220416172524995.png rename to source/_posts/C_C++/assets/dll生成与使用/202204161808242.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220415075952640.png b/source/_posts/C_C++/assets/dll生成与使用/202204280931661.png similarity index 100% rename from source/_posts/C_C++/assets/dll生成与使用/image-20220415075952640.png rename to source/_posts/C_C++/assets/dll生成与使用/202204280931661.png diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220415082158220.png b/source/_posts/C_C++/assets/dll生成与使用/image-20220415082158220.png deleted file mode 100644 index 2a1bd2e..0000000 Binary files a/source/_posts/C_C++/assets/dll生成与使用/image-20220415082158220.png and /dev/null differ diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220415205304326.png b/source/_posts/C_C++/assets/dll生成与使用/image-20220415205304326.png deleted file mode 100644 index 3912f5f..0000000 Binary files a/source/_posts/C_C++/assets/dll生成与使用/image-20220415205304326.png and /dev/null differ diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220416163601291.png b/source/_posts/C_C++/assets/dll生成与使用/image-20220416163601291.png deleted file mode 100644 index e47a7e3..0000000 Binary files a/source/_posts/C_C++/assets/dll生成与使用/image-20220416163601291.png and /dev/null differ diff --git a/source/_posts/C_C++/assets/dll生成与使用/image-20220416172434762.png b/source/_posts/C_C++/assets/dll生成与使用/image-20220416172434762.png deleted file mode 100644 index 071663e..0000000 Binary files a/source/_posts/C_C++/assets/dll生成与使用/image-20220416172434762.png and /dev/null differ diff --git a/source/_posts/C_C++/assets/dll生成与使用/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZyZWVraW5nMTAx,size_16,color_FFFFFF,t_70.png b/source/_posts/C_C++/assets/dll生成与使用/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZyZWVraW5nMTAx,size_16,color_FFFFFF,t_70.png deleted file mode 100644 index b3a833e..0000000 Binary files a/source/_posts/C_C++/assets/dll生成与使用/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZyZWVraW5nMTAx,size_16,color_FFFFFF,t_70.png and /dev/null differ diff --git a/source/_posts/C_C++/dll生成与使用.assets/202204161759934.png b/source/_posts/C_C++/dll生成与使用.assets/202204161759934.png deleted file mode 100644 index 18df411..0000000 Binary files a/source/_posts/C_C++/dll生成与使用.assets/202204161759934.png and /dev/null differ diff --git a/source/_posts/C_C++/dll生成与使用.md b/source/_posts/C_C++/dll生成与使用.md index 80e7044..e5e61a7 100644 --- a/source/_posts/C_C++/dll生成与使用.md +++ b/source/_posts/C_C++/dll生成与使用.md @@ -25,7 +25,7 @@ password: ## DLL概述 -概述内容[来自博客](https://blog.csdn.net/elaine_bao/article/details/51784864?spm=1001.2101.3001.6650.6&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-6.pc_relevant_default&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-6.pc_relevant_default&utm_relevant_index=12) +本节内容[来自博客](https://blog.csdn.net/elaine_bao/article/details/51784864?spm=1001.2101.3001.6650.6&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-6.pc_relevant_default&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-6.pc_relevant_default&utm_relevant_index=12) 它们是一些独立的文件,其中包含能被可执行程序或其他dll调用来完成某项工作的函数,只有在其他模块调用dll中的函数时,dll才发挥作用。 @@ -63,13 +63,13 @@ password: C++库-类型选择shared -![image-20220415075952640](https://gitee.com/tianzhendong/img/raw/master/images/202204280931661.png) +![image-20220415075952640](./assets/dll生成与使用/202204280931661.png) #### 新建文件 右键项目,新建C/C++源文件(或者直接新建一个类),并勾选创建关联头 -![image-20220415080306293](https://gitee.com/tianzhendong/img/raw/master/images/202204161800789.png) +![image-20220415080306293](./assets/dll生成与使用/202204161800789.png) #### 编写c++ @@ -140,7 +140,7 @@ int Student::getAge() { 在debug目录下生成了.dll、.lib文件,后续主要用到.dll、.lib和.h文件 -![image-20220415081755593](https://gitee.com/tianzhendong/img/raw/master/images/202204161800737.png) +![image-20220415081755593](./assets/dll生成与使用/202204161800737.png) ### 测试 @@ -170,13 +170,13 @@ dumpbin -exports Dll1.dll #### 新建项目 -![image-20220415082005386](https://gitee.com/tianzhendong/img/raw/master/images/202204161800823.png) +![image-20220415082005386](./assets/dll生成与使用/202204161800823.png) #### 在项目根目录新建lib文件夹 将上述生成的.dll .lib 和编写的.h文件复制到lib中 -![image-20220415104232406](https://gitee.com/tianzhendong/img/raw/master/images/202204161800779.png) +![image-20220415104232406](./assets/dll生成与使用/202204161800779.png) #### cmakelist.txt添加: @@ -203,7 +203,7 @@ target_link_libraries(${PROJECT_NAME} #### 运行/调试配置: -![image-20220415082456066](https://gitee.com/tianzhendong/img/raw/master/images/202204161801274.png) +![image-20220415082456066](./assets/dll生成与使用/202204161801274.png) #### 调用 @@ -443,7 +443,7 @@ int main() { } ``` -![image-20220415131004040](https://gitee.com/tianzhendong/img/raw/master/images/202204161801691.png) +![image-20220415131004040](./assets/dll生成与使用/202204161801691.png) @@ -516,11 +516,11 @@ int main() { 选择`具有导出项的DLL动态链接库` -![image-20220415205304326](https://gitee.com/tianzhendong/img/raw/master/images/202204161801655.png) +![image-20220415205304326](./assets/dll生成与使用/202204161801655.png) -![image-20220415205923849](https://gitee.com/tianzhendong/img/raw/master/images/202204161801582.png) +![image-20220415205923849](./assets/dll生成与使用/202204161801582.png) @@ -566,7 +566,7 @@ void hello2() { 在项目中定义.def 文件,该文件为模块导出文件 -![image-20220415210130710](https://gitee.com/tianzhendong/img/raw/master/images/202204161801369.png) +![image-20220415210130710](./assets/dll生成与使用/202204161801369.png) 编写.def @@ -578,11 +578,11 @@ EXPORTS 如果是vs平台,需要在连接器中添加.def文件,如果是通过上述方式添加的.def,会自动添加 -![image-20220415210911123](https://gitee.com/tianzhendong/img/raw/master/images/202204161801001.png) +![image-20220415210911123](./assets/dll生成与使用/202204161801001.png) 然后点击 “ 生成解决方案 ” ,就可以在工程目录的 debug 目录或者 release 目录下(这取决你生成的是debug版本还是release版本)生成了动态链接库的相关文件。第三方调用时关键的文件为 **.lib文件** 和 **.dll文件** 以及工程目录下的 **.h头文件** -![image-20220415211216385](https://gitee.com/tianzhendong/img/raw/master/images/202204161801637.png) +![image-20220415211216385](./assets/dll生成与使用/202204161801637.png) ### 调用 @@ -614,14 +614,14 @@ int main() } ``` -![image-20220415211932141](https://gitee.com/tianzhendong/img/raw/master/images/202204161801293.png) +![image-20220415211932141](./assets/dll生成与使用/202204161801293.png) ##### 在解决方案管理面板中添加头文件和资源文件 添加一个现有项头文件,在文件夹中找到第三方库的头文件( .h ),添加进新建立的项目。 添加一个现有项资源文件,在文件夹中找到第三方库的库文件( .lib ),添加进新建立的项目。 -![image-20220415212042047](https://gitee.com/tianzhendong/img/raw/master/images/202204161801281.png) +![image-20220415212042047](./assets/dll生成与使用/202204161801281.png) ```c #include @@ -655,13 +655,13 @@ int main() VS2019 自带的工具 dumpbin.exe 可以查看编译后的 动态链接库对应的 函数名。 -![image-20220415212305795](https://gitee.com/tianzhendong/img/raw/master/images/202204161801247.png) +![image-20220415212305795](./assets/dll生成与使用/202204161801247.png) 打开命令行,输入命令 `dumpbin -exports Dll2.dll` 这里采用博客中的内容: -![img](https://gitee.com/tianzhendong/img/raw/master/images/202204161801007.png) +![img](./assets/dll生成与使用/202204161801007.png) 所以 addFunc 不能直接使用,只能用被 name Mangling 后的名字 ,这里 **addFunc** 编译后的名字是 **?addFunc@@YAHHH@Z** @@ -709,12 +709,13 @@ int main() FreeLibrary(hModule); } ``` + ##### 使用lib和dll文件 * 把 **CPPDLL.dll** 文件放到 debug 目录下, * 然后在项目中引入 **CPPDLL.lib** 文件。 **链接器** -> **输入** -> **附加依赖项** -> **编辑** -![img](https://gitee.com/tianzhendong/img/raw/master/images/202204161802483.png) +![img](./assets/dll生成与使用/202204161802483.png) ```c #include @@ -750,6 +751,8 @@ int main() 这样就不用 手动设置 添加 lib 文件了 + + #### 多层调用 只需要把多层的dll都放进去即可 @@ -878,11 +881,11 @@ void Student2::student2Hello() { ### 建立netframework项目 -![image-20220415183057359](https://gitee.com/tianzhendong/img/raw/master/images/202204161802422.png) +![image-20220415183057359](./assets/dll生成与使用/202204161802422.png) ### 将两个dll文件放到debug目录下 -![image-20220415183130534](https://gitee.com/tianzhendong/img/raw/master/images/202204161802830.png) +![image-20220415183130534](./assets/dll生成与使用/202204161802830.png) ### 调用 @@ -929,15 +932,15 @@ namespace ConsoleApp3 - 新建dll项目 -![image-20220416163601291](https://gitee.com/tianzhendong/img/raw/master/images/202204161802876.png) +![image-20220416163601291](./assets/dll生成与使用/202204161802876.png) -![image-20220416163629764](https://gitee.com/tianzhendong/img/raw/master/images/202204161802629.png) +![image-20220416163629764](./assets/dll生成与使用/202204161802629.png) -![image-20220416163646551](https://gitee.com/tianzhendong/img/raw/master/images/202204161802040.png) +![image-20220416163646551](./assets/dll生成与使用/202204161802040.png) 生成后的结构如下(这里用的的DllDemo1): -![image-20220416171503348](https://gitee.com/tianzhendong/img/raw/master/images/202204161802235.png) +![image-20220416171503348](./assets/dll生成与使用/202204161802235.png) 其中_global.h自带两个系统默认导出宏 @@ -1009,27 +1012,27 @@ int myadd(int a, int b){ #### 新建c++控制台项目 -![image-20220416172129620](https://gitee.com/tianzhendong/img/raw/master/images/202204161802293.png) +![image-20220416172129620](./assets/dll生成与使用/202204161802293.png) #### 构建项目,生成debug目录 #### 将dll文件放到debug目录 -![image-20220416172227912](https://gitee.com/tianzhendong/img/raw/master/images/202204161802638.png) +![image-20220416172227912](./assets/dll生成与使用/202204161802638.png) #### 将lib文件放到工程目录下 -![image-20220416172258532](https://gitee.com/tianzhendong/img/raw/master/images/202204161802366.png) +![image-20220416172258532](./assets/dll生成与使用/202204161802366.png) #### 右键项目,添加lib库 -![](https://gitee.com/tianzhendong/img/raw/master/images/202204161802541.png) +![](./assets/dll生成与使用/202204161802541.png) **选择外部库** 选择刚才的lib文件,取消平台下的linux,mac,取消为debug版本添加d作为哦后缀,点击完成代码就被添加进call.pro -![image-20220416172524995](https://gitee.com/tianzhendong/img/raw/master/images/202204161808242.png) +![image-20220416172524995](./assets/dll生成与使用/202204161808242.png) #### 调用 @@ -1061,7 +1064,7 @@ int main(int argc, char *argv[]) 右键项目,添加两个.h文件 -![image-20220416175017723](https://gitee.com/tianzhendong/img/raw/master/images/202204161802804.png) +![image-20220416175017723](./assets/dll生成与使用/202204161802804.png) 引入头文件 @@ -1085,4 +1088,4 @@ int main(int argc, char *argv[]) return a.exec(); } -``` +``` \ No newline at end of file diff --git a/source/_posts/C_C++/make、cmake、qmake.md b/source/_posts/C_C++/make、cmake、qmake.md deleted file mode 100644 index 2b7502a..0000000 --- a/source/_posts/C_C++/make、cmake、qmake.md +++ /dev/null @@ -1,99 +0,0 @@ ---- -title: gcc、make、cmake、qmake、make install -date: 2022年4月28日 -author: TianZD -top: true -cover: true -toc: true -mathjax: false -summary: gcc、make、cmake、qmake、make install这么多的make都干了什么【转载】 -tags: - - make - - 编程 -categories: - - "转载" -abbrlink: 84b47928 -reprintPolicy: cc_by -coverImg: -img: -password: ---- -[toc] - -# gcc、make、cmake、qmake、make install这么多的make都干了什么【转载】 - -[www.jianshu.com](https://www.jianshu.com/p/758549cc274d) - -## 前言 - -cmake是一种跨平台的编译工具,比make更为高级,使用起来要方便的多。cmake主要是编写了cmakelists.txt文件,然后用cmake命令将cmakelists.txt文件转化成make所需要的makefile文件,最后用make命令编译源码生成可执行程序或者共享库(so(shared object))。它的作用和qt的qmake是相似的。 - -可以如下图理解: - -configure(配置编译环境) - -cmake=qmake(生成makefile) - -make(从makefile中读取指令,然后编译) - -make install(从makefile中读取指令,安装程序) - -* * * - -## 那gcc/g++在这个过程中干了什么呢 - -我们知道编译和链接阶段是靠g++和gcc编辑器来完成,但是如果编译和链接的阶段如果源文件太多,一个一个编译时就会特别麻烦,于是人们想到,为什么不设计一种类似批处理的程序,来批处理编译源文件呢,于是就有了make工具,它是一个自动化编译工具,你可以使用一条命令实现完全编译。但是你需要编写一个规则文件,make依据它来批处理编译,这个文件就是makefile。 - -\--------------------- - -## 详细过程 - -1.gcc是GNU Compiler Collection(就是GNU编译器套件),也可以简单认为是编译器,它可以编译很多种编程语言(括C、C++、Objective-C、Fortran、Java等等)。 - -2.当你的程序只有一个源文件时,直接就可以用gcc命令编译它。 - -3.但是当你的程序包含很多个源文件时,用gcc命令逐个去编译时,你就很容易混乱而且工作量大 - -4.所以出现了make工具 - -make工具可以看成是一个智能的批处理工具,它本身并没有编译和链接的功能,而是用类似于批处理的方式—通过调用makefile文件中用户指定的命令来进行编译和链接的。 - -5.makefile是什么?简单的说就像一首歌的乐谱,make工具就像指挥家,指挥家根据乐谱指挥整个乐团怎么样演奏,make工具就根据makefile中的命令进行编译和链接的。 - -6.makefile命令中就包含了调用gcc(也可以是别的编译器)去编译某个源文件的命令。 - -7.makefile在一些简单的工程完全可以人工手下,但是当工程非常大的时候,手写makefile也是非常麻烦的,如果换了个平台makefile又要重新修改。 - -8.这时候就出现了Cmake这个工具,cmake就可以更加简单的生成makefile文件给上面那个make用。当然cmake还有其他功能,就是可以跨平台生成对应平台能用的makefile,你不用再自己去修改了。 - -9.可是cmake根据什么生成makefile呢?它又要根据一个叫CMakeLists.txt文件(学名:组态档)去生成makefile。 - -10.到最后CMakeLists.txt文件谁写啊?亲,是你自己手写的。 - -11.当然如果你用IDE,类似VS这些一般它都能帮你弄好了,你只需要按一下那个三角形 - -12.cmake是make maker,生成各种可以直接控制编译过程的控制器的配置文件,比如makefile、各种IDE的配置文件。 - -13.make是一个简单的通过文件时间戳控制自动过程、处理依赖关系的软件,这个自动过程可以是编译一个项目。 - -## linux平台下的编译流程 - -  文本程序到可执行文件生成无论在什么平台大致分为以下几个部分: - -  1.用编辑器编写源代码,如.c文件。 - -  2.用编译器编译代码生成目标文件,如.o。 - -  3.用链接器连接目标代码生成可执行文件,如.exe。 - -  Linux平台下,.o文件一般是通过编译的但还未链接的目标文件,.out文件一般都是经过相应的链接产生的可执行文件(linux下)。当然这是一般情况下人们这么设置,而真正的,在linux中 .o通常保存的是可执行代码 ,至于可执行文件则没有规定扩展名,用的是文件属性位来决定的是否可执行。在chmod中设置。 - -  我们知道编译和链接阶段是靠g++和gcc编辑器来完成,这两个编译阶段是相同的,但是链接阶段g++默认链接c++库,所以一般情况下用gcc编译c文件,而g++编译cpp文件。当然g++也可以编译c文件,而gcc编译cpp文件则需要在后面加上参数-lstdc++,作用就是链接c++库。 - -  但是如果编译和链接的阶段如果源文件太多,一个一个编译时就会特别麻烦,于是人们想到,为什么不设计一种类似批处理的程序,来批处理编译源文件呢,于是就有了make工具,它是一个自动化编译工具,你可以使用一条命令实现完全编译。但是你需要编写一个规则文件,make依据它来批处理编译,这个文件就是makefile,所以编写makefile文件也是一个程序员所必备的技能。 - -  对于一个大工程,编写makefile实在是件复杂的事,于是人们又想,为什么不设计一个工具,读入所有源文件之后,自动生成makefile呢,于是就出现了cmake工具,它能够输出各种各样的makefile或者project文件,从而帮助程序员减轻负担。但是随之而来也就是编写cmakelist文件,它是cmake所依据的规则。所以在编程的世界里没有捷径可走,还是要脚踏实地的。 - -  原文件—cmakelist —cmake —makefile —make —生成可执行文件(make中则包含了多条链接以及gcc/g++编译语句)。 - -[查看原网页: www.jianshu.com](https://www.jianshu.com/p/758549cc274d) \ No newline at end of file diff --git a/source/_posts/Git/Git-gitignore文件不生效.md b/source/_posts/Git/Git-gitignore文件不生效.md deleted file mode 100644 index cea0ebf..0000000 --- a/source/_posts/Git/Git-gitignore文件不生效.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -title: Git-.gitignore文件不生效-转载 -author: TianZD -top: true -cover: true -toc: true -mathjax: false -summary: >- - .gitignore中已经标明忽略的文件目录下的文件,git push的时候还会出现在push的目录中,或者用git - status查看状态,想要忽略的文件还是显示被追踪状态。 -tags: - - Git -categories: - - Git -reprintPolicy: cc_by -abbrlink: 88176b53 -date: 2022-04-29 10:39:56 -coverImg: -img: -password: ---- - -## 原因 -在git忽略目录中,新建的文件在git中会有缓存,如果某些文件已经被纳入了版本管理中,就算是在.gitignore中已经声明了忽略路径也是不起作用的,这时候我们就应该先把本地缓存删除,然后再进行git的提交,这样就不会出现忽略的文件了。 - -需要特别注意的是: -1).gitignore只能忽略那些原来没有被track的文件,如果某些文件已经被纳入了版本管理中,则修改.gitignore是无效的。 -2)想要.gitignore起作用,必须要在这些文件不在暂存区中才可以,.gitignore文件只是忽略没有被staged(cached)文件, -对于已经被staged文件,加入ignore文件时一定要先从staged移除,才可以忽略。 - -## 解决 -首先清楚本地缓存,将其变为untrack状态,然后提交 -```bash -git rm -r --cached . -git add . -git commit -m 'update .gitignore' -git push -u origin master -``` \ No newline at end of file diff --git a/source/_posts/Git/Git.md b/source/_posts/Git/Git.md deleted file mode 100644 index dcaeca4..0000000 --- a/source/_posts/Git/Git.md +++ /dev/null @@ -1,468 +0,0 @@ ---- -title: Git -author: TianZD -top: true -cover: true -toc: true -mathjax: false -summary: 分布式版本控制工具Git介绍和一些常用操作 -tags: - - Git - - 版本控制 -categories: - - Git -reprintPolicy: cc_by -abbrlink: 69c3279c -date: 2022-04-29 10:37:33 -coverImg: -img: -password: ---- - - - -[toc] - -# Git学习 - -# 1、版本控制 - -## 版本控制工具 - -* Git -* SVN(Subversion) -* CVS(Concurrent Version System) -* VSS(Micorosoft Visual SourceSafe) -* TFS(Team Foundation Server) -* Visual Studio Online - -Git是目前世界上最先进的分布式**版本控制**系统 - -## 分类 - -### 本地版本控制 - -记录文件每次的更新,可以对每一个版本做一个快照,或是记录补丁文件,适合个人使用,如RCS - -![image-20210804192335392](https://gitee.com/tianzhendong/img/raw/master//images/image-20210804192335392.png) - -### 集中版本控制 - -代表是SVN - -版本库是集中存放在**中央服务器**的,而干活的时候,用的都是自己的电脑,所以要先从中央服务器取得最新的版本,然后开始干活,干完活了,再把自己的活推送给中央服务器。中央服务器就好比是一个图书馆,你要改一本书,必须先从图书馆借出来,然后回到家自己改,改完了,再放回图书馆。**集中式版本控制系统最大的毛病就是必须联网才能工作** - -![image-20210804192519737](https://gitee.com/tianzhendong/img/raw/master//images/image-20210804192519737.png) - -### 分布式版本控制 - -代表GIT - -每个人拥有全部代码,有安全隐患 - -分布式版本控制系统根本没有“中央服务器”,每个人的电脑上都是一个完整的版本库,这样,你工作的时候,就不需要联网了,因为版本库就在你自己的电脑上。既然每个人电脑上都有一个完整的版本库,那多个人如何协作呢?比方说你在自己电脑上改了文件A,你的同事也在他的电脑上改了文件A,这时,你们俩之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。**集中式版本控制系统相比,分布式版本控制系统的安全性要高很多,因为每个人电脑里都有完整的版本库,某一个人的电脑坏掉了不要紧,随便从其他人那里复制一个就可以了。而集中式版本控制系统的中央服务器要是出了问题,所有人都没法干活了。** - -![image-20210804192644016](https://gitee.com/tianzhendong/img/raw/master//images/image-20210804192644016.png) - - - -# 2、Git安装 - -## Linux安装Git - -首先,你可以试着输入git,看看系统有没有安装Git: - -``` -$ git -The program 'git' is currently not installed. You can install it by typing: -sudo apt-get install git -``` - -## Macos安装Git - -直接从AppStore安装Xcode,Xcode集成了Git,不过默认没有安装,你需要运行Xcode,选择菜单“Xcode”->“Preferences”,在弹出窗口中找到“Downloads”,选择“Command Line Tools”,点“Install”就可以完成安装了。 - -## 在Windows上安装Git - -在Windows上使用Git,可以从Git官网直接下载安装程序,然后按默认选项安装即可。 -安装完成后,在开始菜单里找到“Git”->“Git Bash”,蹦出一个类似命令行窗口的东西,就说明Git安装成功。 - -## 启动Git - -**GIt Bash:**Unix与Linux风格的命令行,使用最多,推荐对坐 - -**Git CMD:**Windows风格的命令行 - -**Git GUI:**图形界面的Git,不建议使用 - -# 3、基本linux命令 - -* cd:改变目录 -* cd .. :回到上一个目录 -* pwd:查看当前目录 -* clear:清屏 -* ls(ll):列出 当前目录所有文件 -* touch:新建一个文件 -* rm:删除一个文件 -* mkdir:新建一个目录 -* rm -r:删除一个文件夹 - * rm -rf /:递归删除所有文件 -* mv:移动文件mv a.html src 把a.html移动到src文件夹下 -* reset:重新初始化终端/清平 -* history:查看命令历史 -* help:帮助 -* exit:推出 -* #:注释 - -# 4、Git配置 - -所有的配置文件都保存在本地 - -## 查看配置 - -```bash -git config -l #查看配置 -git config --system -l #查看系统配置 -git config --global -l #查看(当前用户)全局配置 -``` - -## 配置用户名和密码 - -```bash -#配置用户名、邮箱 -git config --global user.name "tianzhendong" -git config --global user.email 1203886034@qq.com -``` - -# 5、Git原理 - -## 工作区域 - -* git本地有三个工作区域: - * 工作目录(working directory):工作区域,平时存放代码的地方 - * 暂存区(stage/index):用于临时存放你的改动,事实上只是一个文件,保存即将提交到文件列表信息 - * 资源库(repository或者git directory):本地仓库,安全存放数据的位置,里面有你提交的所有版本的数据,其中HEAD指向最新放入仓库的版本 - -* 远程的git仓库(Remote directory):托管代码的服务器 - -![image-20210804204342702](https://gitee.com/tianzhendong/img/raw/master//images/image-20210804204342702.png) - - - - - -![image-20210804205024206](https://gitee.com/tianzhendong/img/raw/master//images/image-20210804205024206.png) - - - -## 工作流程 - -1. 在工作目录中添加、修改文件 -2. 将需要进行版本管理的文件放入暂存区 -3. 将暂存区的文件提交到git仓库 -4. 提交到远程 - -git管理的文件三种状态:已修改(modified)、已暂存(staged)、已提交(committed) - -![image-20210804205250289](https://gitee.com/tianzhendong/img/raw/master//images/image-20210804205250289.png) - -# 6、Git使用 - -![image-20210804205421101](https://gitee.com/tianzhendong/img/raw/master//images/image-20210804205421101.png) - -## 创建本地仓库 - -```bash -#方法1:创建全新的仓库 -git init #初始化本地库 -#方法2:克隆远程仓库到本地 -git clone [url] #克隆远程仓库到本地 -``` - -## Git文件操作 - -文件四种状态: - -* **Untracked:**未跟踪,文件在文件夹中,但是没有加入到git库 - * 通过**git add .**状态变为**staged** -* **Unmodify:**已入库,未修改 - * 如果被修改,变为**Modified** - * 如果使用**git rm**移出版本库则成为**Untracked** -* **Modified:**已修改 - * **git add**:进入暂存**staged**状态 - * **git checkout**:丢弃修改,返回到**unmodify**状态,git checkout即从库中取出文件,覆盖当前修改 -* **Staged:**暂存状态 - * **git commit**:修改同步到库中,随后文件变成**unmodify**状态 - * **git reset HEAD filename**:取消暂存,变为**Modified** - -### 基本操作 - -```bash -#查看状态 -git status -#查看指定文件状态 -git status [filename] -#添加到暂存区 -git add . -#提交暂存区内容到本地仓库 -git commit -m "注释内容" #-m表示提交信息 -``` - -### 忽略文件 - -有时候不需要把某些文件纳入版本控制中,比如数据库文件、临时文件、设计文件等 - -在主目录下建立".gitignore"文件,此文件有如下规则: - -1. 文件中的空行或以#开头的行将会被忽略 -2. 可以是用linux通配符,例如:*表示任意多个字符,?表示一个字符,[]表示可选字符范围,{}代表可选的字符串集 -3. 如果名称的最前面有一个!,表示例外规则,将不会被忽略 -4. 如果名称前面有一个/,表示要忽略的文件在此目录下,子目录下的文件不忽略 -5. 如果名称的最后面有一个/,表示要忽略的是此目录下的所有文件 - -```bash -#为注释 -*.txt #忽略所有.txt结尾的文件 -!lib.txt #lib.txt除外 -/temp #仅忽略项目根目录下的TODO文件,不包括其他目录temp -build/ #忽略build/目录下的所有文件 -doc/*.txt #忽略doc/notes.txt,但不包括doc/server/arch.txt -``` - -## 使用码云Gitee - -### 设置本机绑定SSH公钥实现免密登陆 - -```bash -# C:\users\Administrator目录 -#生成公钥 -ssh-keygen -``` - -把在.ssh目录下的id_rsa.pub文件中的内容复制到gitee公钥设置中即可 - -## IDEA中集成Git - -1. 新建项目,绑定git - 1. 将远程的git文件目录拷贝到项目中即可 -2. 修改文件,使用git - -# 7、Git分支 - -* **master**:主分支 -* **dev**:开发用 - -master主分支应该非常稳定,用来发布新版本,一般情况下不允许在上面工作,工作一般情况下在新建的dev分支上工作,工作完后,比如要分布,或者说dev分支代码稳定后可以合并到主分支master上来 - -三种分支合并情况可以见该链接:https://blog.csdn.net/qq_42780289/article/details/97945300 - -```bash -# 列出所有本地分支 -git branch -# 列出所有远程分支 -git branch -r -# 新建一个分支,但是并未切换 -git branch [branch_name] -# 新建一个分支,并切换至该分支 -git checkout -b [branch] -# 合并指定分支到当前分支 -git merge [branch] -# 删除分支 -git branch -d [branch] -# 删除远程分支 -git push origin --delete [branch] -``` - -# 8、部分指令 - -## 指令 - -```bash -git init //初始化本地库 -git add readme.txt //将文件添加到仓库 -git commit -m "first commit" //把文件提交到仓库 -git status //查看仓库当前状态 -git diff readme.txt //查看该文件的不同 -git log // 查看每次更改内容 -git reset --hard HEAD^ //回退到上一个版本,HEAD表示当前版本,HEAD^上一个版本,几个^号 表示上几个版本; -rm readme.txt //删除文件 -git rm readme.txt//从库中删除文件 -git commit -m "remove the readme.txt" -git remote add origin git@github.com:michaelliao/learngit.git //关联远程库 -git push -u origin master //将本地库的内容推送到远程 -git remote -v //查看远程库信息 -git remote rm origin //接触本地与远程的绑定关系 -git clone git@github.com:michaelliao/gitskills.git //从远程库克隆 -``` - -## 合并其他分支代码至master分支 - -下面以dev分支为例来讲解。 - -1. 当前分支所有代码提交 - 先将dev分支上所有有代码提交至git上,提交的命令一般就是这几个,先复习下: - -```bash -# 将所有代码提交 -git add . -# 编写提交备注 -git commit -m "修改bug" -# 提交代码至远程分支 -git push origin dev -``` - -2. 切换当前分支至主干(master) - -```bash -# 切换分支 -git checkout master - -# 如果多人开发建议执行如下命令,拉取最新的代码 -git pull origin master -``` - -3. 合并(merge)分支代码 - -```bash -git merge dev -# merge完成后可执行如下命令,查看是否有冲突 -git status -``` - -4. 提交代码至主干(master) - -```bash -git push origin master -``` - -5. 最后切换回原开发分支 - -```bash -git checkout dev -``` - -## 删除分支 - -```bash -// delete branch locally -git branch -d localBranchName - -// delete branch remotely -git push origin --delete remoteBranchName -``` - -## 重命名文件 - -第一种方法:使用mv命令 - -``mv readme README.md`` - -这个时候,如果使用git status查看工作区的状态,Git会提示,readme文件被删除,README.md文件未被跟踪。git add进行提交到暂存区的时候,需要把这个两个文件一起提交,即: - -``git add readme README.md`` - -第二中方法:直接使用Git的 git mv命令。 -`` -git mv readme README.md`` - -此时,我们不需要再使用git add 命令把两个文件一起提交,直接使用git commit即可。 -也就是说,git mv命令比linux的mv命令,省去了git add提交文件到暂存区这个步骤。 - - - -## 【Git】pull遇到错误:error: Your local changes to the following files would be overwritten by merge: - -首先取决于你是否想要保存本地修改。(是 /否) - -### 是 - -别急我们有如下三部曲 - -```bash -git stash -git pull origin master -git stash pop -``` - - -- git stash的时候会把你本地快照,然后git pull 就不会阻止你了,pull完之后这时你的代码并没有保留你的修改。惊了! 别急,我们之前好像做了什么? - -STASH -这时候执行git stash pop你去本地看会发现发生冲突的本地修改还在,这时候你该commit push啥的就悉听尊便了。 - -### 否 - -既然不想保留本地的修改,那好办。直接将本地的状态恢复到上一个commit id 。然后用远程的代码直接覆盖本地就好了。 - -```bash -git reset --hard -git pull origin master -``` - - - ------------------------------------------------- - -版权声明:本文为CSDN博主「转身雪人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 -原文链接:https://blog.csdn.net/nakiri_arisu/article/details/80259531 - - - -# Git 标签 - -标签用于标记某一提交点,唯一绑定一个固定的commitId,相当于为这次提交记录指定一个别名,方便提取文件。 -可以为重要的版本打上标签,标签可以是一个对象,也可以是一个简单的指针,但是指针不会移动。 - -## 创建标签 - -`git tag ` #为当前分支指向的commit记录创建标签 - - `git tag ` #为指定的commitId创建标签 - - `git tag -a -m "msg" ` #创建标签同时添加说明信息 - -## 查看标签 - -`git tag` #查看所有标签名称 - - `git show ` #查看标签的详细信息(包含commit的信息) - - `git tag -ln [tag_name]` #显示标签名及其描述信息 - -## 远程推送标签 - -`git push ` #将标签推送到远程服务器 - - `git push --tags` #将本地的全部tag推送到远程服务器 - -## 删除标签 - -`git tag -d ` #删除本地的标签 - -`git push :refs/tags/` #删除远程标签 - -## 标签内容提取 - -``` -git archive --format=zip --output=src/xxx.zip ` #提取为zip格式,src可以是相对路径,也可以是绝对路径 - **示例:**在d盘下生成包含0.8标签内容的压缩包 - `git archive --format=zip --output=d:/v0.8.zip v0.8 -``` - -## 切换标签 - -如果我们不想直接提取出标签的代码,而是希望在指定标签下继续进行开发,此时可以切换到标签。 - -`git checkout ` #切换到指定标签 - -**示例:**切换到v0.8标签进行开发,此时提示我们处于`detached HEAD state`(分离头指针状态),即说明HEAD指针没有指向具体的分支,查看HEAD指针它直接指向了一个commit对象,此时进行开发操作没有任何意义。 - -如果想要退出`detached HEAD state`,很简单只需要切换回指定分支就可以了,如`git checkout master` - - 如果想要在当前tag下继续开发,可以新建一个分支并让HEAD指向分支就可以了。 - - - -## 强制推送 - -`git push -f origin master`强制推送到origin远程的master分支上 \ No newline at end of file diff --git a/source/_posts/Java/JavaWeb.md b/source/_posts/Java/JavaWeb.md deleted file mode 100644 index 5c47372..0000000 --- a/source/_posts/Java/JavaWeb.md +++ /dev/null @@ -1,3353 +0,0 @@ ---- -title: JavaWeb学习笔记 -author: TianZD -top: true -cover: true -toc: true -mathjax: false -summary: JavaWeb学习笔记,粗略学了一下,没有参考价值 -tags: - - javaweb - - java - - 学习笔记 -categories: - - java -reprintPolicy: cc_by -abbrlink: b29dfb1f -date: 2022-04-29 10:48:32 -coverImg: -img: -password: ---- - - -[toc] - -# JavaWeb - -# 0、JavaEE三层架构 - -![image-20210803175103445](https://gitee.com/tianzhendong/img/raw/master//images/image-20210803175103445.png) - -# 1、概述 - -## 1.1、软件结构 - -C/S:客户端client/服务器server - -B/S:客户端采用浏览器,服务器端采用web服务器 - -## 1.2、页面组成 - -* 内容(结构):在页面中看到的数据,一般内容采用html技术 -* 表现:内容在页面上的展示形式,一般用CSS技术 -* 行为:页面中的元素与输入设备交互的响应,一般使用JavaScript技术 - -# 2、HTML - -> HTML:超文本标记语言,通过标签来标记要显示的网页中的各个部分,网页文件本身是一种文本文件,通过在文本文件中添加标记符,告诉浏览器如何显示其中的内容 - -```html - - - - - 标题 - - - tianzhendong - - -``` - -## 2.1、HTML标签介绍 - -```html -<标签名>封装的数据 -``` - -* 标签名不区分大小写 -* 标签拥有自己的属性 - * 基本属性:bgcolor = “red” 可以修改简单的样式效果 - * 事件属性:onclick= “alert(‘你好!’);” 可以设置事件响应后的代码 -* 单双标签 - * 单标签:<标签名 /> br表示换行,hr表示水平线 - * 双标签:<标签名>封装的数据 - -```html - - - - - 标题 - - - tianzhendong -
- -
- - - - -``` - - - -## 2.2、常用标签 - -### 字体font - -```html -我是字体标签 -``` - -### 字符实体& - -一些字符在 HTML 中拥有特殊的含义,比如小于号 (<) 用于定义 HTML 标签的开始。如果我们希望浏览器正确地显示这些字符,我们必须在 HTML 源码中插入字符实体。 - -字符实体有三部分:一个和号 (&),一个实体名称,或者 # 和一个实体编号,以及一个分号 (;)。 - -![image-20210728211011236](https://i.loli.net/2021/07/28/KObBFExtv9IMgDh.png) - -### 标题标签h1 - -支持h1-h6,align为对其属性 - -```html -

标题1

-``` - -### 超链接a - -* href属性:设置连接的地址 -* target属性:设置哪个目标进行跳转 - * _self:默认值,当前页面 - * _blank:打开新页面进行跳转 - -```html -百度 -百度 -百度 -``` - - - -### 列表标签ul/ol - -* 无序列表始于<**ul**> 标签,每个列表项始于 <**li**> -* 有序列表始于<**ol**>标签,每个列表始于<**li**> -* 属性:type=”none“,取消前面的标号或者小圆点 - -```html -
    -
  • 1
  • -
  • 2
  • -
  • 3
  • -
-``` - -### 图像标签img - -* src属性:src=“url”,url为图像的位置 -* alt属性:设置替代的文本属性 -* width属性:设置宽度 -* height属性:设置高度 -* border属性:设置边框宽度 - -```html - -找不到图片 -``` - -### 表格标签table - -表格由 <**table**> 标签来定义。每个表格均有若干行(由 <**tr**>标签定义),每行被分割为若干单元格(由 <**td**> 标签定义)。字母 td 指表格数据(table data),即数据单元格的内容。数据单元格可以包含文本、图片、列表、段落、表单、水平线、表格等等。表格的表头使用 <**th**> 标签进行定义。 - -* <**th**>:表头 -* <**tr**>:行 -* <**td**>:列 - -```html - - - - - - - - - - - - -
表头1
row 1, cell 1row 1, cell 2
row 2, cell 1row 2, cell 2
-``` - -### 内嵌窗口iframe - -在页面上开辟一个小区域,显示一个单独的页面 - -iframe和a标签组合使用: - -* 在iframe标签中使用name属性定义一个名称 -* 在a标签的target属性上设置iframe的name属性值 - -```html - -
- -``` - -### 表单标签form - -html页面中用来收集用户信息的所有元素集合,表单元素是允许用户在表单中(比如:文本域、下拉列表、单选框、复选框等等)输入信息的元素。 - -```html - - -
- -
- -

用户注册

- - 用户名称:
- - 用户密码:
- 确认密码:
- - 性别:男 -
- - 兴趣爱好:java - c - c++
- - 国籍:
- - 自我评价:
- -
- -
- -
- -
-
- -``` - -一般把表单放在一个表格table中 - -# 3、CSS - -CSS:层叠样式菜单,用于增强控制网页样式并允许将样式信息与网页内容分离的一种标记性语言 - -## 3.1、语法规则 - -* 选择器:浏览器根据“选择器”决定受CSS样式影响的HTML元素(标签) -* 属性:要改变的样式名,并且每个属性有一个值,属性和值由:分开,并且由花括号包围 -* 值 - -```html -body {color: blue} -``` - -将 body 元素内的文字颜色定义为蓝色。在上述例子中,body 是选择器,而包括在花括号内的的部分是声明。声明依次由两部分构成:属性和值,color 为属性,blue 为值。 - -注意: - -* 如果值为若干单词,则要给值加引号 - -```html -p {font-family: "sans serif";} -``` - -* 如果要定义不止一个声明,则需要用分号将每个声明分开 - -```html -p { -text-align: center; -color: black; -font-family: arial; -} -``` - -## 3.2、CSS和HTML结合方式 - -### 方法1 - -```html - - - - - CSS-HTML - - - - - tianzhendong - - -``` - -缺点: - -* 只能在同一页面内复用代码,不能再多个页面中复用CSS代码 -* 维护起来不方便,实际的项目中会有成千上万的页面,要到每个页面中修改,工作量太大 - -### 方法2 - -把CSS样式写成一个单独的CSS文件,通过link标签引入即可复用 - -```css -font{ - border : 1px solid red; -} -``` - -```html - - - - - CSS-HTML - - - - - tianzhendong - - -``` - -## 3.3、常用选择器 - -### 标签名选择器 - -可以决定哪些标签被动的使用这个样式 - -格式: - -```css -标签名{ - 属性:值; -} -``` - -```html - - - - - CSS-HTML - - - - - - tianzhendong
- tianzhendong
- - -``` - -### id选择器 - -可以通过id属性选择性的去使用这个样式 - -```html -#id属性值{ - 属性:值; -} -``` - -```html - - - - - CSS-HTML - - - - - - tianzhendong
- tianzhendong
- - -``` - -id不能为数字,需要字母开头 - -### Class(类)选择器 - -```html -.class属性值{ - 属性:值; -} -``` - -```html - - - - - CSS-HTML - - - - - - tianzhendong
- tianzhendong
- - -``` - -### 组合选择器 - -```html -选择器1,选择器2,选择器n{ - 属性:值; -} -``` - -## 3.4、常用样式 - -```html -color: red; /*修改字体颜色*/ -width: 300px; /*宽度*/ -height: 300px; /*高度*/ -background-color: aqua; /*背景颜色*/ -font-size: 400px; /*字体大小*/ -border: 1px solid red; /*红色的1像素的边框*/ -margin-left: auto; /*DIV居中*/ -margin-right: auto; /*DIV居中*/ -text-align: center; /*文本居中*/ -text-decoration: none; /*超链接去下划线*/ -/*表格细线*/ -border: 1px solid black; /*设置边框*/ -border-collapse: collapse;/*将边框合并*/ -list-style: none;/*列表去除修饰*/ -``` - -# 4、JavaScript - -## 4.1、概述 - -主要用于完成页面的数据验证,运行在客户端,需要运行浏览器来解析执行JavaScript代码 - -> **JS是弱类型(类型可变),java是强类型(定义变量时类型已定,不能改变)** - -**特点:** - -* 交互性(它可以做的就是信息的动态交互) -* 安全性(不允许直接访问本地硬盘) -* 跨平台性(只要是可以解释JS的浏览器都可以执行,和平台无关) - -## 4.2、JavaScript和html结合的方式 - -### 方式1 - -在head标签中或者在body标签中,使用script标签来书写JavaScript代码 - -```html - - - - - JavascriptDemo - - - - - - - -``` - -### 方式2 - -使用Script标签引入单独的JavaScript代码文件 - -```js -alert("hello"); -``` - -```html - - - - - JavascriptDemo - - - - - - - -``` - -## 4.3、变量 - - - -| 变量类型 | 表达式 | -| :--------: | -------- | -| 数值类型 | number | -| 字符串类型 | string | -| 对象类型 | object | -| 布尔类型 | boolean | -| 函数类型 | function | - -特殊的值 - -* undefined:未定义,所有js变量未赋值时,都是undefined -* null:空值 -* NAN:not a number 非数字 - -```html - - - - - JavascriptDemo - - - - - -``` - -## 4.4、关系运算 - -* 等于:==,简单的字面值比较 -* 全等于:===,除了字面值比较外,还会做类型比较 - -```html - - - - - JavascriptDemo - - - - - -``` - -## 4.5、逻辑运算 - -* 且运算:&& - * 当表达式全为真,返回最后一个表达式的值 - * 当表达式中有一个为假,返回第一个为假的表达式的值 -* 或运算:|| - * 表达式全为假,返回最后要给表达式的值 - * 有一个表达式为真,返回第一个为真的表达式的值 -* 取反运算:! - -在JavaScript中,所有的变量,都可以作为一个boolean类型的变量去使用,0、null、undefined、‘’‘’(空串)都可以认为是false - -```html - - - - - JavascriptDemo - - - - - -``` - -## 4.6、数组 - - - -```js -var 数组名 =[];//空数组 -var 数组名 =[1,'a',true]//定义数组同时赋值元素 -``` - -JavaScript中数组会自动扩容 - -```html - - - - - JavascriptDemo - - - - - -``` - -## 4.7、函数 - -js中函数重载会覆盖上面的定义,不允许重载函数 - -### 方式1:使用function关键字定义 - -```html - - - - - JavascriptDemo - - - - - -``` - -### 方式2:var fun=function(){} - -```html - - - - - JavascriptDemo - - - - - -``` - -### 隐形参数 - -```html - - - - - JavascriptDemo - - - - - -``` - -## 4.8、js中的事件 - -> 事件是由电脑输入设备与页面进行交互的响应 - -### 常用事件: - -| 事件名称 | 功能 | -| ------------------------ | -------------------------------------------- | -| onload加载完成事件 | 页面加载完成后,常用于做页面js代码初始化操作 | -| onclick单机事件 | 用于按钮的点击响应事件 | -| onblur失去焦点事件 | 用于输入框失去焦点后验证其输入内容是否合法 | -| onchange内容发生改变事件 | 用于下拉列表和输入框内容发生改变后操作 | -| onsubmint表单提交事件 | 用于表单提交前,验证所有表单项是否合法 | - -### 注册事件 - -> 告诉浏览器,事件响应后要执行哪些操作代码,叫事件注册或事件绑定 - -* 静态注册:通过html标签的事件属性直接赋予事件响应后的代码 -* 动态注册:先通过js代码得到标签的dom对象,再通过dom对象.事件名 = function(){}形式赋予事件响应后的代码.动态注册基本步骤: - * 获取标签对象 - * 标签对象.事件名= function(){} - -#### onload事件注册 - -onload加载完成事件 :页面加载完成后,常用于做页面js代码初始化操作 - -```html - - - - - JavascriptDemo - - - - - - -``` - -```HTML - - - - - JavascriptDemo - - - - - - -``` - -#### onclick事件注册 - -onclick单机事件:用于按钮的点击响应事件 - -```html - - - - - JavascriptDemo - - - - - - - - -``` - -#### onblur事件注册 - -onblur失去焦点事件:用于输入框失去焦点后验证其输入内容是否合法 - -```html - - - - - JavascriptDemo - - - - - - - - - - - - - - -
- 用户注册 -
- 用户名: - - -
- 密码: - - -
- - -``` - -#### onchange事件注册 - -onchange内容发生改变事件:用于下拉列表和输入框内容发生改变后操作 - -```html - - - - - JavascriptDemo - - - - - - - - - - - - - - -
- 用户注册 -
- 选择1: - - - -
- 选择2: - - - -
- - -``` - -#### onsubmit事件注册 - -onsubmint表单提交事件 :用于表单提交前,验证所有表单项是否合法 - -```html - - - - - JavascriptDemo - - - - - - - - - - - -
- - -``` - -## 4.9、DOM模型 - -Document Object Model文档对象模型:把文档中的标签、属性、文本转化为对象进行管理 - -![image-20210730210623985](https://i.loli.net/2021/07/30/5vLdeYW4cHU1gqR.png) - - - -### 方法 - -| close() | 关闭用 document.open() 方法打开的输出流,并显示选定的数据。 | -| ---------------------- | ------------------------------------------------------------ | -| getElementById() | 返回对拥有指定 id 的第一个对象的引用。 | -| getElementsByName() | 返回带有指定名称的对象集合。 | -| getElementsByTagName() | 返回带有指定标签名的对象集合。 | -| open() | 打开一个流,以收集来自任何 document.write() 或 document.writeln() 方法的输出。 | -| write() | 向文档写 HTML 表达式 或 JavaScript 代码。 | -| writeln() | 等同于 write() 方法,不同的是在每个表达式之后写一个换行符。 | - -三种查询方法: - -* 优先使用getElementById() -* 其次getElementsByName() -* 最后getElementsByTagName() - -越往后,查询范围越大 - -#### getElementById() - -返回对拥有指定 id 的第一个对象的引用。 - -```html - - - - - JavascriptDemo - - - - - 密码:
- - - - -``` - -![image-20210730221657830](https://i.loli.net/2021/07/30/nyg56iMB8HtTwsc.png) - -#### getElementsByName() - -返回带有指定名称的对象集合 - -```html - - - - - JavascriptDemo - - - - - 兴趣爱好: - C; - Java; - Python; -
- - - - - -``` - -#### getElementsByTagName() - - 返回带有指定标签名的对象集合。 - -和getElementsByName() 类似,不过是使用标签名进行查询 - -### 节点(标签对象)的常用属性和方法 - -节点就是标签对象 - -#### 方法 - -| 方法 | 功能 | -| ------------------------- | -------------------------------- | -| getElementsByTagName() | 获取当前节点的指定标签名孩子节点 | -| appendChild(oChildNode) | 添加一个子节点 | - -#### 属性 - -| 属性 | 功能 | -| --------------- | ------------------------------------------ | -| childNodes | 获取当前节点的**所有子节点** | -| firstChild | 获取当前节点的**第一个**子节点 | -| lastChild | 获取当前节点的**最后一个**子节点 | -| parentNode | 获取当前节点的父节点 | -| nextSibling | **下一个**节点 | -| previousSibling | **上一个**节点 | -| className | 用于获取或者设置标签的**class属性值** | -| innerHTML | 获取或者设置起始标签和结束标签**中的内容** | -| innerText | 获取或者设置起始标签和结束标签**中的文本** | - - - -# 5、jQuery - -## 5.1、介绍 - -**概述:**jQuery就是JavaScript和查询Query,辅助JavaScript开发的js类库 - -**核心思想:**写的更少,做的更多,所以它实现了很多浏览器的兼容问题 - -**优势:**免费、开源,语法设计可以使开发更加便捷 - -## 5.2、安装 - -### 从网页添加 - -可以通过多种方法在网页中添加 jQuery。 您可以使用以下方法: - -* 从 [jquery.com](http://jquery.com/download/) 下载 jQuery 库 -* 从 CDN 中载入 jQuery, 如从 Google 中加载 jQuery - -### 下载 jQuery - -有两个版本的 jQuery 可供下载: - -* Production version - 用于实际的网站中,已被精简和压缩。 -* Development version - 用于测试和开发(未压缩,是可读的代码) - -以上两个版本都可以从 [jquery.com](http://jquery.com/download/) 中下载。 - -jQuery 库是一个 JavaScript 文件,您可以使用 HTML 的 <**script**> 标签引用它: - -```html - - - -``` - -**提示:** 将下载的文件放在网页的同一目录下,就可以使用jQuery。 - -### 替代方案 - -如果您不希望下载并存放 jQuery,那么也可以通过 CDN(内容分发网络) 引用它。 - -```html - - - - - -``` - -## 5.3、初体验 - -```html - - - - - jQueryDemo - - - - - - - -``` - -## 5.4、jQuery核心函数 - -**$**是jQuery的核心函数,能完成很多功能,**$**()就是调用这个函数: - -1. 传入参数为函数时:表示页面加载完成以后,相当于window.onload = function () -2. 传入html字符串时:创建html标签对象 -3. 传入选择器字符串时: - 1. **$**(“#id属性值”),根据id查询标签对象。 - 2. **$**(“标签名”),根据标签名查询标签对象。 - 3. **$**(“.class”),根据class查询标签对象 -4. 传入DOM对象时:把DOM对象转换为jQuery对象 - -## 5.5、jQuery语法 - -jQuery 语法是通过选取 HTML 元素,并对选取的元素执行某些操作。 - -#### 基础语法: **$(selector\).action\()** - -* 美元符号定义 jQuery -* 选择符(selector)"查询"和"查找" HTML 元素 -* jQuery 的 action() 执行对元素的操作 - -#### 实例: - -* $(this).hide() - 隐藏当前元素 -* $("p").hide() - 隐藏所有 <**p**> 元素 -* $("p.test").hide() - 隐藏所有 class="test" 的 <**p**> 元素 -* $("#test").hide() - 隐藏 id="test" 的元素 - -#### 文档就绪事件 - -```html -$(document).ready(function(){ - - // 开始写 jQuery 代码... - -}); -``` - -这是为了防止文档在完全加载(就绪)之前运行 jQuery 代码,即在 DOM 加载完成后才可以对 DOM 进行操作。 - -**提示:**简洁写法(与以上写法效果相同): - -```html -$(function(){ - - // 开始写 jQuery 代码... - -}); -``` - -## 5.6、jQuery选择器 - -jQuery 选择器允许您对 HTML 元素组或单个元素进行操作。 - -jQuery 选择器基于元素的 id、类、类型、属性、属性值等"查找"(或选择)HTML 元素。 它基于已经存在的 CSS 选择器,除此之外,它还有一些自定义的选择器。 - -jQuery 中所有选择器都以美元符号开头:$()。 - -**实例** - -```html - - - - - jQueryDemo - - - - - -

1级标题

-

这是一个标题

-

这是一个段落。

-

这是另一个段落。

- - - - -``` - -## 5.7、jQuery事件 - -jQuery 是为事件处理特别设计的。 - -页面对不同访问者的响应叫做事件。 - -事件处理程序指的是当 HTML 中发生某些事件时所调用的方法。 - -实例: - -* 在元素上移动鼠标。 -* 选取单选按钮 -* 点击元素 - -### jQuery 事件方法语法 - -```html -$("p").click(function(){//定义了一个事件 - // 动作触发后执行的代码!! -}); -``` - -### 常用事件 - -| 事件名 | 功能 | -| ------------ | ------------------------------------------------------------ | -| click() | click() 方法是当按钮点击事件,被触发时会调用一个函数。 | -| dblclick() | 当双击元素时,会发生 dblclick 事件。 | -| mouseenter() | 当鼠标指针穿过元素时,会发生 mouseenter 事件。 | -| mouseleave() | 当鼠标指针离开元素时,会发生 mouseleave 事件。 | -| mousedown() | 当鼠标指针移动到元素上方,并按下鼠标按键时,会发生 mousedown 事件。 | -| mouseup() | 当在元素上松开鼠标按钮时,会发生 mouseup 事件。 | -| hover() | hover()方法用于模拟光标悬停事件。当鼠标移动到元素上时,会触发指定的第一个函数(mouseenter);当鼠标移出这个元素时,会触发指定的第二个函数(mouseleave)。 | -| focus() | 当元素获得焦点时,发生 focus 事件。当通过鼠标点击选中元素或通过 tab 键定位到元素时,该元素就会获得焦点。 | -| blur() | 当元素失去焦点时,发生 blur 事件。 | -| keydown() | 当键盘键被按下时发生 keydown 事件。 | -| keypress() | 键按下的过程 | -| keyup() | 当键盘键被松开时发生 keyup 事件。 | -| submit() | 当提交表单时,会发生 submit 事件。 | -| change() | 当元素的值改变时发生 change 事件(仅适用于表单字段 | - -```html - - - - - jQueryDemo - - - - - - - -
- mouseenter - - -``` - -## 5.8、jQuery效果 - -### 显示/隐藏 - -#### jQuery hide() 和 show() - -通过 jQuery,您可以使用 hide() 和 show() 方法来隐藏和显示 HTML 元素: - -参数: - -* speed 参数规定隐藏/显示的速度,可以取以下值:"slow"、"fast" 或毫秒。 -* callback 参数是隐藏或显示完成后所执行的函数名称。 - -```html - - - - - jQueryDemo - - - - -

如果你点击“隐藏” 按钮,我将会消失。

- - - - -``` - -#### toggle() - -可以使用 toggle() 方法来切换 hide() 和 show() 方法。显示被隐藏的元素,并隐藏已显示的元素: - -```html - - - - - jQueryDemo - - - - -

如果你点击按钮,我将会消失/显示。

- - - -``` - -### 淡入淡出 - -通过 jQuery,您可以实现元素的淡入淡出效果。 - -jQuery 拥有下面四种 fade 方法: - -| 方法 | 用途 | -| ------------ | ------------------------------------------------ | -| fadeIn() | 用于淡入已隐藏的元素 | -| fadeOut() | 用于淡出可见元素。 | -| fadeToggle() | 在 fadeIn() 与 fadeOut() 方法之间进行切换。 | -| fadeTo() | 允许渐变为给定的不透明度(值介于 0 与 1 之间)。 | - -* 可选的 speed 参数规定效果的时长。它可以取以下值:"slow"、"fast" 或毫秒。. - -* 可选的 callback 参数是 fading 完成后所执行的函数名称。 - -### 滑动 - -jQuery 拥有以下滑动方法: - -* slideDown():向下滑动元素 -* slideUp():用于向上滑动元素。 -* slideToggle():在 slideDown() 与 slideUp() 方法之间进行切换。 - -### 动画 - -jQuery animate() 方法用于创建自定义动画。 - -```html - - - - - - - - - - -

默认情况下,所有的 HTML 元素有一个静态的位置,且是不可移动的。 -如果需要改变,我们需要将元素的 position 属性设置为 relative, fixed, 或 absolute!

-
-
- - - -``` - -* 必需的 params 参数定义形成动画的 CSS 属性。 -* 可选的 speed 参数规定效果的时长。它可以取以下值:"slow"、"fast" 或毫秒。 -* 可选的 callback 参数是动画完成后所执行的函数名称。 - -#### 使用相对值 - -```html - - - - - - - - - - -

默认情况下,所有的 HTML 元素有一个静态的位置,且是不可移动的。 -如果需要改变为,我们需要将元素的 position 属性设置为 relative, fixed, 或 absolute!

-
-
- - - -``` - -#### 使用队列功能 - -```html - - - - - - - - - - -

默认情况下,所有的 HTML 元素有一个静态的位置,且是不可移动的。 -如果需要改变为,我们需要将元素的 position 属性设置为 relative, fixed, 或 absolute!

-
-
- - - -``` - -#### 停止动画 - -jQuery stop() 方法用于停止动画或效果,在它们完成之前。 - -stop() 方法适用于所有 jQuery 效果函数,包括滑动、淡入淡出和自定义动画。 - -```html - - - - - - - - - - - - -
点我向下滑动面板
-
Hello world!
- - - -``` - -## 5.9、链(Chaining) - -Chaining 允许我们在一条语句中运行多个 jQuery 方法(在相同的元素上) - -```html - - - - - - - - - -

菜鸟教程!!

- - - - -``` - -## 5.10、获取内容和属性 - -### 获得内容 - text()、html() 以及 val() - -三个简单实用的用于 DOM 操作的 jQuery 方法: - -* text() - 设置或返回所选元素的文本内容 -* html() - 设置或返回所选元素的内容(包括 HTML 标记) -* val() - 设置或返回表单字段的值 - -### 获取属性 - attr() - -jQuery attr() 方法用于获取属性值。 - -下面的例子演示如何获得链接中 href 属性的值: - -## 5.12、添加元素 - -* append() - 在被选元素的结尾插入内容 -* prepend() - 在被选元素的开头插入内容 -* after() - 在被选元素之后插入内容 -* before() - 在被选元素之前插入内容 - -```html - - - - - - - - - -

这是一个段落。

-

这是另外一个段落。

-
    -
  1. List item 1
  2. -
  3. List item 2
  4. -
  5. List item 3
  6. -
- - - - -``` - - - -## 5.13、删除元素 - -* remove() - 删除被选元素(及其子元素) -* empty() - 从被选元素中删除子元素 - -```html - - - - - - - - - -
- -这是 div 中的一些文本。 -

这是在 div 中的一个段落。

-

这是在 div 中的另外一个段落。

- -
-
- - - - -``` - -## 5.14、获取并设置 CSS 类 - -* addClass() - 向被选元素添加一个或多个类 -* removeClass() - 从被选元素删除一个或多个类 -* toggleClass() - 对被选元素进行添加/删除类的切换操作 -* css() - 设置或返回样式属性 - -```html - - - - - - - - - - -

标题 1

-

标题 2

-

这是一个段落。

-

这是另外一个段落。

-
这是一些重要的文本!
-
- - - - -``` - -## 5.15、css() 方法 - -设置或返回被选元素的一个或多个样式属性。 - -```html - - - - - - - - - -

这是一个标题

-

这是一个段落。

-

这是一个段落。

-

这是一个段落。

- - - -``` - - - - - -# 6、XML - -XML 指可扩展标记语言(e**X**tensible **M**arkup **L**anguage)。被设计用来**传输和存储数据**。 - -HTML 被设计用来显示数据。 - -XML 不是 HTML 的替代。 - -XML 和 HTML 为不同的目的而设计: - -* XML 被设计用来传输和存储数据,其焦点是数据的内容。 -* HTML 被设计用来显示数据,其焦点是数据的外观。 - -### 6.1、用途 - -XML 应用于 Web 开发的许多方面,常用于简化数据的存储和共享。 - -### 6.2、XML 语法规则 - -1. **XML 文档必须有根元素,它是所有其他元素的父元素,比如以下实例中 root 就是根元素** - -```xml - - - ..... - - -``` - -2. **XML 声明文件的可选部分,如果存在需要放在文档的第一行** - -```xml - -``` - -3. **在 XML 中,省略关闭标签是非法的。所有元素都必须有关闭标签:** - -```xml -

This is a paragraph.

-
-``` - -声明不是 XML 文档本身的一部分,它没有关闭标签。 - -4. **XML 标签对大小写敏感。标签 <**Letter**> 与标签 <**letter**> 是不同的。** - -5. **在 XML 中,所有元素都必须彼此正确地嵌套:** - -```xml -This text is bold and italic -``` - -6. **在 XML 中,XML 的属性值必须加引号。** - -在 XML 中,一些字符拥有特殊的意义。如果您把字符 "<" 放在 XML 元素中,会发生错误,这是因为解析器会把它当作新元素的开始。为了避免这个错误,请用**实体引用**来代替特殊字符: - -| < | < | less than | -| ------ | ---- | -------------- | -| > | > | greater than | -| & | & | ampersand | -| ' | ' | apostrophe | -| " | " | quotation mark | - -7. **在 XML 中编写注释的语法与 HTML 的语法很相似。** - -8. **在 XML 中,文档中的空格不会被删减。** - -HTML 会把多个连续的空格字符裁减(合并)为一个,在 XML 中,文档中的空格不会被删减。 - -9. **XML 以 LF 存储换行。** - -在 Windows 应用程序中,换行通常以一对字符来存储:回车符(CR)和换行符(LF)。 - -在 Unix 和 Mac OSX 中,使用 LF 来存储新行。 - -在旧的 Mac 系统中,使用 CR 来存储新行。 - -XML 以 LF 存储换行。 - -### 6.3、XML元素 - -#### 概述 - -XML 元素指的是从(且包括)开始标签直到(且包括)结束标签的部分。 - -一个元素可以包含: - -* 其他元素 -* 文本 -* 属性 -* 或混合以上所有... - -#### 元素命名规则 - -XML 元素必须遵循以下命名规则: - -* 名称可以包含字母、数字以及其他的字符 -* 名称不能以数字或者标点符号开始 -* 名称不能以字母 xml(或者 XML、Xml 等等)开始 -* 名称不能包含空格 - -可使用任何名称,没有保留的字词。 - -### 6.4、XML解析 - -。。。。 - -# 7、Tomcat - -## 7.1、JavaWeb概述 - -### 概念 - -javaweb是指所有通过java语言编写的可以通过浏览器访问的程序的总称,叫javaweb - -javaweb是基于请求和响应来开发的 - -* 请求:客户端给服务器发送数据,叫请求request -* 响应:服务器给客户端回传数据,叫响应response - -请求和响应是成对出现的,有请求就有响应 - -![image-20210731113642368](https://i.loli.net/2021/07/31/9mdOKJ47HFMA3p8.png) - -### Web资源分类 - -根据实现的技术和呈现的效果不同,分为静态资源和动态资源 - -* 静态:html、css、js、txt、MP4视频、jpg图片 -* 动态:jsp页面、Servlet程序 - -### web服务器 - -其他常用的web服务器: - -1. Tomcat -2. Jboss - -3. GlassFish - -4. Resin - -5. Weblogic - -## 7.2、Tomcat概述 - -由Apache组织提出的一种web服务器,**提供对jsp和Servlet的支持**,是一种**轻量级**的javaweb容器(服务器),也是当前应用最广的javaweb服务器,并且**免费** - -> 与servlet版本对应关系 - -![image-20210731114737054](https://i.loli.net/2021/07/31/ZLPvEwz2aJoKVrR.png) - -Servlet程序从2.5版本是现在市面使用最多的版本(xml配置) - -Servlet3.0以后,就是注解的版本 - -## 7.3、Tomcat使用 - -### 安装 - -官网链接:http://tomcat.apache.org/ -选择download,找到自己所要的版本,下载对应版本的Tomcat。 - -有zip和exe两种格式的,zip(64-bit Windows zip(pgp,md5,sha1))是免安装版的,exe(32-bit/64-bit Windows Service installer(pgp,md5,sha1))是安装版。同时观察自己的电脑是64位系统还是32位系统。 - -![在这里插入图片描述](https://img-blog.csdnimg.cn/img_convert/12dcf4419fef39c06c0fb63464792779.png) - -### 安装目录介绍 - -![image-20210731123357654](https://i.loli.net/2021/07/31/WLstoRXBVlYF6y9.png) - -### 启动 - -1. 方法1:双击bin目录下的startup.bat - -2. 方法2:在bin目录下打开命令行,输入catalina run命令 - -### 测试 - -打开浏览器,键入 http://localhost:8080 进入tomcat页面则表示安装成功 - -### 停止 - -双击bin目录下的shutdown.bat - -### 启动界面中文乱码 - -修改C:\apache-tomcat-10.0.8\conf\logging.properties文件中的java.util.logging.ConsoleHandler.encoding = UTF-8为java.util.logging.ConsoleHandler.encoding = GBK - -### 修改Tomcat端口号 - -mysql默认端口号3306 - -Tomcat默认端口号8080 - -修改C:\apache-tomcat-10.0.8\conf\server.xml - -修改 -``` - -启动时会根据path加载abc配置文件,根据dacBase在E盘找到book工程目录 - -访问: - -浏览器输入 http://localhost:8080/abc/index.html - -### 手拖浏览器页面打开和浏览器地址打开页面的区别 - -手拖使用的是file://协议 - -浏览器地址访问使用的是:http协议 - -### 默认工程访问 - -浏览器地址:http:/ip:port/ 没有工程名的时候,默认访问root工程 - -浏览器地址:http:/ip:port/工程名/ 没有资源名的时候,默认访问index.html页面 - -### IDEA配置Tmocat - -… - -# 8、Servlet - -## 8.1、概述 - -JavaWeb三大组件: - -1. Servlet程序 -2. Fliter过滤器 -3. Listener监听器 - -servlet是javaEE规范之一,规范就是接口 - -Servlet是运行在服务器上的一个java小程序,通过HTTP接收和响应客户端发送过来的请求 - -## 8.2、手动实现Servlet程序 - -1. 编写一个类去实现servlet接口 -2. 实现service方法,处理请求,并响应数据 -3. 到web.xml中去配置servlet程序的访问地址 - -web.xml配置文件: - -```xml - - - - - - Hello - - com.example.demo.Hello - - - - - Hello - - /h - - -``` - - - -### Tomcat 10.0.4 构建类servlet报错:类HelloServlet不是Servlet - -tomcat10有个最大的变动就是包名javax.servlet改成了"jakarta.servlet - -## 8.3、Servlet声明周期 - -1. 执行servlet构造器方法 -2. init方法 -3. service方法 -4. destory方法 - -1和2在第一次访问的时候创建servlet程序会调用 - -3是每次访问都会调用 - -4在web工程停止时调用 - -## 8.4、Get、Post请求分发处理 - -servelet接口实现类: - -```java -package com.example.demo; - -import jakarta.servlet.*; -import jakarta.servlet.http.HttpServletRequest; - -import java.io.IOException; - -public class Hello implements Servlet { - public Hello() { - System.out.println("构造函数"); - } - - @Override - public void init(ServletConfig servletConfig) throws ServletException { - System.out.println("init"); - } - - @Override - public ServletConfig getServletConfig() { - return null; - } - - @Override - public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { -// 获取servletRequest的子类型,因为子类型中有getmethd方法 - HttpServletRequest hsr = (HttpServletRequest) servletRequest; - //获取请求的方式 - String method = hsr.getMethod(); - //输出请求方式是get还是post,根据不同的请求做不同的事情 - if ("GET".equals(method)) { - doGet(); - } else if ("POST".equals(method)) { - doPost(); - } - } - //post方法 - private void doPost() { - System.out.println("POST请求"); - } - //get方法 - private void doGet() { - System.out.println("get请求"); - } - - @Override - public String getServletInfo() { - return null; - } - - @Override - public void destroy() { - System.out.println("destory"); - - } -} -``` - -```html - - - - - Title - - -
- -
- - -``` - -```xml - - - - - - Hello - - com.example.demo.Hello - - - - - Hello - - /h - - -``` - -## 8.5、HttpServlet类 - -一般在实际开发项目中,都是使用继承httpservlet类的方式实现servlet程序 - -httpservlet简单,已经实现了get和post的分发处理 - -1. 继承httpservlet类 -2. 根据业务需要重写doGet或者doPost方法 - -```java -package com.example.demo; - -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServlet; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - -import java.io.IOException; - -public class Hello2 extends HttpServlet { - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - System.out.println("doget"); - } - - @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - System.out.println("dopost"); - } -} -``` - -3. web.xml中配置servlet程序的访问地址 - -```xml - - - - - - hello2 - com.example.demo.Hello2 - - - hello2 - /h2 - - -``` - -4. 编写html - -```html - - - - - Title - - -
- -
- - -``` - -## 8.6、Servlet类继承体系 - -![image-20210731211509936](https://i.loli.net/2021/07/31/et1soMiyUY8fFPR.png) - -## 8.7、ServletConfig类 - -Servlet程序的配置信息类 - -作用: - -1. 获取Servlet程序的别名Servlet-name值 -2. 初始化init-param -3. 获取servletcontext对象 - -```java -public class Hello implements Servlet { - public Hello() { - System.out.println("构造函数"); - } - - @Override - public void init(ServletConfig servletConfig) throws ServletException { - System.out.println("init"); - //1. 获取Servlet程序的别名Servlet-name值 - System.out.println("别名是:"+servletConfig.getServletName());//别名是:hello - //2. 初始化init-param - System.out.println("init-parm:"+servletConfig.getInitParameter("name1"));//init-parm:tian - //3. 获取ServletContext对象 - System.out.println(servletConfig.getServletContext()); - } - //此处略去其他的代码 -} -``` - -## 8.8、ServletContext类 - -* 是一个接口,表示Servlet上下文对象 -* 一个web工程只有一个servletcontext对象实例 -* servletcontext对象是一个域对象 - -### 域对象 - -域对象是可以像map一样存取数据的对象,域i指的是存取数据的操作范围 - -### 作用 - -1. 获取web.xml中配置的 上下文参数context-param -2. 获取当前的工程路径,格式:/工程路径 -3. 获取工程部署后在服务器硬盘上的绝对路径 -4. 像map一样存取数据 - -```java -package com.example.demo; - -import jakarta.servlet.ServletConfig; -import jakarta.servlet.ServletContext; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServlet; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - -import java.io.IOException; - -public class Hello2 extends HttpServlet { - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - System.out.println("doget"); - } - - @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - System.out.println("dopost"); - //获取servletcontext对象 - ServletContext servletContext = getServletConfig().getServletContext(); - //获取上下文参数context-param - System.out.println(servletContext.getInitParameter("namespace"));; - //获取当前的工程路径,格式:/工程路径 - System.out.println(servletContext.getContextPath()); - //获取工程部署后在服务器硬盘上的绝对路径 - System.out.println(servletContext.getRealPath("/")); - } -} -``` - -## 8.9、HTTP协议 - -### http协议概述 - -客户端和服务器之间通信时,发送的数据,需要遵循的规则,叫HTTP协议 - -HTTP协议中的数据又叫报文 - -**HTTP协议格式** - -* 请求:客户端给服务器发送数据 - * get请求 - * post请求 -* 响应:服务器给客户端回传数据 - -### 请求的HTTP协议格式: - -#### GET请求 - -* 请求行 - * 请求的方式:GET - * 请求的资源路径[+?+请求参数] - * 请求的协议版本号 HTTP/1.1 -* 请求头:key:value组成,不同的键值对表示不同的涵义 - -![image-20210731222752388](https://i.loli.net/2021/07/31/VHux2IzkNrqaPv9.png) - -#### Post请求 - -* 请求行 - * 请求的方式:POST - * 请求的资源路径[+?+请求参数] - * 请求的协议版本号 HTTP/1.1 -* 请求头 - * key:value组成,不同的键值对表示不同的涵义 -* 空行 -* 请求体:发送给服务器的数据 - -![image-20210731223112742](https://i.loli.net/2021/07/31/86rKuwIFHBvNRsy.png) - -#### 常用请求头说明 - -![image-20210731223237993](https://i.loli.net/2021/07/31/YIEvoh4SmXNpiJA.png) - -#### 请求区分 - -**GET请求:** - -* form标签 method=get -* a标签 -* link标签引入css -* Script标签引入js文件 -* img标签引入图片 -* iframe引入html页面 -* 在浏览器地址栏输入地址后敲回车 - -**POST请求** - -* form标签 method=post - -### 响应的HTTP协议格式 - -* 响应行 - * 响应的协议和版本号 - * 响应状态码 - * 响应状态描述符 -* 响应头 - * key:value 不同的响应头有不同的涵义 -* 空行 -* 响应体: 回传给客户端的数据 - -![image-20210731224119102](https://i.loli.net/2021/07/31/coN1dhXbDsuBmw8.png) - -#### 常见的响应码说明 - -* 200:表示请求成功 -* 302:表示请求重定向 -* 404:表示请求服务器已经收到了,但是要的数据不存在(请求地址错误) -* 500:表示服务器已经收到请求,但是服务器内部错误(代码错误) - -## 8.10、HttpServletRequest类 - -### 作用 - -每次只要有请求进入tomcat服务器,服务器就会把请求过来的HTTP协议信息解析好封装到Request对象中,然后传递到service方法(doGet和doPost)中给我们使用,可以通过HttpServletRequest对象获取到所有请求的信息 - -### 常用方法 - -| 方法 | 功能 | -| ------------------- | ------------------------------------ | -| req.getRequestURI() | 获取请求的资源路径 | -| req.getRequestURL() | 获取请求的统一资源定位符(绝对路径) | -| req.getRemoteHost() | 获取客户端的ip地址 | -| req.getHeader() | 获取请求头 | -| req.getMethod() | 获取请求的方式GET或者POST | -| req.getParameter() | 获取请求参数 | - -```java -package com.example.servletdemo; - - -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServlet; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - -import java.io.IOException; - -public class RequestServletDemo extends HttpServlet { - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - //i.getRequestURI() 获取请求的资源路径 - System.out.println(req.getRequestURI()); // /Request_servlet/r - //i.getRequestURL() 获取请求的统一资源定位符(绝对路径) - System.out.println(req.getRequestURL()); // http://localhost:8080/Request_servlet/r - //i.getRemoteHost() 获取客户端的ip地址 - System.out.println(req.getRemoteHost()); - //i.getHeader() 获取请求头 - System.out.println(req.getHeader("USER-Agent")); - //i.getMethod() 获取请求的方式GET或者POST - System.out.println(req.getMethod()); - } - - @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - super.doPost(req, resp); - } -} -``` - -#### req.getParameter() - -```java -package com.example.servletdemo; - - -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServlet; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - -import java.io.IOException; - -public class RequestServletDemo extends HttpServlet { - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - String username = req.getParameter("username"); - String password = req.getParameter("password"); -// String hobby = req.getParameter("hobby"); //适用于单个值 - String[] hobbies = req.getParameterValues("hobby"); //适用于多个值 - System.out.println(username); - System.out.println(password); - for (String hobby : hobbies) { - System.out.println(hobby); - } - } -} -``` - -```html - - - - - Title - - -
- 用户名:
- 密码:
- 兴趣爱好:C++ - C - Java
- -
- - -``` - -### post请求中文乱码问题 - -在dopost函数中设置请求体的字符集为UTF-8 - -```java -protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - //设置设置请求体的字符集为UTF-8,解决post请求的中文乱码问题 - req.setCharacterEncoding("UTF-8"); - //省略其他 -} -``` - -### 请求转发 - -服务器收到请求后从一个资源跳转到另一个资源 - -![image-20210803152530155](https://gitee.com/tianzhendong/img/raw/master//images/image-20210803152530155.png) - -特点: - -1. 浏览器地址栏没有发生变化 -2. 是一次请求 -3. 共享request域中的数据 -4. 可以转发到web-inf目录下 -5. 不可以访问工程以外的目录 - -Servlet1: - -```java -package com.example.javaWeb; - -import javax.servlet.RequestDispatcher; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * @author TianZhendong - * @date 2021/8/3 - */ -public class Servlet1 extends HttpServlet { - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - // 获取请求的参数,(办事的材料)查看 - String username = req.getParameter("username"); - System.out.println("请求的参数为:" + username); - // 给材料盖章 - req.setAttribute("key1", "servlet1"); - // 获取转发的地址 - /* - * 请求转发必须以斜杠开头 - * / 表示http://ip:port/工程名/ - * */ - RequestDispatcher requestDispatcher = req.getRequestDispatcher("/s2"); - // 转向servlet2 - requestDispatcher.forward(req, resp); - } -} -``` - -servlet2: - -```java -package com.example.javaWeb; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * @author TianZhendong - * @date 2021/8/3 - */ -public class Servlet2 extends HttpServlet { - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - // 获取请求参数 - String username = req.getParameter("username"); - System.out.println("请求参数为:" + username); - // 查看柜台1的章 - Object key1 = req.getAttribute("key1"); - System.out.println("是否有柜台1的章:" + key1); - // 处理请求 - System.out.println("处理业务,这里省略"); - } -} -``` - -web.xml - -```xml - - servlet1 - com.example.javaWeb.Servlet1 - - - servlet2 - com.example.javaWeb.Servlet2 - - - servlet1 - /s1 - - - servlet2 - /s2 - -``` - -### base标签的作用 - -设置页面相对路径工作时参照的地址,href属性就是参数的地址值 - -```html - - - -``` - -### web中/意义 - -在web中/是一种绝对路径 - -* 被浏览器解析得到:http://ip:port/ - -```html -斜杠 -``` - -* 被服务器解析得到:http://ip:port/工程路径 - -```xml -/s -``` - -```java -servletContext.getRealPath("/"); -``` - -## 8.11、HttpServletResponse类 - -### 概述 - -和HttpServletRequest一样,每次请求进来,tomcat服务器都会创建一个response对象传递给servlet程序使用,HttpServletRequest表示请求过来的信息,HttpServletResponse表示相应的信息 - -### 两个输出流 - -* 字节流:getOutputStream();常用有下载(传递二进制数据) -* 字符流:getWriter()常用于回传字符串(常用) - -两个流同时只能使用一个 - -### 往客户端回传数据 - -```java -package com.example.javaWeb; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.PrintWriter; - -/** - * @author TianZhendong - * @date 2021/8/3 - */ -public class ResponseIoServlet extends HttpServlet { - @Override - public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { - // 设置服务器和客户端使用UTF-8字符集,解决乱码问题 - response.setContentType("text/html; charset=UTF-8"); - System.out.println(response.getCharacterEncoding()); - - // 回传数据 - PrintWriter writer = response.getWriter(); - writer.write("田真帅"); - } -} -``` - -### 请求重定向 - -#### 概念 - -客户端给服务器发送请求,服务器给客户端一些地址,让客户端去新地址访问(之前的地址可能被废弃) - -![image-20210803170323153](https://gitee.com/tianzhendong/img/raw/master//images/image-20210803170323153.png) - -#### 特点 - -1. 浏览器地址栏有变化 -2. 两次请求 -3. 不共享resquest域中的数据 -4. 不能访问web-inf下的资源 -5. 可以访问工程以外的数据,如百度 - -#### 使用 - -resonse1 - -```java -package com.example.javaWeb; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * @author TianZhendong - * @date 2021/8/3 - */ -public class Response1 extends HttpServlet { - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - System.out.println("访问了源地址response1"); - // 设置相应码的状态,表示重定向 - resp.setStatus(302); - // 设置响应头,说明新的地址在哪里 - resp.setHeader("Location", "http://localhost:8080/demo1/response2"); - } -} -``` - -response2 - -```java -package com.example.javaWeb; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * @author TianZhendong - * @date 2021/8/3 - */ -public class Response1 extends HttpServlet { - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - System.out.println("访问了源地址response1"); - // 设置相应码的状态,表示重定向 - resp.setStatus(302); - // 设置响应头,说明新的地址在哪里 - // 可以访问 - resp.setHeader("Location", "http://www.baidu.com"); - // resp.setHeader("Location", "http://localhost:8080/demo1/response2"); - } -} -``` - -#### 简单写法sendRedirect() - -```java -package com.example.javaWeb; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * @author TianZhendong - * @date 2021/8/3 - */ -public class Response1 extends HttpServlet { - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - System.out.println("访问了源地址response1"); - resp.sendRedirect("http://www.baidu.com"); - } -} -``` - -# 9、jsp - -## 9.1、概述 - -全称是java serverpages:java服务器页面,用于动态web - -作用:代替servlet程序回传html页面的数据 - -已逐渐被淘汰 - -> 特点 - -* jsp页面可以嵌入java代码,为用户提供动态数据 -* html只提供静态 - -> 原理 - -* 浏览器向服务器发送请求,不管访问什么资源,都是在访问servlet -* jsp最终会被转换成一个java类 -* JSP本质上就是一个servlet -* jsp页面中的java代码会原封不动的输出,html代码会被转换为`out.write("\r\n")` - -> 依赖 - -```xml - - - - javax.servlet - servlet-api - 2.5 - - - - javax.servlet.jsp - javax.servlet.jsp-api - 2.3.3 - - - - javax.servlet.jsp.jstl - jstl-api - 1.2 - - - - taglibs - standard - 1.1.2 - - -``` - -## 9.2、JSP语法 - -> JSP表达式 - -```jsp -<%= 表达式 %> -``` - -```jsp -

- 今天的日期是: <%= (new java.util.Date()).toLocaleString()%> -

-``` - -> JSP注释 - -```jsp -<%-- 该部分注释在网页中不会被显示--%> -``` - -> JSP脚本程序 - -脚本程序可以包含任意量的Java语句、变量、方法或表达式,只要它们在脚本语言中是有效的。 - -脚本程序的语法格式: - -```jsp -<% 代码片段 %> -``` - -```jsp -<% -out.println("Your IP address is " + request.getRemoteAddr()); -%> -``` - -> JSP声明 - -一个声明语句可以声明一个或多个变量、方法,供后面的Java代码使用。在JSP文件中,您必须先声明这些变量和方法然后才能使用它们。 - -JSP声明的语法格式: - -```jsp -<%! declaration; [ declaration; ]+ ... %> -``` - -```jsp -<%! int i = 0; %> -<%! int a, b, c; %> -<%! Circle a = new Circle(2.0); %> -``` - - - - - - - - - - - - - - - - - - - - - - - -## 9.2、EL表达式 - -expression language表达式语言 - -作用:代替jsp页面中的表达式脚本在jsp页面中进行数据的输出 - -## 9.3、jstl标签库 - -jsp标准标签库,替换jsp中的代码脚本,使得jsp页面变得更加简洁 - -# 10、 文件的上传和下载 - -## 10.1、文件的上传 - -1. 有一个form标签,method=post请求 -2. form标签的encType属性值必须为multipart/form-data值 -3. 在form标签中使用input type=file添加上传的文件 -4. 编写服务器代码接收,处理上传的数据 - -## 10.2、文件的下载 - - - -# 11、Cookie和Session - -## 11.1、Cookie - -### 概述 - -服务器通知客户端保存键值对的一种技术 - -客户端有了cookie后,每次请求都发送给服务器 - -每个Cookie的大小不能超过4kb - -### 使用 - - - -## 11.2、Session - -- Session就一个接口(HttpSession) -- Session就是会话,用来伟华客户端和服务器之间关联的一种技术 -- 每个客户端都有自己的一个Session会话 -- Session会话中,经常用来保存用户登陆之后的信息 - -# 12、Filter过滤器 - -## 概述 - -* javaweb三个组件之一,servlet、listener、filter -* filter过滤器是javaee的规范,也就是接口 -* 作用:拦截请求,过滤响应 - -# 13、json - -## 13.1、概述 - -JSON(javascript object notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。JSON采用完全独立于语言的文本格式,很多语言都提供了对json的支持,使得json成为理想的数据交换格式 - -轻量级指的是跟xml作比较 - -数据交换指的是客户端和服务器i之间业务数据的传输格式 - -## 13.2、JSON语法 - -JSON 语法是 JavaScript 语法的子集。 - -### JSON 语法规则 - -JSON 语法是 JavaScript 对象表示语法的子集。 - -- 数据在名称/值对中 -- 数据由逗号分隔 -- 大括号 **{}** 保存对象 -- 中括号 **[]** 保存数组,数组可以包含多个对象 - -### JSON 值 - -JSON 值可以是: - -- 数字(整数或浮点数) -- 字符串(在双引号中) -- 逻辑值(true 或 false) -- 数组(在中括号中) -- 对象(在大括号中) -- null - -```html - - - - - JSON - - -

- 姓名:
- 年龄:
- 学校:
-

- - - -``` - -![image-20210804154554069](https://gitee.com/tianzhendong/img/raw/master//images/image-20210804154554069.png) - -## 13.3、JSON vs XML - -JSON 和 XML 都用于接收 web 服务端的数据。 - -JSON 和 XML在写法上有所不同,如下所示: - -```json -{ - "sites": [ - { "name":"菜鸟教程" , "url":"www.runoob.com" }, - { "name":"google" , "url":"www.google.com" }, - { "name":"微博" , "url":"www.weibo.com" } - ] -} -``` - -```xml - - - 菜鸟教程 www.runoob.com - - - google www.google.com - - - 微博 www.weibo.com - - -``` - -## 13.4、JSON.parse() - -JSON字符串转换为JSON对象 - -JSON 通常用于与服务端交换数据。 - -在接收服务器数据时一般是字符串。 - -我们可以使用 JSON.parse() 方法将数据转换为 JavaScript 对象。 - -```json -JSON.parse(text[, reviver]) -``` - -**参数说明:** - -- **text:**必需, 一个有效的 JSON 字符串。 -- **reviver:** 可选,一个转换结果的函数, 将为对象的每个成员调用此函数。 - -```js -var obj = JSON.parse('{ "name":"runoob", "alexa":10000, "site":"www.runoob.com" }'); -``` - -## 13.5、JSON.stringify() - -JSON对象转换为JSON字符串 - -例如我们向服务器发送以下数据 - -```js -var obj = { "name":"runoob", "alexa":10000, "site":"www.runoob.com"}; -var myJSON = JSON.stringify(obj); -document.getElementById("demo").innerHTML = myJSON; -``` - -## 13.6、JSON使用 - -```html - - - - -菜鸟教程(runoob.com) - - -

从 JSON 字符串中创建对象

-

-网站名:
-网站地址:
-

- - - -``` - -## 13.7、JSON在java中使用 - -### JavaBean和JSON转换 - -* toJson()方法可以把java对象转换成json字符串 - -* fromJson()相反 - -```java -Person person = new Person(1, "测试"); -//创建GSON对象实例 -Gson gson = new Gson(); -//java对象转换成json字符串 -String s = gson.toJson(person); -//json字符串转换为java对象 -Person person1 = gson.fronJson(s,Person.class); -``` - -# 14、AJAX请求 - -## 14.1、概述 - -AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。 - -AJAX 不是新的编程语言,而是一种使用现有标准的新方法。 - -AJAX 最大的优点是在**不重新加载整个页面的情况下**,可以与服务器交换数据并更新部分网页内容。 - -AJAX 不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。 - -**浏览器通过js异步发起请求,局部更新页面的技术** - -## 14.2、使用 - -* 第一步:创建 XMLHttpRequest 对象 - -XMLHttpRequest 用于在后台与服务器交换数据。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。 - -```js -var xmlhttp; -if (window.XMLHttpRequest) -{ - // IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码 - xmlhttp=new XMLHttpRequest(); -} -else -{ - // IE6, IE5 浏览器执行代码 - xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); -} -``` - -* 第二步:向服务器发送请求 - -XMLHttpRequest 对象用于和服务器交换数据。 - -如需将请求发送到服务器,我们使用 **XMLHttpRequest** 对象的 **open()** 和 **send()** 方法: - -```js -xmlhttp.open("GET","ajax_info.txt",true); -xmlhttp.send(); -``` - -| 方法 | 描述 | -| :--------------------------- | :----------------------------------------------------------- | -| open(*method*,*url*,*async*) | 规定请求的类型、URL 以及是否异步处理请求。*method*:请求的类型;GET 或 POST*url*:文件在服务器上的位置*async*:true(异步)或 false(同步) | -| send(*string*) | 将请求发送到服务器。*string*:仅用于 POST 请求 | - -如果来自服务器的响应并非 XML,请使用 responseText 属性。 - -responseText 属性返回字符串形式的响应,因此您可以这样使用: - -```js -document.getElementById("myDiv").innerHTML=xmlhttp.responseText; -``` - -如果来自服务器的响应是 XML,而且需要作为 XML 对象进行解析,请使用 responseXML 属性: - -```js -xmlDoc=xmlhttp.responseXML; -txt=""; -x=xmlDoc.getElementsByTagName("ARTIST"); -for (i=0;i"; -} -document.getElementById("myDiv").innerHTML=txt; -``` - diff --git a/source/_posts/Java/MySql.md b/source/_posts/Java/MySql.md deleted file mode 100644 index e4dc01c..0000000 --- a/source/_posts/Java/MySql.md +++ /dev/null @@ -1,2091 +0,0 @@ ---- -title: MySql -author: TianZD -top: true -cover: true -toc: true -mathjax: false -summary: 《MySQL》必知必会学习笔记 -tags: - - 数据库 - - MySql - - 学习笔记 -categories: - - 数据库 -reprintPolicy: cc_by -abbrlink: 6cac71de -date: 2022-04-29 10:45:40 -coverImg: -img: -password: ---- - - - -[toc] - -# 1、了解MySQL - -## 数据库(DB,DataBase) - -保存有组织的数据的容器(通常是一个文件或者一组文件) - -注:数据库不同于数据库软件,数据库软件应成为DBMS(数据库管理系统) - -数据库分类: - -* 关系型数据库SQL:通过表和表之间,行和列之间的关系进行数据的存储 - * MySQL - * Oracle - * Sql Server - * DB2 - * SQLlite -* 非关系型数据库NoSQL(Not Only SQL):对象存储,通过对象的自身的属性决定 - * Redis - * MongDB - -### 表 - -某种特定类型数据的结构化清单 - -### 列和数据类型 - -列:表中的一个字段。所有表都是由一个或多个列组成的。 - -数据类型(datatype) 所容许的数据的类型。每个表列都有相应的数据类型,它限制(或容许)该列中存储的数据。 - -### 行 - -行(row) 表中的一个记录。 - -### 主键 - -主键(primary key) 一列(或一组列),其值能够唯一区分表中每个行。 - -## SQL - -### SQL简介 - -SQL(发音为字母S-Q-L或sequel)是结构化查询语言(Structured QueryLanguage)的缩写。 SQL是一种专门用来与数据库通信的语言 - -数据的所有存储、检索、管理和处理实际上是由数据库软件——DBMS( 数据库管理系统)完成的。 MySQL是一种DBMS,即它是一种数据库软件。 - -### 客户机-服务器软件 - -DBMS可分为两类:一类为基于共享文件系统的DBMS,另一类为基于客户机—服务器的DBMS。 前者(包括诸如Microsoft Access和FileMaker)用于桌面用途,通常不用于高端或更关键的应用。 - -MySQL、 Oracle以及Microsoft SQL Server等数据库是基于客户机—服务器的数据库。客户机—服务器应用分为两个不同的部分。 服务器部分是负责所有数据访问和处理的一个软件。这个软件运行在称为数据库服务器的计算机上。 -与数据文件打交道的只有服务器软件。关于数据、数据添加、删除和数据更新的所有请求都由服务器软件完成。这些请求或更改来自运行客户机软件的计算机。 客户机是与用户打交道的软件。例如,如果你请求一个按字母顺序列出的产品表,则客户机软件通过网络提交该请求给服务器软件。服务器软件处理这个请求,根据需要过滤、丢弃和排序数据;然后把结果送回到你的客户机软件。 - -客户机和服务器软件可能安装在两台计算机或一台计算机上。不管它们在不在相同的计算机上,为进行所有数据库交互,客户机软件都要与服务器软件进行通信 - -## MySQL - -MySQL是一个关系型数据库管理系统,属于Oracle旗下产品,关系数据库将数据保存在不同的表中 - -* 是最好的RDBMS(关系数据库管理系统)应用软件之一 -* 开源 -* 体积小、速度快,总体拥有成本低 - -# 2、使用MySQL - -为了连接到MySQL,需要以下信息: - -* 主机名(计算机名)——如果连接到本地MySQL服务器, 为localhost; -* 端口(如果使用默认端口3306之外的端口); -* 一个合法的用户名; -* 用户口令(如果需要)。 - - -```sql ---使用root登录 -mysql -u root -p; - ---显示可用的数据库列表 -SHOW databases; - ---选择数据库 -USE crashcourse; - ---获得一个数据库内的表的列表 -SHOW tables; - ---显示数据库中所有表的信息 -describe student; - ---创建一个数据库 -create databases school; - ---显示表列 -SHOW columns from customers; - ---显示广泛的服务器状态信息 -SHOW status; - ---分别用来显示创建特定数据库或表的MySQL语句 -SHOW create database; -SHOW create table; - ---显示授予用户(所有用户或特定用户)的安全权限 -SHOW Grants; - ---显示服务器错误或警告消息 -SHOW errors; -SHOW warnings; - ---推出连接 -exit; ---表示单行注释 -/* -多行注释 -*/ -``` - -# 3、操作数据库 - -操作数据库——>操作数据库中的表——>操作数据库中表的数据 - -## 操作数据库 - -```sql ---增加数据库school1(在不存在时 -CREATE DATABASES IF NOT EXISTS school1; - ---删除数据库 -DROP DATABASE IF EXISTS school1; - ---使用数据库 -USE school; - ---查看所有的数据库 -SHOW DATABASES; -``` - -## 操作数据库表 - -### 创建数据库表 - -```sql ---创建数据库表 -CREATE TABLE [IF NOT EXISTS] `表名`( - `字段名` 列类型 [属性] [索引] [注释], - `字段名` 列类型 [属性] [索引] [注释], - `字段名` 列类型 [属性] [索引] [注释] -)[表类型] [字符集设置] [注释] ---实例 -CREATE TABLE `student` ( - `id` int NOT NULL COMMENT '学员id', - `name` varchar(100) NOT NULL COMMENT '学员姓名', - `age` int NOT NULL COMMENT '学员年龄', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 - ---查看创建数据表table的语句 -SHOW CREATE TABLE student; - ---显示表的结构 -DESC student; -``` - -ENGINE=InnoDB 表示采用的数据库引擎 - -数据库引擎主要有: - -* INNODB:安全性高,支持事务处理和多表多用户操作 -* MYISAM:节约空间,速度快 - -| | MYISAM | INNODB | -| ---------- | ------ | ------------- | -| 事务支持 | 不支持 | 支持 | -| 数据行锁定 | 不支持 | 支持 | -| 外键 | 不支持 | 支持 | -| 全文索引 | 支持 | 不支持 | -| 表空间大小 | 较小 | 较大,约为2倍 | - -所有的数据库文件都储存在data目录下,本质还是文件的存储 - -### 修改删除表 - -```sql ---修改表名 -ALTER TABLE teacher RENAME AS teacher1; - ---增加表的字段 表名 ADD 字段名 列属性 -ALTER TABLE teacher1 ADD age INT(11); - ---修改表的字段,重命名,修改约束 -ALTER TABLE teacher1 MODIFY age VARCHAR(11); --修改约束:列属性 -ALTER TABLE teacher1 CHANGE age age1 INT(1); --修改约束:重命名 - ---删除表的字段 -ALTER TABLE teacher1 DROP age1; - ---删除表 -DROP TABLE IF EXISTS teacher1; -``` - -MODIFY和CHANGE区别: - -* change用来字段重命名 -* modify不用来字段重命名,只能修改字段类型和约束 - -# 4、MySQL数据管理 - -## 外键 - -### 物理外键 - -数据库级别的外键,不建议使用,数据库之间有关联,容易导致混乱 - -1. 创建表时添加外键 - -```sql -CREATE TABLE `student` ( - `id` int NOT NULL COMMENT '学员id', - `name` varchar(100) NOT NULL COMMENT '学员姓名', - `age` int NOT NULL COMMENT '学员年龄', - `gradeid` int NOT NULL COMMENT `年级id` --年级表的主键 - PRIMARY KEY (`id`), --主键 - KEY `FK_gradeid` (`gradeid`), --外键 - CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade`(`gradeid`) --给外键添加约束 -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 -``` - -2. 创建后添加外键 - -```sql -ALTER TABLE `student` -ADD CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade`(`gradeid`) -``` - -### 最佳实践 - -* 数据库就是单纯的表,只用来存储数据,只有行(数据)和列(字段) -* 使用程序去实现外键功能 - -# 5、DML语言 - -数据库操作语言 - -* insert添加 -* update修改 -* delete删除 - -## insert添加 - -```sql ---插入语句 ---insert into `表名` (`字段`) values('value1'),('value2')... -INSERT INTO `student`('name') -VALUES ('zhansan'), -('lisi'); - -INSERT INTO `student`('name','sex') -VALUES ('zhansan','男'), -('lisi','男'); -``` - -字段可以省略 - -### 插入完整的行 - -```sql -INSERT INTO customers(cust_name, - cust_contact, - cust_email, - cust_address, - cust_city, - cust_state, - cust_zip, - cust_country) -VALUES('Pep E.LaPew', - NULL, - NULL, - '100 Main Street', - 'Los Angeles', - 'CA', - '90046', - 'USA'); -``` - -### 插入多个列 - -如果插入的顺序相同,那么可以在VALUES后再加一个括号,把另外的数据加上 - -### 插入检索出的数据 - -INSERT SELECT - -```sql ---使用INSERT SELECT从custnew中将所有数据导入customers -INSERT INTO customers(cust_name, - cust_contact, - cust_email, - cust_address, - cust_city, - cust_state, - cust_zip, - cust_country) -SELECT cust_name, - cust_contact, - cust_email, - cust_address, - cust_city, - cust_state, - cust_zip, - cust_country -FROM custnew; -``` - -## update修改 - -```sql ---修改单个属性 -UPDATE `student` --修改的表 -SET `name`='tianzhendong' --修改的内容 -WHERE id=1; --修改的具体行,不指定条件会修改所有的行 - ---修改多个属性 -UPDATE `student` -SET `name`='tianzhendong',`email`='xxxx@qq.com' --修改的内容 -WHERE id=1; - ---更新10005客户的邮件地址 -UPDATE customers -SET cust_email='elmer@fudd.com' -WHERE cust_id = 10005; - ---更新多个列 -UPDATE customers -SET cust_email='120@fudd.com', - cust_name = 'tian' -WHERE cust_id = 10005; - ---删除某个列的值,可设置其为NULL -UPDATE customers -SET cust_email=NULL -WHERE cust_id = 10005; -``` - -## 删除 - -### delete - -```sql ---删除数据(避免这样写,会全部删除) -DELETE FROM `student`; - ---删除指定数据 -DELETE FROM `student` -WHERE id = 1; -``` - -### truncate - -可使用TRUNCATE TABLE语句,它完成相同的工作,但速度更快( TRUNCATE实际是删除原来的表并重新创建一个表,而不是逐行删除表中的数据)。 - -```sql ---清空student表,表的结构和索引约束不会变 -TRUNCATE `student`; -``` - -### 区别 - -* 相同点:都能删除数据,都不会删除表结构 - -* 不同: - * truncate**重新设置自增列,计数器归零**,delete不会影响自增 - * truncate不会影响事务 - -# 6、DQL查询数据 - -## 检索数据 - -```sql ---检索单个列 -SELECT prod_name -FROM products; - ---检索多个列 -SELECT prod_id, prod_name, prod_price -FROM products; - ---检索所有列 -SELECT * -FROM products; - ---只返回列中不同(唯一)的vend_id行 -SELECT DISTINCT vend_id -FROM products; - ---返回第一行或前几行 -SELECT prod_name -FROM products -LIMIT 5; - ---返回从行5开始的5行,检索出来的第一行为行0而不是行1。因此, LIMIT 1, 1将检索出第二行而不是第一行。如果没有足够的行只返回它能返回的那么多行。 -SELECT prod_name -FROM products -LIMIT 5,5; - ---使用完全限定的名字来引用列(同时使用表名和列字) -SELECT products.prod_name -FROM crashcourse.products; -``` - -多条SQL语句必须以分号(;)分隔。 MySQL如同多数DBMS一样,不需要在单条SQL语句后加分号。但特定的DBMS可能必须在单条SQL语句后加上分号。当然,如果愿意可以总是加上分号。事实上,即使不一定需要,但加上分号肯定没有坏处。如果你使用的是mysql命令行,必须加上分号来结束SQL语句。 - -SQL语句不区分大小写,因此SELECT与select是相同的。同样,写成Select也没有关系。 -许多SQL开发人员喜欢对所有SQL关键字使用大写,而对所有列和表名使用小写,这样做使代码更易于阅读和调试。 - -在处理SQL语句时,其中所有空格都被忽略。 SQL语句可以在一行上给出,也可以分成许多行。多数SQL开发人 -员认为将SQL语句分成多行更容易阅读和调试 - -## 排序检索数据 - -子句(clause) SQL语句由子句构成,有些子句是必需的,而有的是可选的。一个子句通常由一个关键字和所提供的数据组成。子句的例子有SELECT语句的FROM子句。 - -为了明确地排序用SELECT语句检索出的数据,可使用ORDER BY子句。 -ORDER BY子句取一个或多个列的名字,据此对输出进行排序。 - -```java ---指示MySQL对prod_name列以字母顺序排序数据 -SELECT prod_name -FROM products -ORDER BY prod_name; - ---检索3个列,并按其中两个列对结果进行排序——首先按价格,然后再按名称排序 -SELECT prod_id, prod_price, prod_name -FROM products -ORDER BY prod_price, prod_name;--仅在多个行具有相同的prod_price值时才对产品按prod_name进行排序 - ---按价格以降序排序产品(最贵的排在最前面) -SELECT prod_id, prod_price, prod_name -FROM products -ORDER BY prod_price DESC; - ---DESC关键字只应用到直接位于其前面的列名。 -SELECT prod_id, prod_price, prod_name -FROM products -ORDER BY prod_price DESC, prod_name; ---在上例中,只对prod_price列指定DESC,对prod_name列不指定。因此,prod_price列以降序排序,而prod_name列(在每个价格内)仍然按标准的升序排序 - ---使用ORDER BY和LIMIT的组合,能够找出一个列中最高或最低的值 -SELECT prod_id, prod_price, prod_name -FROM products -ORDER BY prod_price DESC -LIMIT 1; -``` - -与DESC相反的关键字是ASC(ASCENDING), 在升序排序时可以指定它。 -但实际上, ASC没有多大用处,因为升序是默认的(如果既不指定ASC也不指定DESC,则假定为ASC) - -在字典( dictionary)排序顺序中,A被视为与a相同,这是MySQL(和大多数数据库管理系统)的默认行为。但是,许多数据库管理员能够在需要时改变这种行为(如果你的数据库包含大量外语字符,可能必须这样做)。 - -## 过滤数据 - -在同时使用ORDER BY和WHERE子句时,应该让ORDER BY位于WHERE之后, 否则将会产生错误 - -```JAVA ---在SELECT语句中,数据根据WHERE子句中指定的搜索条件进行过滤。 -SELECT prod_price, prod_name -FROM products -WHERE prod_price = 2.50; - ---在SELECT语句中,数据根据WHERE子句中指定的搜索条件进行过滤。 -SELECT prod_price, prod_name -FROM products -WHERE prod_price != 2.50;--等同于WHERE prod_price <> 2.50; - ---检索价格在5美元和10美元之间的所有产品 -SELECT prod_price, prod_name -FROM products -WHERE prod_price BETWEEN 5 AND 10; - ---返回没有价格(空prod_price字段,不是价格为0)的所有产品 -SELECT prod_price, prod_name -FROM products -WHERE prod_price BETWEEN IS NULL; -``` - -## 组合WHERE子句 - -```java ---AND OR IN ---检索由供应商1003制造且价格小于等于10美元的所有产品的名称和价格 -SELECT prod_id, prod_price, prod_name -FROM products -WHERE vend_id = 1003 AND prod_id <=10; - ---检索由任一个指定供应商制造的所有产品的产品名和价格 -SELECT prod_id, prod_price, prod_name -FROM products -WHERE vend_id = 1003 OR vend_id = 1002; - ---IN和OR具有相同的功能 ---检索供应商1002和1003制造的所有产品 -SELECT prod_id, prod_price, prod_name -FROM products -WHERE vend_id in(1002,1003) -ORDER BY prod_name; - --- SQL(像多数语言一样)在处理OR操作符前,优先处理AND操作符。组合使用时应使用圆括号明确地分组相应的操作符 -SELECT prod_name, prod_price -FROM products -WHERE (vend_id = 1002 OR vend_id = 1003) AND prod_price>=10; - ---NOT NOT否定跟在它之后的条件, --- 匹 配 1002 和 1003 之 外 供 应 商 的vend_id ---和!= <>好像没有区别,只是not适用于复杂的语句 -SELECT prod_id, prod_price, prod_name -FROM products -WHERE vend_id NOT IN (1002,1003) -ORDER BY prod_name; -``` - -## 用通配符进行过滤 - -前面介绍的所有操作符都是针对已知值进行过滤的。不管是匹配一个还是多个值,测试大于还是小于已知值,或者检查某个范围的值,共同点是过滤中使用的值都是已知的。但是,这种过滤方法并不是任何时候都好用。例如,怎样搜索产品名中包含文本anvil的所有产品?用简单的比较操作符肯定不行,必须使用通配符。利用通配符可创建比较特定数据的搜索模式。在这个例子中,如果你想找出名称包含anvil的所有产品,可构造一个通配符搜索模式,找出产品名中任何位置出现anvil的产品。 - -通配符:(wildcard) 用来匹配值的一部分的特殊字符。 - -搜索模式(search pattern)由字面值、通配符或两者组合构成的搜索条件。 - -为在搜索子句中使用通配符,必须使用LIKE操作符。 LIKE指示MySQL,后跟的搜索模式利用通配符匹配而不是直接相等匹配进行比较。 - -谓词 操作符何时不是操作符?答案是在它作为谓词( predicate)时。从技术上说, LIKE是谓词而不是操作符。 - -```java ---百分号( %)通配符 ---在搜索串中, %表示任何字符出现任意次数。 ---检索任意以jet起头的词。 %告诉MySQL接受jet之后的任意字符,不管它有多少字符。 -SELECT prod_id, prod_name -FROM products -WHERE prod_name LIKE 'jet%'; - ---匹配任何位置包含文本anvil的值,而不论它之前或之后出现什么字符。 -SELECT prod_id, prod_name -FROM products -WHERE prod_name LIKE '%anvil%'; - ---找出以s起头以e结尾的所有产品 -SELECT prod_id, prod_name -FROM products -WHERE prod_name LIKE 's%e'; - ---下划线( _)通配符 ---下划线的用途与%一样,但下划线只匹配单个字符而不是多个字符。_总是匹配一个字符,不能多也不能少 -``` - -注意:通配 -符搜索的处理一般要比前面讨论的其他搜索所花时间更长。不要过度使用通配符。如果其他操作符能达到相同的目的,应该使用其他操作符。 - -* 在确实需要使用通配符时,除非绝对有必要,否则不要把它们用在搜索模式的开始处。把通配符置于搜索模式的开始处,搜索起来是最慢的。 -* 仔细注意通配符的位置。如果放错地方,可能不会返回想要的数据。 - -## 正则表达式 - -MySQL中的正则表达式匹配(自版本3.23.4后)不区分大小写(即,大写和小写都匹配)。为区分大 -小写,可使用BINARY关键字,如WHERE prod_name REGEXPBINARY 'JetPack .000'。 - -LIKE匹配整个列。如果被匹配的文本在列值中出现, LIKE将不会找到它,相应的行也不被返回(除非使用 -通配符)。而REGEXP在列值内进行匹配,如果被匹配的文本在列值中出现, REGEXP将会找到它,相应的行将被返回。这是一个非常重要的差别。 - -```java ---REGEXP ---检索列prod_name包含文本1000的所有行 -SELECT prod_id, prod_name -FROM products -WHERE prod_name REGEXP '1000'; - ---检索列prod_name包含文本000的所有行, .是正则表达式语言中一个特殊的字符。它表示匹配任意一个字符,因此, 1000和2000都匹配 -且返回 -SELECT prod_id, prod_name -FROM products -WHERE prod_name REGEXP '.000'; - ---搜索两个串之一(或者为这个串,或者为另一个串),使用| -SELECT prod_id, prod_name -FROM products -WHERE prod_name REGEXP '1000|2000'; - ---匹配特定的字符 ---正则表达式[123] Ton为[1|2|3] Ton的缩写,也可以使用后者。但是,需要用[]来定义OR语句查找什么。 --- /使用了正则表达式[123] Ton。 [123]定义一组字符,它的意思是匹配1或2或3,因此, 1 ton和2 ton都匹配且返回(没有3 ton) -SELECT prod_id, prod_name -FROM products -WHERE prod_name REGEXP '[123] Ton'; - ---集合可用来定义要匹配的一个或多个字符 -SELECT prod_id, prod_name -FROM products -WHERE prod_name REGEXP '[1-3] Ton'; - ---为了匹配特殊字符,必须用\\为前导。 \\-表示查找-, \\.表示查找. -SELECT prod_id, prod_name -FROM products -WHERE prod_name REGEXP '\\.'; -``` - -### 匹配字符类: - -| 类 | 说明 | -| ---------- | ------------------------------------------------- | -| [:alnum:] | 任意字母和数字(同[a-zA-Z0-9]) | -| [:alpha:] | 任意字符(同[a-zA-Z]) | -| [:blank:] | 空格和制表(同[\\t]) | -| [:cntrl:] | ASCII控制字符( ASCII 0到31和127) | -| [:digit:] | 任意数字(同[0-9]) | -| [:graph:] | 与[:print:]相同,但不包括空格 | -| [:lower:] | 任意小写字母(同[a-z]) | -| [:print:] | 任意可打印字符 | -| [:punct:] | 既不在[:alnum:]又不在[:cntrl:]中的任意字符 | -| [:space:] | 包括空格在内的任意空白字符(同[\\f\\n\\r\\t\\v]) | -| [:upper:] | 任意大写字母(同[A-Z]) | -| [:xdigit:] | 任意十六进制数字(同[a-fA-F0-9]) | - -### 匹配多个示例 - -可以用正则表达式重复元字符来完成 - -| 元字符 | 说明 | -| ------ | ----------------------------- | -| * | 0个或多个匹配 | -| + | 1个或多个匹配(等于{1,}) | -| ? | 0个或1个匹配(等于{0,1}) | -| {n} | 指定数目的匹配 | -| {n,} | 不少于指定数目的匹配 | -| {n,m} | 匹配数目的范围( m不超过255) | - -```java -SELECT prod_name -FROM products -WHERE prod_name REGEXP '\\([0-9] sticks?\\)'; --- \\(\\)使用\\匹配特殊字符 ---sticks后跟?,使的s可有可无,可以出现0次或者1次,因此stick也会被匹配出来。 - ---匹配连在一起的4位数字 -SELECT prod_name -FROM products -WHERE prod_name REGEXP '[[:digit:]]{4}'; ---[:digit:]匹配任意数字,因而它为数字的一个集合。 {4}确切地要求它前面的字符(任意数字)出现4次,所以[[:digit:]]{4}匹配连在一起的任意4位数字。 ---等同于下面: -SELECT prod_name -FROM products -WHERE prod_name REGEXP '[0-9][0-9][0-9][0-9]'; -``` - -### 定位符 - -特定位置的文本需要使用定位符 - -| 元 字 符 | 说 明 | -| -------- | ---------- | -| ^ | 文本的开始 | -| $ | 文本的结尾 | -| [[:<:]] | 词的开始 | -| [[:>:]] | 词的结尾 | - - LIKE和REGEXP的不同在于, LIKE匹配整个串而REGEXP匹配子串。利用定位符,通过用^开始每个表达式,用$结束每个表达式,可以使REGEXP的作用与LIKE一样。 - -```java ---找出以一个数(包括以小数点开始的数)开始的所有产品 -SELECT prod_name -FROM products -WHERE prod_name REGEXP '^[0-9\\.]'; ---^匹配串的开始。因此, ^[0-9\\.]只在.或任意数字为串中第一个字符时才匹配它们 -``` - - -## 创建计算字段 - -存储在表中的数据都不是应用程序所需要的。我们需要直接从数据库中检索出转换、计算或格式化过的数据;而不是检索出数据,然后再在客户机应用程序或报告程序中重新格式化。 - ->字段(field) 基本上与列( column) 的意思相同,经常互换使用,不过数据库列一般称为列,而术语字段通常用在计算字段的连接上。 - -### 拼接字段 - -拼接(concatenate) 将值联结到一起构成单个值。在MySQL的SELECT语句中,可使用Concat()函数来拼接两个列。 - ->多数DBMS使用+或||来实现拼接,MySQL则使用Concat()函数来实现。当把SQL语句转换成MySQL语句时一定要把这个区别铭记在心。 - -```java ---Concat()拼接串,即把多个串连接起来形成一个较长的串。Concat()需要一个或多个指定的串,各个串之间用逗号分隔。 -SELECT Concat(vend_name, '(', vend_country, ')') -FROM vendors -ORDER BY vend_name; -``` - -输出: - -![image-20210726210622468](https:--gitee.com/tianzhendong/img/raw/master--images/image-20210726210622468.png) - - -```java ---删除数据右侧多余的空格来整理数据,这可以使用MySQL的RTrim()函数来完成 -SELECT Concat(Rtrim(vend_name), '(', Rtrim(vend_country), ')') -FROM vendors -ORDER BY vend_name; -``` - ->Trim函数 MySQL除了支持RTrim()(正如刚才所见,它去掉串右边的空格),还支持LTrim()(去掉串左边的空格)以及Trim()(去掉串左右两边的空格) - -### 别名 - -上述拼接过后实际上它没有名字,它只是一个值。如果仅在SQL查询工具中查看一下结果,这样没有什么不好。但是,一个未命名的列不能用于客户机应用中,因为客户机没有办法引用它。 - -为了解决这个问题, SQL支持列别名。 别名( alias) 是一个字段或值的替换名。别名用AS关键字赋予。 - -```java -SELECT Concat(Rtrim(vend_name), '(', Rtrim(vend_country), ')') AS vend_title -FROM vendors -ORDER BY vend_name; -``` - -### 执行算数计算 - -```java ---汇总物品的价格(单价乘以订购数量) -SELECT prod_id, quantity, item_price, quantity*item_price AS expanded_price -FROM orderitems -where order_num = 20005; -``` - -输出: - -![image-20210726210649949](https:--gitee.com/tianzhendong/img/raw/master--images/image-20210726210649949.png) - -## 使用数据处理函数 - -与其他大多数计算机语言一样, SQL支持利用函数来处理数据。函数一般是在数据上执行的,它给数据的转换和处理提供了方便。 - -### 使用函数 - -大多数SQL实现支持以下类型的函数。 - -* 用于处理文本串(如删除或填充值,转换值为大写或小写)的文本函数。 -* 用于在数值数据上进行算术操作(如返回绝对值,进行代数运算)的数值函数。 -* 用于处理日期和时间值并从这些值中提取特定成分(例如,返回两个日期之差,检查日期有效性等)的日期和时间函数。 -* 返回DBMS正使用的特殊信息(如返回用户登录信息,检查版本细节)的系统函数。 - -### 文本处理函数 - -| 函 数 | 说 明 | -| ----------- | ----------------- | -| Left() | 返回串左边的字符 | -| Length() | 返回串的长度 | -| Locate() | 找出串的一个子串 | -| Lower() | 将串转换为小写 | -| LTrim() | 去掉串左边的空格 | -| Right() | 返回串右边的字符 | -| RTrim(a) | 去掉串右边的空格 | -| Soundex() | 返回串的SOUNDEX值 | -| SubString() | 返回子串的字符 | -| Upper(a) | 将串转换为大写 | - -SOUNDEX是一个将任何文本串转换为描述其语音表示的字母数字模式的算法。 SOUNDEX考虑了类似的发音字符和音节,使得能对串进行发音比较而不是字母比较。 - -### 日期和时间处理函数 - -日期和时间采用相应的数据类型和特殊的格式存储,以便能快速和有效地排序或过滤,并且节省物理存储空间。 - -一般,应用程序不使用用来存储日期和时间的格式,因此日期和时间函数总是被用来读取、统计和处理这些值。 - -| 函 数 | 说 明 | -| ------------- | ------------------------------ | -| AddDate() | 增加一个日期(天、周等) | -| AddTime() | 增加一个时间(时、分等) | -| CurDate() | 返回当前日期 | -| CurTime() | 返回当前时间 | -| Date() | 返回日期时间的日期部分 | -| DateDiff() | 计算两个日期之差 | -| Date_Add() | 高度灵活的日期运算函数 | -| Date_Format() | 返回一个格式化的日期或时间串 | -| Day() | 返回一个日期的天数部分 | -| DayOfWeek() | 对于一个日期,返回对应的星期几 | -| Hour() | 返回一个时间的小时部分 | -| Minute() | 返回一个时间的分钟部分 | -| Month() | 返回一个日期的月份部分 | -| Now() | 返回当前日期和时间 | -| Second() | 返回一个时间的秒部分 | -| Time() | 返回一个日期时间的时间部分 | -| Year() | 返回一个日期的年份部分 | - - -无论你什么时候指定一个日期,不管是插入或更新表值还是用WHERE子句进行过滤,日期必须为格式yyyy-mm-dd。因此, 2005年9月1日,给出为2005-09-01。 - -```java -SELECT cust_id ,order_num -FROM orders -WHERE DATE(order_date) ='2005-09-01'; - ---检索出2005年9月下的所有订单 ---方法1 -SELECT cust_id ,order_num -FROM orders -WHERE DATE(order_date) BETWEEN '2005-09-01' AND '2005-09-30'; ---方法2 -SELECT cust_id ,order_num -FROM orders -WHERE Year(order_date)=2005 AND MONTH(order_date)=9; -``` - -### 数值处理函数 - -数值处理函数仅处理数值数据。这些函数一般主要用于代数、三角或几何运算,因此没有串或日期—时间处理函数的使用那么频繁。 - -| 函 数 | 说 明 | -| ------ | ------------------ | -| Abs() | 返回一个数的绝对值 | -| Cos() | 返回一个角度的余弦 | -| Exp() | 返回一个数的指数值 | -| Mod() | 返回除操作的余数 | -| Pi() | 返回圆周率 | -| Rand() | 返回一个随机数 | -| Sin() | 返回一个角度的正弦 | -| Sqrt() | 返回一个数的平方根 | -| Tan() | 返回一个角度的正切 | - -## 汇总数据 - -### 聚集函数 - -* 确定表中行数(或者满足某个条件或包含某个特定值的行数)。 -* 获得表中行组的和。 -* 找出表列(或所有行或某些特定的行)的最大值、最小值和平均值 - -聚集函数( aggregate function) 运行在行组上,计算和返回单个值的函数。 - -| 函 数 | 说 明 | -| ------- | ---------------- | -| AVG() | 返回某列的平均值 | -| COUNT() | 返回某列的行数 | -| MAX() | 返回某列的最大值 | -| MIN() | 返回某列的最小值 | -| SUM() | 返回某列值之和 | - -#### AVG() - -AVG()只能用来确定特定数值列的平均值,而且列名必须作为函数参数给出。为了获得多个列的平均值,必须使用多个AVG()函数。 - - -```java ---AVG()通过对表中行数计数并计算特定列值之和,求得该列的平均值。 AVG()可用来返回所有列的平均值,也可以用来返回特定列或行的平均值 ---返回products表中所有产品的平均价格 -SELECT AVG(prod_price) AS avg_price -FROM products; ---确定特定列或行的平均值。 下面的例子返回特定供应商所提供产品的平均价格 -SELECT AVG(prod_price) AS avg_price -FROM products -WHERE vend_id = 1003; -``` - -#### COUNT() - -COUNT()函数进行计数。 可利用COUNT()确定表中行的数目或符合特定条件的行的数目。 - -COUNT()函数有两种使用方式。 - -* 使用COUNT(*)对表中行的数目进行计数, 不管表列中包含的是空值( NULL)还是非空值。 -* 使用COUNT(column)对特定列中具有值的行进行计数,忽略NULL值 - -如果指定列名,则指定列的值为空的行被COUNT()函数忽略,但如果COUNT()函数中用的是星号( *),则不忽 -略。 - -```JAVA ---返回customers表中客户的总数 -SELECT COUNT(*) AS num_cust -FROM customers; - ---对具有电子邮件地址的客户计数 -SELECT COUNT(cust_email) AS num_cust -FROM customers; -``` - -#### MAX()函数\MIN()函数 - -MAX()返回指定列中的最大值。 MAX()要求指定列名 - -MIN()的功能正好与MAX()功能相反,它返回指定列的最小值 - ->对非数值数据使用MAX()函数\MIN()函数,虽然MAX()函数\MIN()函数一般用来找出最大最小的数值或日期值,但MySQL允许将它用来返回任意列中的最大最小值,包括返回文本列中的最大最小值。在用于文本数据时,如果数据按相应的列排序,则MAX()函数\MIN()函数返回最后一行\第一行。 - -MAX()函数\MIN()函数忽略列值为NULL的行。 - -```java ---返回products表中最贵的物品的价格 -SELECT MAX(prod_price) AS max_price -FROM products; - ---返回products表中最便宜物品的价格 -SELECT MIN(prod_price) AS min_price -FROM products; -``` - -#### SUM()函数 - -SUM()用来返回指定列值的和(总计)。\ - -```java ---检索所订购物品的总数(所有quantity值之和) -SELECT SUM(quantity) AS items_ordered -FROM orderitems -WHERE order_num = 20005; - ---返回订单中所有物品价钱之和 -SELECT SUM(quantity*item_price) AS total_price -FROM orderitems -WHERE order_num = 20005; -``` - -### 聚集不同的值 - -以上5个聚集函数都可以如下使用: - -* 对所有的行执行计算,指定ALL参数或不给参数(因为ALL是默认行为); -* 只包含不同的值,指定DISTINCT参数 - ->如果指定列名,则DISTINCT只能用于COUNT()。DISTINCT不能用于COUNT(*),因此不允许使用COUNT( DISTINCT),否则会产生错误。类似地, DISTINCT必须使用列名,不能用于计算或表达式 - -```java ---返回特定供应商1003提供的产品的平均价格(相同的产品只会计算一次) -SELECT AVG( DISTINCT prod_price) AS avg_price -FROM products -WHERE vend_id = 1003; -``` - -### 组合聚集函数 - -目前为止的所有聚集函数例子都只涉及单个函数。但实际上SELECT语句可根据需要包含多个聚集函数。 - -```JAVA -SELECT COUNT(*) AS num_items, - MIN(prod_price) AS price_min, - Max(prod_price) AS price_max, - AVG(prod_price) AS price_avg, - SUM(prod_price) AS price_sum -FROM products; -``` - -## 分组数据 - -```java ---返回vend_id=1005的数量 -SELECT COUNT(*) AS num_prods -FROM products -WHERE vend_id = 1005; - ---创建数据分组,GROUP BY子句指示MySQL按vend_id排序并分组数据。 -SELECT vend_id, COUNT(*) AS num_prods -FROM products -GROUP BY vend_id; - ---过滤数量大于等于3的。 ---where只能过滤行,不能过滤分组、 ---HAVING 用于过滤分组 -SELECT vend_id, COUNT(*) AS num_prods -FROM products -GROUP BY vend_id -HAVING COUNT(*)>=3; - ---分组和排序 ---GROUP BY子句用来按订单号(order_num列)分组数据,以便SUM(*)函数能够返回总计订单价格。 HAVING子句过滤数据,使得只返回总计订单价格大于等于50的订单。最后, 用ORDERBY子句排序输出 -SELECT order_num, SUM(quantity*item_price) AS ordertotal -FROM orderitems -GROUP BY order_num -HAVING SUM(quantity*item_price)>=50 -ORDER BY ordertotal; -``` - -## 使用子查询 - -### 子查询 - -查询(query) 任何SQL语句都是查询。但此术语一般指SELECT语句 - -SELECT语句是SQL的查询。迄今为止我们所看到的所有SELECT语句都是简单查询,即从单个数据库表中检索数据的单条语句。 - -SQL还允许创建子查询( subquery) ,即嵌套在其他查询中的查询。 - -### 利用子查询进行过滤 - -```JAVA ---检索包含物品TNT2的所有订单的编号 -SELECT order_num -FROM orderitems -WHERE prod_id = 'TNT2'; ---检索具有前一步骤列出的订单编号的所有客户的ID。 -SELECT cust_id -FROM orders -WHERE order_num IN(20005,20007); - ---把第一个查询(返回订单号的那一个)变为子查询组合两个查询。 -SELECT cust_id -FROM orders -WHERE order_num IN(SELECT order_num - FROM orderitems - WHERE prod_id = 'TNT2'); - -``` - -## 联结表 - -外键(foreign key):外键为某个表中的一列,它包含另一个表的主键值,定义了两个表之间的关系。 - -可伸缩性(scale) 能够适应不断增加的工作量而不失败。设计良好的数据库或应用程序称之为可伸缩性好( scale well) 。 - -联结是一种机制,用来在一条SELECT语句中关联表,因此称之为联结。使用特殊的语法,可以联结多个表返 -回一组输出,联结在运行时关联表中正确的行。 - -### 创建联结 - -联结的创建非常简单,规定要联结的所有表以及它们如何关联即可。 - -```JAVA ---使用where联结 -SELECT vend_name , prod_name, prod_price -FROM vendors, products -WHERE vendors.vend_id = products.vend_id -ORDER BY vend_name, prod_name; - ---使用INNER JOIN联结,推荐 -SELECT vend_name , prod_name, prod_price -FROM vendors INNER JOIN products -ON vendors.vend_id = products.vend_id -ORDER BY vend_name, prod_name; -``` - -### 联结多个表 - -SQL对一条SELECT语句中可以联结的表的数目没有限制。创建联结的基本规则也相同。首先列出所有表,然后定义表之间的关系 - -```JAVA -SELECT prod_name, vend_name , prod_price, quantity -FROM orderitems,products,vendors, -WHERE products.vend_id=vendors.vend_id -AND orderitems.prod_id = products.prod_id -AND order_num =20005 -ORDER BY vend_name, prod_name; -``` - -## 创建高级联结 - -### 使用别名 - -```JAVA --- 使用别名 -SELECT cust_name, cust_contact -FROM customers AS c,orders AS o, orderitems AS oi -WHERE c.cust_id = o.cust_id - AND oi.order_num = o.order_num - AND prod_id = 'TNT2'; - ---自联结 ---在同一个表中搜索 -SELECT p1.prod_id, p1.prod_name -FROM products AS p1, products AS p2 -WHERE p1.vend_id = p2.vend_id - AND p2.prod_id = 'DTNTR'; -``` - - -## 组合查询 - -```java -SELECT vend_id, prod_id ,prod_price -FROM products -WHERE prod_price<=5 -UNION -SELECT vend_id, prod_id ,prod_price -FROM products -WHERE vend_id IN(1001,1002); - ---等同于 -SELECT vend_id, prod_id ,prod_price -FROM products -WHERE prod_price<=5 OR vend_id IN (1001,1002); -``` - -UNION从查询结果集中自动去除了重复的行(换句话说,它的行为与单条SELECT语句中使用多个WHERE子句条件一样)。 - -使用UNION ALL, MySQL不取消重复的行。 - -在用UNION组合查询时,只能使用一条ORDER BY子句,它必须出现在最后一条SELECT语句之后。 - -## 全文本搜索 - -### 启用全文本搜索支持 - -一般在创建表时启用全文本搜索。 CREATE TABLE语句(第21章中介绍)接受FULLTEXT子句,它给出被索引列的一个逗号分隔的列表。 - -### 进行全文本搜索 - -使用两个函数Match()和Against()执行全文本搜索,其中Match()指定被搜索的列, Against()指定要使用的搜索表达式。 - -```java -SELECT note_text -FROM productnotes -WHERE Match(note_text) Against('rabbit'); - ---等同于 -SELECT note_text -FROM productnotes -WHERE note_text LIKE '%rabbit%'; - -SELECT note_text -FROM productnotes -WHERE note_text REGEXP 'rabbit'; -``` - -区别: - -全文本搜索会根据匹配程度进行自动排序 - -### 使用查询扩展 - -找出所有提到anvils的注释。只有一个注释包含词anvils,但你还想找出可能与你的搜索有关的所有其他行,即使它们不包含词anvils。 - -在使用查询扩展时, MySQL对数据和索引进行两遍扫描来完成搜索: - -```java -SELECT note_text -FROM productnotes -WHERE Match(note_text) Against('rabbit' WITH QUERY EXPANSION); -``` - -### 布尔文本搜索 - -```java ---查询有heavy但没有rope的行 -SELECT note_text -FROM productnotes -WHERE Match(note_text) Against('heavy -rope*' IN BOOLEAN MODE); -``` - -# 7、事务 - -要么都成功,要么都失败 - -A给B转账,步骤1:A钱减少500,步骤2:B钱增加500 - -## ACID - -参考:https://blog.csdn.net/dengjili/article/details/82468576 - -ACID,数据库事务正确执行的四个基本要素的缩写,是指数据库管理系统(DBMS)在写入或更新资料的过程中,为保证事务(transaction)是正确可靠的,所必须具备的四个特性: - -* **原子性**(atomicity,或称不可分割性):两个步骤1和2要么都成功,要么都失败, -* **一致性**(consistency):最终一致性,事务前后的数据完整性要保证一致,转账前后A和B最终总钱数不变 -* **隔离性**(isolation,又称独立性):针对多个用户同时操作,排除其他事务对本次事务的影响 - * 同时C在给B转账,之间不会有影响 -* **持久性**(durability):事务一旦提交则不可逆,被持久化到数据库中,事务结束后的数据不随着外界原因导致数据丢失 - * 转账事务提交前断电,重启后,恢复到原状,数据状态为转账前的 - * 转账事务提交后断电,重启后,数据状态为转账后的 - -隔离性导致的问题:**事务的隔离级别:** - -* **脏读:**一个事务读取了另外一个事务未提交的数据 -* **不可重复读:**在一个事务内读取表中的某一行数据,多次读取结果不同。(这个不一定是错误,只是某些场合不对) -* **幻读(虚读):**是指在一个事务内读取到了别的事务插入的数据,导致前后读取数量总量不一致。(一般是行影响,如下图所示:多了一行) - -## 执行事务 - -mysql是默认开启事务自动提交的 - -```sql ---mysql是默认开启事务自动提交的 -SET autocommit =0; --关闭 -SET autocommit =1; --开启 -``` - -事务的完整过程: - -```sql ---1.关闭自动提交 -SET autocommit =0; --关闭自动提交 - ---2.标记一个事务的开始,从这个开始后的sql都在同一个事务内 -START TRANSACTION; -INSERT XX - ---3.1提交:持久化(成功!) -COMMIT; - ---3.2回滚:回到原来的样子(失败) -ROLLBACK; - ---4.事务结束 -SET autocommit =1; --开启自动提交 - ---了解 -SAVEPOINT 保存点名 --设置一个事务的保存点 -ROLLBACK TO SAVEPOINT 保存点名 --回滚到保存点 -RELEASE SAVEPOINT 保存点名 --撤销保存点 -``` - -# 8、索引 - -> 索引index是帮助MySQL高效获取数据的数据结构 -> -> 提取句子主干,就可以得到索引的本质:索引是数据结构,MySQL中常用的索引结构(索引底层的数据结构)有:B-TREE ,B+TREE ,HASH 等 - -**优点**: - -- 索引大大减小了服务器需要扫描的数据量 - -- 索引可以帮助服务器避免排序和临时表 - -- 索引可以将随机IO变成顺序IO - -- 索引对于InnoDB(对索引支持行级锁)非常重要,因为它可以让查询锁更少的元组。在MySQL5.1和更新的版本中,InnoDB可以在服务器端过滤掉行后就释放锁,但在早期的MySQL版本中,InnoDB直到事务提交时才会解锁。对不需要的元组的加锁,会增加锁的开销,降低并发性。 InnoDB仅对需要访问的元组加锁,而索引能够减少InnoDB访问的元组数。但是只有在存储引擎层过滤掉那些不需要的数据才能达到这种目的。一旦索引不允许InnoDB那样做(即索引达不到过滤的目的),MySQL服务器只能对InnoDB返回的数据进行WHERE操作,此时,已经无法避免对那些元组加锁了。如果查询不能使用索引,MySQL会进行全表扫描,并锁住每一个元组,不管是否真正需要。 - -- - 关于InnoDB、索引和锁:InnoDB在二级索引上使用共享锁(读锁),但访问主键索引需要排他锁(写锁) - - - -**缺点** - -- 虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存索引文件。 -- 建立索引会占用磁盘空间的索引文件。一般情况这个问题不太严重,但如果你在一个大表上创建了多种组合索引,索引文件的会膨胀很快。 -- 如果某个数据列包含许多重复的内容,为它建立索引就没有太大的实际效果。 -- 对于非常小的表,大部分情况下简单的全表扫描更高效; - -索引只是提高效率的一个因素,如果你的MySQL有大数据量的表,就需要花时间研究建立最优秀的索引,或优化查询语句。 - -因此应该只为最经常查询和最经常排序的数据列建立索引。 - -MySQL里同一个数据表里的索引总数限制为16个。 - -## 索引的分类 - -```sql ---显示所有索引信息 -SHOW INDEX FROM student; -``` - - - -* 主键索引*PRIMARY KEY* - -唯一的标识,一张表只能有一个主键索引,不允许重复、不允许为 NULL; - -```sql - ALTER TABLE TableName ADD PRIMARY KEY(column_list); -``` - -* 唯一索引*UNIQUE KEY - -避免重复的列出现,允许为 NULL 值,一张表可有多个唯一索引,索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一 - -```sql -ALTER TABLE TableName ADD UNIQUE (column_list); -``` - -* 常规索引*KEY/INDEX* - -一张表可以创建多个普通索引,一个普通索引可以包含多个字段,允许数据重复,允许 NULL 值插入; - -```sql -ALTER TABLE TableName ADD INDEX IndexName(`字段名`(length)); -``` - -* 全文索引*FullText* - * 在特定的数据库引擎下才有,MylSAIM - * 快速定位数据,它查找的是文本中的关键词,主要用于全文检索。 - -## 索引原则 - -* 索引不是越多越好 -* 不要对进程变动数据加索引 -* 小数据量的表不需要加索引 -* 索引一般加在常用来查询的字段上 - -# 9、权限管理 - -## 用户管理 - -```sql ---创建用户 -CREATE USER username IDENTIFIED BY 'password'; - ---修改用户密码 -USE mysql --使用mysql数据库 -ALTER USER 'test'@'localhost' IDENTIFIED WITH MYSQL_NATIVE_PASSWORD BY '新密码'; --%代替localhost表示所有ip - ---用户名重命名 -RENAME USER oldname TO newname; - ---用户授权 -GRANT ALL PRIVILEGES ON *.* TO username;--全部的表,所有权限,除了给别人授权 - ---查看指定用户权限 -SHOW GRANTS FOR username; - ---撤销权限 revoke哪些权限, 在哪个表, 给谁 -REVOKE ALL PRIVILEGES ON *.* FROM username; - ---删除用户 -DROP USER username; -``` - -# 10、数据库备份 - -* 直接拷贝物理文件 -* 在可视化工具中导出 -* 使用命令行mysqludmp - -**导出** - -```bash -mysqldump -h 主机 -u 用户名 -p 密码 数据库 表1 表2 表3 >物理磁盘位置/文件名 -mysqldump -hlocalhost -uroot -p123456 school student >D:/a.sql -``` - -**导入** - -```sql ---首先登陆数据库 -source 物理磁盘位置/文件名.sql -``` - -# 11、规范数据库设计 - -## 为什么需要设计 - -当数据库比较复杂时,需要进行数据库设计 - -糟糕的设计: - -* 数据冗余,浪费空间 -* 数据增删麻烦,会产生异常 -* 程序性能差 - - - -良好的设计: - -* 节省内存空间 -* 保证数据库的完整性 -* 方便开发系统 -* 避免使用物理外键 - - - -软件开发中,关于数据库的设计: - -* 分析需求:分析业务和需要处理的数据库需求 -* 概要设计:设计关系图E-R图 - - - -设计数据库步骤(个人博客): - -1. 收集信息,分析需求 - 1. 用户表(用户登陆注销、个人信息,写博客,创建分类) - 2. 分类表(文章分类,谁创建的) - 3. 文章表(文章的信息) - 4. 评论表(评论信息) - 5. 友链表(友情链接信息) - 6. 自定义表(可以不需要)(系统信息,某个关键的字,或者一些主题)key:value -2. 标识实体(把需求落实到每个字段) -3. 标识实体之间的关系 - 1. 写博客:user->blog - 2. 创建分类:user->category - 3. 关注:user->user - 4. 评论:user->user->blog - -## 三大范式 - -为什么需要数据规范化: - -* 信息重复 -* 更新异常 -* 插入异常 - * 无法正常显示信息 -* 删除异常 - * 丢失有效的信息 - -**三大范式:**规范数据库的设计 - -* **第一范式(1NF)** - * 要求数据库表中的每一列都是不可分割的原子数据项 -* **第二范式(2NF)** - * 前提:满足第一范式 - * 每张表只描述一件事情 -* **第三范式(3NF)** - * 前提:满足第一范式和第二范式 - * 确保数据表中的每一列数据都和主键直接相关,而不能间接相关 - -**规范性和性能的问题**: - -* 关联查询的表不能超过三个 -* 考虑商业化的需求和目标,数据库的性能更加重要 -* 在规范性能的问题的时候,需要适当的考虑一下规范性 -* 故意给某些表增加一些冗余字段(从多表查询变为单表查询) -* 故意增加一些计算列(从大数据量降低为小数据量的查询) - -# 12、JDBC - -## 概述 - -### 数据库驱动 - -应用程序通过数据库驱动和数据库链接、打交道。不同的数据库有不同的驱动 - -**应用程序——>MySQL驱动、Oracle驱动——>数据库** - -### JDBC - -> 架构中,没有什么是加一层解决不了的 - -SUN公司为了简化开发人员的(对数据库的统一)操作,提供了一个java操作数据库的规范,即JDBC - -这些规范的实现由具体的厂商去做,对于开发人员,只需要掌握JDBC接口的操作即可 - -**应用程序——>JDBC——>MySQL驱动、Oracle驱动——>数据库** - -## 第一个JDBC程序 - -1. **maven引入jdbc驱动,配置pom.xml** - -数据库版本为8.0.26 - -```xml - - mysql - mysql-connector-java - 8.0.26 - -``` - -2. **编写java程序** - 1. 加载驱动 - 2. 连接数据库Drivermanager - 3. 获得执行sql对象Statement - 4. 获得返回的结果集ResultSet - 5. 释放连接 - -```java -public class JdbcTest { - public static void main(String[] args) throws ClassNotFoundException, SQLException { - //1.加载驱动 固定写法 - Class.forName("com.mysql.cj.jdbc.Driver"); - - //2.用户信息和url - /*school:连接的数据库 - useUnicode=true&characterEncoding=utf8&useSSL=true:支持中文编码&字符集utf8&使用安全连接*/ - String url = "jdbc:mysql://localhost:3306/school?useUnicode=true&characterEncoding=utf8&useSSL=true"; - String username = "root"; - String password = "123456"; - - //3.连接成功,数据库对象Connection代表数据库对象 - Connection connection = DriverManager.getConnection(url, username, password); - - //4.执行sql对象 statement执行sql的对象 - Statement statement = connection.createStatement(); - - //5.执行sql对象去执行sql,可能存在结果,查看连接结果 - //sql语句 - String sql = "SELECT * FROM student"; - //返回的结果集,结果集中封装了查询出来的全部结果 - ResultSet resultSet = statement.executeQuery(sql); - //输出结果 - while (resultSet.next()) { - System.out.println("id = " + resultSet.getObject("id")); - System.out.println("name = " + resultSet.getObject("name")); - System.out.println("age = " + resultSet.getObject("age")); - System.out.println("=============="); - } - - //6.释放连接 - resultSet.close(); - statement.close(); - connection.close(); - } -} -``` - -## JDBC对象解释 - -### Connection - -```java -//3.连接成功,数据库对象Connection代表数据库对象 -Connection connection = DriverManager.getConnection(url, username, password); -connection.rollback(); //事务回滚 -connection.commit(); //事务提交 -connection.setAutoCommit(true); //设置事务自动提交 -``` - - - -### Statement - -执行sql的对象的类 - -```java -Statement statement = connection.createStatement(); //获取statement对象 -ResultSet resultSet = statement.executeQuery(sql); //查询操作,返回ResultSet结果集 -boolean execute = statement.execute(sql);//执行任何sql -int i = statement.executeUpdate(sql);//更新、插入、删除,返回一个受影响的行数 -``` - -### ResultSet - -查询的结果集 - -```java -ResultSet resultSet = statement.executeQuery(sql); //查询操作,返回ResultSet结果集 - -resultSet.getObject(); //返回结果,在不知道列的类型的情况下使用 -resultSet.getString(); -resultSet.getInt(); -.... -``` - -遍历操作 - -```java -resultSet.beforeFirst(); //移动到最前面 -resultSet.afterLast(); //移动到最后面 -resultSet.next(); //移动到下一个 -resultSet.previous(); //移动到前一个 -resultSet.absolute(row); //移动到指定行 -``` - -### 释放资源 - -```java -//6.释放连接 -resultSet.close(); -statement.close(); -connection.close(); -``` - -## 采用配置文件方式 - -**新增db.properties文件** - -```properties -driver = com.mysql.cj.jdbc.Driver -url = jdbc:mysql://localhost:3306/school?useUnicode=true&characterEncoding=utf8&useSSL=true -username = root -password = 123456 -``` - -**jdbc配置类:** - -```java -package com.jdbc; - -import java.io.IOException; -import java.io.InputStream; -import java.sql.*; -import java.util.Properties; - -/** - * @author TianZhendong - * @date 2021/8/5 - */ - -public class JdbcUtils { - private static String driver=null; - private static String url=null; - private static String username=null; - private static String password=null; - static{ - try{ - //db.properties"为配置文件名 - InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties"); - Properties properties = new Properties(); - properties.load(in); - driver=properties.getProperty("driver"); - url=properties.getProperty("url"); - username=properties.getProperty("username"); - password=properties.getProperty("password"); - //驱动加载 - Class.forName(driver); - } catch (IOException | ClassNotFoundException e) { - e.printStackTrace(); - } - } - - /** - * @Description: 获取连接 - * @Author: TianZD - * @Date: 2021/8/5 19:15 - * @Param: [] - * @Return: java.sql.Connection - */ - public static Connection getConnection() throws SQLException { - return DriverManager.getConnection(url, username, password); - } - - /** - * @Description: 释放资源 - * @Author: TianZD - * @Date: 2021/8/5 19:14 - * @Param: [conn, st, rs] - * @Return: void - */ - public static void release(Connection conn, Statement st, ResultSet rs) throws SQLException { - if(rs!=null){ - rs.close(); - } - if(st!=null){ - st.close(); - } - if(conn!=null){ - conn.close(); - } - } -} -``` - -java: - -```java -package com.jdbc; - -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import com.jdbc.JdbcUtils; - -/** - * @program: JDBCDemo - * @description: test - * @author: TianZD - * @create: 2021-08-05 19:22 - **/ -public class InsertTest { - public static void main(String[] args) { - Connection connection = null; - Statement statement = null; - ResultSet resultSet = null; - try { - //获取数据库连接 - connection = JdbcUtils.getConnection(); - //获得sql的执行对象 - statement = connection.createStatement(); - String sql = "INSERT INTO student(id, name, age)" + - " VALUES(3, 'stu3', '25')"; - int i = statement.executeUpdate(sql); - if (i > 0) { - System.out.println("插入成功!"); - } - } catch (SQLException throwable) { - throwable.printStackTrace(); - } finally { - JdbcUtils.release(connection,statement, null); - } - - } -} -``` - -## SQL注入 - -SQL 注入(SQL Injection)是发生在 Web 程序中数据库层的安全漏洞,是网站存在最多也是最简单的漏洞。主要原因是**程序对用户输入数据的合法性没有判断和处理**,导致攻击者可以在 Web 应用程序中事先定义好的 **SQL 语句中添加额外的 SQL 语句**,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步获取到数据信息。 - - - -简而言之,**SQL 注入就是在用户输入的字符串中加入 SQL 语句**,如果在设计不良的程序中忽略了检查,那么这些注入进去的 SQL 语句就会被数据库服务器误认为是正常的 SQL 语句而运行,攻击者就可以执行计划外的命令或访问未被授权的数据。 - -## PreparedStatement对象 - -可以防止SQL注入 - -本质是:把传递进来的参数当作字符 - -具体实现见DBCP部分代码 - -## 数据库连接池 - -数据库连接——执行完毕——释放 - -连接——释放很耗费资源 - -**池化技术:准备一些预先的资源,过来就连接预先准备好的** - -* 最小连接数:根据常用连接数确定 - -* 最大连接数:业务最高承载上限 - -* 排队等待 - -* 等待超时 - -编写连接池,实现一个接口DataSource - -> 开源数据源实现 - -* DBCP -* C3P0 -* Druid阿里巴巴 - -使用这些数据库连接池后,在项目开发中就不需要编写连接数据库代码了 - -### DBCP - -maven引入依赖: - -```xml - - - mysql - mysql-connector-java - 8.0.26 - - - commons-dbcp - commons-dbcp - 1.4 - - -``` - -编写dbcp.properties - -```properties -#连接设置 该部分格式不能变 -driverClassName=com.mysql.cj.jdbc.Driver -url=jdbc:mysql://localhost:3306/school -username=root -password=123456 - -#下面的都可以不要 -# -initialSize=10 - -#最大连接数量 -maxActive=50 - -# -maxIdle=20 - -# -minIdle=5 - -# -maxWait=60000 - - -#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;] -#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。 -connectionProperties=useUnicode=true;characterEncoding=gbk - -#指定由连接池所创建的连接的自动提交(auto-commit)状态。 -defaultAutoCommit=true - -#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。 -#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE -defaultTransactionIsolation=READ_UNCOMMITTED -``` - -编写配置类 - -```java -package com.jdbc; - -import org.apache.commons.dbcp.BasicDataSource; -import org.apache.commons.dbcp.BasicDataSourceFactory; - -import javax.sql.DataSource; -import java.io.InputStream; -import java.sql.*; -import java.util.Properties; - -/** - * @program: JDBCDemo - * @description: test - * @author: TianZD - * @create: 2021-08-05 23:17 - **/ -public class JdbcUtils_Dbcp { - private static DataSource dataSource = null; - static{ - try{ - //dbcp.properties为配置文件名 - InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("dbcp.properties"); - Properties properties = new Properties(); - properties.load(in); - - //创建数据源 工厂模式 - dataSource = BasicDataSourceFactory.createDataSource(properties); - } catch (Exception e) { - e.printStackTrace(); - } - } - - /** - * @Description: 从数据源中获取连接 - * @Author: TianZD - * @Date: 2021/8/5 19:15 - * @Param: [] - * @Return: java.sql.Connection - */ - public static Connection getConnection() throws SQLException { - return dataSource.getConnection(); - } - - /** - * @Description: 释放资源 - * @Author: TianZD - * @Date: 2021/8/5 19:14 - * @Param: [conn, st, rs] - * @Return: void - */ - public static void release(Connection conn, Statement st, ResultSet rs) { - if(rs!=null){ - try { - rs.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - if(st!=null){ - try { - st.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - if(conn!=null){ - try { - conn.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - } -} -``` - -测试 - -```java -package com.jdbc; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.SQLException; - -/** - * @program: JDBCDemo - * @description: - * @author: TianZD - * @create: 2021-08-05 23:23 - **/ -public class TestDbcp { - public static void main(String[] args) { - Connection connection = null; - PreparedStatement preparedStatement = null; - try { - connection = JdbcUtils_Dbcp.getConnection(); - //sql语句,其中value值先不赋予 - - String sql = "insert into student(id, `name`, `age`) values(?,?,?)"; - - //预先编译sql,先写sql,但不执行 - preparedStatement = connection.prepareStatement(sql); - - //手动value赋值 - preparedStatement.setInt(1, 3); - preparedStatement.setString(2, "tian"); - preparedStatement.setInt(3, 25); - - //执行 - int i = preparedStatement.executeUpdate(); - if (i > 0) { - System.out.println("插入成功!"); - } - } catch (SQLException throwables) { - throwables.printStackTrace(); - } finally { - JdbcUtils_Dbcp.release(connection, preparedStatement, null); - } - } -} -``` - -### C3P0 - -maven引入依赖 - -```xml - - - mysql - mysql-connector-java - 8.0.26 - - - com.mchange - c3p0 - 0.9.5.2 - - -``` - -**配置文件,名字必须为c3p0-config** - -```xml - - - - - 10 - 30 - 100 - 10 - 200 - - - - - com.mysql.cj.jdbc.Driver - jdbc:mysql://localhost:3306/school - root - 123456 - 10 - 30 - 100 - 10 - 200 - - - -``` - -配置类 - -```java -package com.jdbc; - -import com.mchange.v2.c3p0.ComboPooledDataSource; -import org.apache.commons.dbcp.BasicDataSourceFactory; - -import javax.sql.DataSource; -import java.io.InputStream; -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.Properties; - -/** - * @program: JDBCDemo - * @description: - * @author: TianZD - * @create: 2021-08-05 23:41 - **/ -public class JdbcUtils_C3P0 { - private static DataSource dataSource = null; - static{ - try{ - //创建数据源 工厂模式 - dataSource = new ComboPooledDataSource("MySQL"); - } catch (Exception e) { - e.printStackTrace(); - } - } - - /** - * @Description: 从数据源中获取连接 - * @Author: TianZD - * @Date: 2021/8/5 19:15 - * @Param: [] - * @Return: java.sql.Connection - */ - public static Connection getConnection() throws SQLException { - return dataSource.getConnection(); - } - - /** - * @Description: 释放资源 - * @Author: TianZD - * @Date: 2021/8/5 19:14 - * @Param: [conn, st, rs] - * @Return: void - */ - public static void release(Connection conn, Statement st, ResultSet rs) { - if(rs!=null){ - try { - rs.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - if(st!=null){ - try { - st.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - if(conn!=null){ - try { - conn.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - } -} -``` - -测试 - -```java -package com.jdbc; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.SQLException; - -/** - * @program: JDBCDemo - * @description: - * @author: TianZD - * @create: 2021-08-05 23:45 - **/ -public class TestC3P0 { - public static void main(String[] args) { - Connection connection = null; - PreparedStatement preparedStatement = null; - try { - connection = JdbcUtils_C3P0.getConnection(); - //sql语句,其中value值先不赋予 - - String sql = "insert into student(id, `name`, `age`) values(?,?,?)"; - - //预先编译sql,先写sql,但不执行 - preparedStatement = connection.prepareStatement(sql); - - //手动value赋值 - preparedStatement.setInt(1, 4); - preparedStatement.setString(2, "tianzhendong"); - preparedStatement.setInt(3, 28); - - //执行 - int i = preparedStatement.executeUpdate(); - if (i > 0) { - System.out.println("插入成功!"); - } - } catch (SQLException throwables) { - throwables.printStackTrace(); - } finally { - JdbcUtils_C3P0.release(connection, preparedStatement, null); - } - } -} -``` - - - - - - - diff --git a/source/_posts/Java/Mybatis.md b/source/_posts/Java/Mybatis.md deleted file mode 100644 index 5b13bb7..0000000 --- a/source/_posts/Java/Mybatis.md +++ /dev/null @@ -1,1815 +0,0 @@ ---- -title: Mybatis -author: TianZD -top: true -cover: true -toc: true -mathjax: false -summary: Mybatis框架学习笔记,粗略学了一下,没有参考价值 -tags: - - Mybatis - - Java - - 数据库 - - 学习笔记 -categories: - - java -reprintPolicy: cc_by -abbrlink: da3e1361 -date: 2022-04-29 11:05:46 -coverImg: -img: -password: ---- - - -[toc] - -# Mybatis - -# 1、简介 - -> **Mybatis** - -* MyBatis 是一款优秀的**持久层**框架 -* 它支持自定义 SQL、存储过程以及高级映射 -* **MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作** -* MyBatis 可以通过简单的 **XML 或注解**来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。 - -获取mybatis - -```xml - - org.mybatis - mybatis - x.x.x - -``` - -> **持久化** - -数据持久化 - -* 持久化就是将程序的数据在持久状态和瞬时状态转化的过程 -* 内存:断电即失 -* 数据库、io文件可以进行持久化 - -为什么需要持久化:有一些对象不能丢失 - -> **持久层** - -Dao层、Service层、Controller层 - -# 2、入门 - -## 第一个Mybatis程序 - -**思路:**搭建环境==》导入Mybatis==》编写代码==》测试 - -> **搭建环境** - -1. **搭建数据库** - -```sql -CREATE DATABASE mybatis; -CREATE TABLE `user`( - `id` INT(20) NOT NULL, - `name` VARCHAR(30) DEFAULT NULL, - `pwd` VARCHAR(30) DEFAULT NULL, - PRIMARY KEY(`id`) -)ENGINE=INNODB DEFAULT CHARSET=UTF8; -``` - -2. **新建maven项目** - 1. 普通maven项目 - 2. 删除src目录,使其变为父工程 - 3. 导入依赖 - -```xml - - - - - mysql - mysql-connector-java - 8.0.25 - - - - org.mybatis - mybatis - 3.5.7 - - - - junit - junit - 3.8.2 - test - - -``` - -> **创建模块** - -由于在上述步骤中配置了父工程,并且父工程已经导入了依赖,因此所有的子模块不需要再次引入依赖 - -子模块的xml配置文件中多了<**parent**> - -```xml - - MybatisStudy - org.example - 1.0-SNAPSHOT - -``` - -> **编写mybatis核心配置文件,在resource目录下创建mybaits-config.xml** - -XML 配置文件中包含了**对 MyBatis 系统的核心设置**,包括**获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器**(TransactionManager)。 - -```xml - - - - - - - - - - - - - - - - - - -``` - -> **编写mybatis工具类** - -* **从xml配置中获取 SqlSessionFactory 实例** - -* **从 SqlSessionFactory 中获取 SqlSession** - -每个基于 MyBatis 的应用都是以一个 **SqlSessionFactory 的实例为核心**的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则**可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例**。 - -从 XML 文件中构建 SqlSessionFactory 的实例非常简单,建议使用类路径下的资源文件进行配置。 但也可以使用任意的输入流(InputStream)实例,比如用文件路径字符串或 file:// URL 构造的输入流。MyBatis 包含一个名叫 Resources 的工具类,它包含一些实用方法,使得从类路径或其它位置加载资源文件更加容易。 - - **SqlSession中包括了操作数据库的方法** - -```java -public class MybatisUtils { - private static SqlSessionFactory sqlSessionFactory = null; - static { - try { - /*以下三句话是固定的 - 用来从xml配置中获取sqlSessionFactory对象 - * 1. 加载配置文件,maven中可以直接读取到resource中的配置文件 - * 2. 获取输入流实例 - * 3. 从XML配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例*/ - String resource = "mybatis-config.xml"; - InputStream inputStream = Resources.getResourceAsStream(resource); - sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); - } catch (IOException e) { - e.printStackTrace(); - } - } - - /** - * @Description: 从 SqlSessionFactory对象 中获取 SqlSession实例 - * @Author: TianZD - * @Date: 2021/8/6 22:01 - * @Param: [] - * @Return: org.apache.ibatis.session.SqlSession - */ - public static SqlSession getSqlSession() { - return sqlSessionFactory.openSession(); - } - -} -``` - -> **编写代码** - -* **实体类** - -```java -package com.tian.pojo; - -/** - * @program: MybatisStudy - * @description: 实体类 - * @author: TianZD - * @create: 2021-08-06 22:04 - **/ -public class User { - private int id; - private String name; - private String pwd; - - public User() { - } - - public User(int id, String name, String pwd) { - this.id = id; - this.name = name; - this.pwd = pwd; - } - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getPwd() { - return pwd; - } - - public void setPwd(String pwd) { - this.pwd = pwd; - } - - @Override - public String toString() { - return "User{" + - "id=" + id + - ", name='" + name + '\'' + - ", pwd='" + pwd + '\'' + - '}'; - } -} -``` - -* **Dao接口** - - - -```java -package com.tian.dao; - -import com.tian.pojo.User; - -import java.util.List; - -/** - * @program: MybatisStudy - * @description: Dao接口,后面用mapper代替,两者等价 - * @author: TianZD - * @create: 2021-08-06 22:07 - **/ -public interface UserDao { - //查询全部 - List getUserList(); - - //根据id查询 - User getUserById(int id); - - //insert插入 - int addUser(User user); - - //update修改 - int updateUser(User user); - - //删除用户 - int deleteUser(int id); -} -``` - -* **接口实现类Mapper** - -以前采用的方式是创建一个**接口实现类,现在采用xml**配置文件的方式 - -```xml - - - - - - - - - - - - - insert into mybatis.user (id, name, pwd) - values (#{id}, #{name}, #{pwd}); - - - - - update mybatis.user - set name = #{name}, - pwd = #{pwd} - where id = #{id}; - - - - - delete - from mybatis.user - where id = #{id}; - - - -``` - -* **Junit测试** - - - -```java -package com.tian.dao; - -import com.tian.pojo.User; -import com.tian.utils.MybatisUtils; -import org.apache.ibatis.session.SqlSession; -import org.junit.Test; - -import java.util.List; - -/** - * @program: MybatisStudy - * @description: 测试 - * @author: TianZD - * @create: 2021-08-06 22:56 - **/ -public class UserDaoTest { - @Test - public void test(){ - //1. 获得SqlSession对象 - SqlSession sqlSession = MybatisUtils.getSqlSession(); - //2. 执行sql、获取结果、输出 - //方式1,getMapper - UserDao userDao = sqlSession.getMapper(UserDao.class); - List userList = userDao.getUserList(); - for (User user : userList) { - System.out.println(user); - } - - //3. 关闭SqlSession - sqlSession.close(); - } - - @Test - public void getUserById(){ - // 1. 获得SqlSession对象 - SqlSession sqlSession = MybatisUtils.getSqlSession(); - // 2. 执行sql - UserDao mapper = sqlSession.getMapper(UserDao.class); - User userById = mapper.getUserById(2); - System.out.println(userById); - // 3. 关闭 - sqlSession.close(); - } - - @Test - public void addUserTest(){ - // 1.从配置类中获得SqlSession对象 - SqlSession sqlSession = MybatisUtils.getSqlSession(); - // 2.sql语句 - UserDao mapper = sqlSession.getMapper(UserDao.class); - int tian4 = mapper.addUser(new User(4, "tian4", "123456")); - if (tian4 > 0) { - System.out.println("插入成功"); - } - - // 增删改需要提交事务 - sqlSession.commit(); - // 3. 关闭 - sqlSession.close(); - } - - @Test - public void updateUserTest() { - // 1.获取sqlsession对象 - SqlSession sqlSession = MybatisUtils.getSqlSession(); - // 2.sql - UserDao mapper = sqlSession.getMapper(UserDao.class); - int tian4 = mapper.updateUser(new User(4, "tian4", "123123")); - // 增删改需要提交事务 - sqlSession.commit(); - // 3.关闭 - sqlSession.close(); - } - - @Test - public void deleteUserTest() { - // 1.获取sqlsession对象 - SqlSession sqlSession = MybatisUtils.getSqlSession(); - // 2.sql - UserDao mapper = sqlSession.getMapper(UserDao.class); - int i = mapper.deleteUser(4); - // 增删改需要提交事务 - sqlSession.commit(); - // 3.关闭 - sqlSession.close(); - } -} -``` - -> **总结** - -在写完上述以后,后续使用步骤: - -1. 在UserDao接口中增加相应的方法 -2. 在UserMapper.xml中增加相应的sql语句 -3. 在测试方法中增加相应的测试方法 - -**注意**:增删改需要在关闭之前提交事务 - -```java -sqlSession.commit(); -``` - -> **可能错误:** - -* org.apache.ibatis.binding.BindingException: Type interface com.tian.dao.UserDao is not known to the MapperRegistry. - -在mybatis.config.xml文件中没有配置mapper.xml - -增加如下: - -**注意,路径用斜杠隔开** - -```xml - - - -``` - - - -* 错误2: - -Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.io.IOException: Could not find resource com/tian/dao/UserMapper.xml - -资源过滤原因 - -maven由于约定大于配置,可能遇到配置文件无法被导出或者生效的问题,解决方案: - -在父工程pop.xml中配置resource,防治资源导出失败 - -```xml - - - - src/main/resources - - **/*.properties - **/*.xml - - false - - - src/main/java - - **/*.properties - **/*.xml - - false - - - -``` - - - -## 生命周期和作用域 - -作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的**并发问题** - -![image-20210807175425111](https://gitee.com/tianzhendong/img/raw/master//images/image-20210807175425111.png) - -> **SqlSessionFactoryBuilder** - -* **一旦创建了 SqlSessionFactory,就不再需要它了** -* **最佳作用域是方法作用域(也就是局部方法变量)** - -这个类可以被实例化、使用和丢弃,**一旦创建了 SqlSessionFactory,就不再需要它了**。 因此 SqlSessionFactoryBuilder 实例的**最佳作用域是方法作用域(**也就是**局部方法变量**)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。 - -> **SqlSessionFactory** - -* **说白了可以想象为:数据库连接池** -* **一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例** -* **最佳作用域是应用作用域** -* **最简单的就是使用单例模式或者静态单例模式** - -SqlSessionFactory **一旦被创建就应该在应用的运行期间一直存在**,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的**最佳作用域是应用作用域**。 有很多方法可以做到,**最简单的就是使用单例模式或者静态单例模式。** - -> **SqlSession** - -* **连接到连接池的一个请求** -* **最佳的作用域是请求或方法作用域** -* **用完之后需要关闭,否则造成资源被占用** - -每个线程都应该有它自己的 SqlSession 实例。**SqlSession 的实例不是线程安全的,因此是不能被共享的**,所以它的**最佳的作用域是请求或方法作用域**。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,**每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它**。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式: - -```java -try (SqlSession session = sqlSessionFactory.openSession()) { - // 你的应用逻辑代码 -} -``` - -![image-20210807175744673](https://gitee.com/tianzhendong/img/raw/master//images/image-20210807175744673.png) - -每一个Mapper代表一个具体的业务 - - - -## CRUD - -> **namespace** - -namespace中的包名要和Dao/Mapper接口的包名保持一致 - -```xml - -``` - -绑定时的包名用.不能用/ - -> **select** - -选择、查询语句 - -1. 编写接口 - -```java - //查询全部 - List getUserList(); - - //根据id查询 - User getUserById(int id); -``` - -2. 编写对应的mapper中的sql语句 - -```xml - - - - - -``` - -3. 编写测试 - -```java -@Test -public void test(){ - //1. 获得SqlSession对象 - SqlSession sqlSession = MybatisUtils.getSqlSession(); - //2. 执行sql、获取结果、输出 - //方式1,getMapper - UserDao userDao = sqlSession.getMapper(UserDao.class); - List userList = userDao.getUserList(); - for (User user : userList) { - System.out.println(user); - } - - //3. 关闭SqlSession - sqlSession.close(); -} - -@Test -public void getUserById(){ -// 1. 获得SqlSession对象 - SqlSession sqlSession = MybatisUtils.getSqlSession(); -// 2. 执行sql - UserDao mapper = sqlSession.getMapper(UserDao.class); - User userById = mapper.getUserById(2); - System.out.println(userById); -// 3. 关闭 - sqlSession.close(); -} -``` - -> **insert** - -增删改需要提交事务 - -1. 编写接口 -2. mapper中的sql语句 - -```xml - - - insert into mybatis.user (id, name, pwd) - values (#{id}, #{name}, #{pwd}); - -``` - -3. 测试:增删改需要提交事务 - -```java -// 增删改需要提交事务 - sqlSession.commit(); -``` - -> **update** - -1. 编写接口 -2. sql语句 - -```xml - - - update mybatis.user - set name = #{name}, - pwd = #{pwd} - where id = #{id}; - -``` - -3. 测试:需要提交事务 - -> **Delete** - -1. 接口 -2. sql语句 - -```xml - - - delete - from mybatis.user - where id = #{id}; - -``` - -3. 测试:需要提交事务 - - - -## 使用Map传参 - -假如我们的实体类或者数据库中的表、字段或者参数过多,我们应当考虑使用Map - -使用User对象时,假如需要修改密码,当字段过多时,sql语句中还需要把其他字段给加上,很麻烦 - -使用map时: - -1. 接口 - -```java -int updateUser2(Map map); -``` - -2. sql语句 - -sql语句中传入的参数类型为map - -具体传入的参数不需要和数据库以及实体类中的对应,在map.put()中进行对应即可 - -```xml - - - update mybatis.user - set pwd = #{userPwd} - where id = #{userId}; - -``` - -3. 测试 - -```java -@Test -public void updateUser2Test() { - // 1.获取sqlsession对象 - SqlSession sqlSession = MybatisUtils.getSqlSession(); - // 2.sql - UserDao mapper = sqlSession.getMapper(UserDao.class); - - // map - Map map = new HashMap<>(); - map.put("userId", 3); - map.put("userPwd", "000000"); - - int i = mapper.updateUser2(map); - - // 增删改需要提交事务 - sqlSession.commit(); - // 3.关闭 - sqlSession.close(); -} -``` - -## 模糊查询 - -模糊查询需要防止sql注入 - -1. java代码执行的时候,传入通配符% % - -```java -List u = mapper.getUserLike("%tian%"); -``` - -2. mapper中的sql语句使用where 字段 like 加%,会导致sql注入 - -```xml -select * from mybatis.user where name like "%"#{value}"%" -``` - - - -# 3、配置解析 - -## 核心配置文件mybatis-config.xml - -配置文件包含了会深深影响Mybatis行为的设置和属性信息 - -* **属性properties** -* **设置settings** -* **类型别名typeAliases** -* **环境配置environments** - * environment(环境变量) - - transactionManager(事务管理器) - - dataSource(数据源) -* **映射器mappers** -* 了解 - * 类型处理器typeHandlers - * 对象工厂objectFactory - * 插件plugins - * 数据库厂商标识databaseProvider - -## 环境配置environments - -```xml - - - - - - - - - - - - - -``` - -**MyBatis 可以配置成适应多种环境,通过id选择使用哪一个** - -**每个 SqlSessionFactory 实例只能选择一种环境** - - - -* **事务管理器** - -在 MyBatis 中有两种类型的事务管理器(也就是 type="[**JDBC|MANAGED**]"),**默认使用JDBC:** - -* **数据源** - -有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]"),**默认使用pooled** - -**UNPOOLED**– 这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择。 - -**POOLED**– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。 - -连接数据库,数据库连接池:dbcp、c3p0、druid,用完会回收 - -**JNDI** – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用 - -## 属性properties - -**通过properties属性涉嫌引用配置文件** - -属性都是可以外部配置且可以动态替换的,既可以在典型的java属性文件中配置,也可可以通过properties元素的子元素来传递,【db.properties】 - -**通过外部配置db.properties** - -1. 编写配置文件:db.properties: - -```properties -driver=com.mysql.cj.jdbc.Driver -url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8 -username=root -password=123456 -``` - -2. xml中引入配置文件 - -在xml中,所有的标签都可以规定其顺序,properties=》settings=》typerAliases。。properties只能放在最上面 - -* 中间可以设置属性,设置用户名和密码 -* 如果两个设置的属性中和db.properties中有同一个字段,优先使用外部的db.properties - -```xml - - - -``` - -3. 在整个配置文件中用来替换需要动态配置的属性值 - -```xml - - - - - - -``` - -## 类型别名typerAliases - -类型别名可为 Java 类型设置一个**缩写名字**。 它仅用于 XML 配置,意在**降低冗余的全限定类名**书写。 - -```xml - - - - -``` - -也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如: - -在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 `domain.blog.Author` 的别名为 `author` - -```xml - - - -``` - -使用`com.tian.pojo.User`时只需要用`user`即可 - -**在实体类比较少的时候,使用第一种,实体类多的时候使用第二种,第一种可以自定义别名,第二种可以通过在实体类上增加注解来自定义别名,如下:** - -```java -@Alias("newName") -public class User{ - ... -} -``` - -## 设置settings - -这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为 - - - -| cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true \| false | true | -| :----------------: | ------------------------------------------------------------ | ------------------------------------------------------------ | ------ | -| lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 `fetchType` 属性来覆盖该项的开关状态。 | true \| false | false | -| logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J \| LOG4J \| LOG4J2 \| JDK_LOGGING \| COMMONS_LOGGING \| STDOUT_LOGGING \| NO_LOGGING | 未设置 | - -一个配置完整的 settings 元素的示例如下: - -```xml - - - - - - - - - - - - - - - - - -``` - -## 其他配置 - -* 类型处理器typeHandlers -* 对象工厂objectFactory -* 插件plugins - * mybatis-generator-core - * mybatis-plus:一个增强工具,简化mybatis - * 通用mapper -* 数据库厂商标识databaseProvider - -## 映射器mappers - -MapperRegistry:注册绑定我们的Mapper文件 - -MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们**需要告诉 MyBatis 到哪里去找到这些语句**。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 `file:///` 形式的 URL),或类名和包名等。 - -**maven中resources文件夹下的文件在编译后,都放在了根目录下** - -> **方式1 :推荐使用** - -```xml - - - - -``` - -> **方式2:使用class文件** - -**注意**: - -* 接口和他的Mapper配置文件必须同名 -* 接口和他的Mapper配置文件必须在同一个包下(Mapper配置文件可以在maven的resources目录下) - -```xml - - - - -``` - -> **方式3**:使用扫描包进行绑定注入 - -**注意:** - -* 接口和他的Mapper配置文件必须同名 -* 接口和他的Mapper配置文件必须在同一个包下 - -```xml - - - - -``` - -# 4、解决属性名和字段名不一致 - -> **问题** - -如:数据库中的字段名为pwd,实体类中的属性名为password - -User: - -```java -public class User { - private int id; - private String name; - private String password; - ... -} -``` - -Mapper: - -```xml - -``` - -test: - -```java -@Test -public void getUserByIdTes() { - SqlSession sqlSession = MybatisUtils.getSqlSession(); - UserMapper mapper = sqlSession.getMapper(UserMapper.class); - User userById = mapper.getUserById(2); - System.out.println(userById); -} -``` - -查询得到输出: - -`User{id=2, name='tian2', password='null'}` - -分析: - -由于数据库中的字段为pwd,在测试查询时,传入的参数为password,导致查询不到 - -> **解决方法:** - -* 起别名 - -在mapper中修改sql语句: - -```xml - -``` - -* resultMap - -## resultMap - -> 结果集映射 - -* `resultMap`元素是Mybatis中最重要最强大的元素 -* 设计思想是:对于简单的语句根本不需要配置显式的结果集映射,对于复杂的语句,只需要描述他们的关系就可以了 - -> 简单的结果集映射 - -```xml - - - - - - - -``` - -> 复杂 - -一对多,多对一 - - - -# 5、日志 - -## 日志工厂 - -> **介绍** - -如果一个数据库操作出现了异常,需要排错,需要日志 - -曾经用:sout、debug - -现在:日志工厂 - -mybatis提供的: - -* SLF4J - -* **LOG4J** -* LOG4J2 -* JDK_LOGGING -* COMMONS_LOGGING -* **STDOUT_LOGGING** -* NO_LOGGING - -具体使用哪一个,在mybatis-config.xml中的settings中设置,默认不使用 - -![image-20210807195224236](https://gitee.com/tianzhendong/img/raw/master//images/image-20210807195224236.png) - -> **标准的日志工厂实现** - -使用`STDOUT_LOGGING`不需要导包 - -* 在mybatis-config.xml中配置日志 - -```xml - - - - - -``` - -* 配置后输出: - -```bash -Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter. -Class not found: org.jboss.vfs.VFS -JBoss 6 VFS API is not available in this environment. -Class not found: org.jboss.vfs.VirtualFile -VFS implementation org.apache.ibatis.io.JBoss6VFS is not valid in this environment. -Using VFS adapter org.apache.ibatis.io.DefaultVFS -Find JAR URL: file:/C:/javaCode/MybatisStudy/Mybatis-02/target/classes/com/tian/pojo -Not a JAR: file:/C:/javaCode/MybatisStudy/Mybatis-02/target/classes/com/tian/pojo -Reader entry: User.class -Listing file:/C:/javaCode/MybatisStudy/Mybatis-02/target/classes/com/tian/pojo -Find JAR URL: file:/C:/javaCode/MybatisStudy/Mybatis-02/target/classes/com/tian/pojo/User.class -Not a JAR: file:/C:/javaCode/MybatisStudy/Mybatis-02/target/classes/com/tian/pojo/User.class -Reader entry: ���� < : -Find JAR URL: file:/C:/javaCode/MybatisStudy/Mybatis-01/target/classes/com/tian/pojo -Not a JAR: file:/C:/javaCode/MybatisStudy/Mybatis-01/target/classes/com/tian/pojo -Reader entry: User.class -Listing file:/C:/javaCode/MybatisStudy/Mybatis-01/target/classes/com/tian/pojo -Find JAR URL: file:/C:/javaCode/MybatisStudy/Mybatis-01/target/classes/com/tian/pojo/User.class -Not a JAR: file:/C:/javaCode/MybatisStudy/Mybatis-01/target/classes/com/tian/pojo/User.class -Reader entry: ���� < : -Checking to see if class com.tian.pojo.User matches criteria [is assignable to Object] -Checking to see if class com.tian.pojo.User matches criteria [is assignable to Object] -PooledDataSource forcefully closed/removed all connections. -PooledDataSource forcefully closed/removed all connections. -PooledDataSource forcefully closed/removed all connections. -PooledDataSource forcefully closed/removed all connections. -Opening JDBC Connection -Created connection 1446983876. -Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@563f38c4] -==> Preparing: select * from mybatis.user where id = ? -==> Parameters: 2(Integer) -<== Columns: id, name, pwd -<== Row: 2, tian2, 1234567 -<== Total: 1 -User{id=2, name='tian2', password='1234567'} -``` - - - -## Log4j - -**注**:需要导包 - -> **什么是log4j** - -* Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件 -* 我们也可以控制每一条日志的输出格式 -* 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程 -* 可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。 - -> **使用log4j** - -1. 导包 - -```xml - - log4j - log4j - 1.2.17 - -``` - -2. 新增配置文件`log4j.properties` - -网上找即可 - -```properties -#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码 -log4j.rootLogger=DEBUG,console,file - -#控制台输出的相关设置 -log4j.appender.console = org.apache.log4j.ConsoleAppender -log4j.appender.console.Target = System.out -log4j.appender.console.Threshold=DEBUG -log4j.appender.console.layout = org.apache.log4j.PatternLayout -log4j.appender.console.layout.ConversionPattern=[%c]-%m%n - -#文件输出的相关设置 -log4j.appender.file = org.apache.log4j.RollingFileAppender -log4j.appender.file.File=./log/tian.log -log4j.appender.file.MaxFileSize=10mb -log4j.appender.file.Threshold=DEBUG -log4j.appender.file.layout=org.apache.log4j.PatternLayout -log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n - -#日志输出级别 -log4j.logger.org.mybatis=DEBUG -log4j.logger.java.sql=DEBUG -log4j.logger.java.sql.Statement=DEBUG -log4j.logger.java.sql.ResultSet=DEBUG -log4j.logger.java.sql.PreparedStatement=DEBUG -``` - -3. 配置log4j为日志的实现 - -```XML - - - - -``` - -4. **log4j使用** - -* 在要使用log4j的类中,导入包`import org.apache.log4j.Logger` -* 获取日志对象,参数为当前类的class - -```java -static Logger logger = Logger.getLogger(UserDaoTest.class); -``` - -* 编写测试代码 - -日志级别:info\debug\error - -```java -package com.tian.dao; - -import org.apache.log4j.Logger; -import org.junit.Test; - -/** - * @program: MybatisStudy - * @description: 测试 - * @author: TianZD - * @create: 2021-08-06 22:56 - **/ -public class UserDaoTest { - static Logger logger = Logger.getLogger(UserDaoTest.class); - @Test - public void testLog4j() { - //不同的级别 - logger.info("info:进入了testLog4j方法"); - logger.debug("debug:进入了testLog4j方法"); - logger.error("error:进入了testLog4j方法"); - } -} -``` - -* 输出 - -在控制台和配置的输出文件中会输出以下: - -```bash -[com.tian.dao.UserDaoTest]-info:进入了testLog4j方法 -[com.tian.dao.UserDaoTest]-debug:进入了testLog4j方法 -[com.tian.dao.UserDaoTest]-error:进入了testLog4j方法 -``` - -# 6、分页 - -> **为什么要分页** - -减少数据的处理量 - -> **limit分页** - -```sql -SELECT * FROM user LIMIT startIndex,pageSize; -``` - -> 使用mybatis分页 - -核心sql - -1. 接口 - -```java -// 分页查询 -List getUserByLimit(Map map); -``` - -2. Mapper.xml - -```xml - - - - - - - -``` - -3. 测试 - -```java -package com.tian.dao; - -import com.tian.pojo.User; -import com.tian.utils.MybatisUtils; -import org.apache.ibatis.session.SqlSession; -import org.apache.log4j.Logger; -import org.junit.Test; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * @program: MybatisStudy - * @description: 测试 - * @author: TianZD - * @create: 2021-08-06 22:56 - **/ -public class UserDaoTest { - static Logger logger = Logger.getLogger(UserDaoTest.class); - @Test - public void TestLimit() { - //1. 获取sqlsession对象 - SqlSession sqlSession = MybatisUtils.getSqlSession(); - //2. sql, 获取mapper - UserMapper mapper = sqlSession.getMapper(UserMapper.class); - //2.1 map - Map map = new HashMap<>(); - map.put("startIndex", 1); - map.put("pageSize", 2); - //2.2 sql - List userByLimit = mapper.getUserByLimit(map); - for (User user : userByLimit) { - logger.info(user); - } - //3. 关闭 - sqlSession.close(); - } -} -``` - -# 7、使用注解开发(mybatis不推荐) - -## 面向接口编程 - -> **三个面向** - -**面向过程**:考虑问题时,以一个具体的流程为单位,考虑它的实现 - -**面向对象**:考虑问题时,以对象为单位,考虑他的属性和方法 - -**面向接口**:更多体现的是对系统整体的架构 - -> **理解** - -**接口**:应是定义(规范、约束)与实现(名实分离的原则)的分离,本身反映了系统设计人员对系统的抽象理解 - -**面向过程编程(`Procedure Oriented`、简称`PO`)** 和 **面向对象编程(`Object Oriented`、简称`OO`)** 我们一定听过,然而实际企业级开发里受用更多的一种编程思想那就是:**面向接口编程(`Interface-Oriented`)**! - -接口这个概念我们一定不陌生,实际生活中**最常见的例子就是**:插座! - -我们只需要事先定义好插座的**接口标准**,各大插座厂商只要按这个接口标准生产,管你什么牌子、内部什么电路结构,这些均和用户无关,用户拿来就可以用;而且即使插座坏了,只要换一个符合接口标准的新插座,一切照样工作! - -同理,实际代码设计也是这样! - -我们在设计一个软件的代码架构时,我们都希望**事先约定**好各个功能的**接口**(即:约定好接口签名和方法),实际开发时我们只需要实现这个接口就能完成具体的功能!后续即使项目变化、功能升级,程序员只需要按照接口约定重新实现一下,就可以达到系统升级和扩展的目的! - -正好,Java中天生就有`interface`这个语法,这简直是为面向接口编程而生的! - -> **优点** - -* 代码的灵活解耦 -* 代码的扩展性 -* 提高复用 -* 分层开发中,上层不用管具体实现,大家遵守共同的标准,使得开发变得容易,规范性更好 - -## mybatis中的注解开发 - -**mybatis中不推荐使用注解**,其他框架推荐 - -使用注解来映射简单语句会使**代码显得更加简洁**,但对于**稍微复杂一点的语句,Java 注解不仅力不从心**,还会让你本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。 - -用来替代mapper.xml配置文件 - -1. **mybatis.config.xml**中绑定接口 - -```xml - - - - -``` - -2. **UserMapper.interface** - -```java -public interface UserMapper { - //注解 - @Select("select id,name,pwd as password from mybatis.user") - List getUsers(); -} -``` - -3. **test.java, 没有变化** - -```java -public class UserDaoTest { - @Test - public void test() { - SqlSession sqlSession = MybatisUtils.getSqlSession(); - UserMapper mapper = sqlSession.getMapper(UserMapper.class); - List users = mapper.getUsers(); - for (User user : users) { - System.out.println(user); - } - sqlSession.close(); - } -} -``` - -> **本质** - -**反射机制实现** - -> **底层** - -**动态代理** - -# 8、Mybatis执行流程 - -![image-20210807224915524](https://gitee.com/tianzhendong/img/raw/master//images/image-20210807224915524.png) - -# 9、多对一、一对多 - -**关联**:多个学生,关联一个老师,多对一 - -**集合**:一个老师,有很多学生,一对多 - -。。。 - -# 10、动态SQL - -> **理解** - -Mybatis的强大特性之一就是动态sql,使用动态sql可以拜托不同条件下拼接sql语句的痛苦 - -**动态sql就是根据不同的条件,生成不同的sql语句** - -如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。 - -- if -- choose (when, otherwise) -- trim (where, set) -- foreach - - - -## 搭建环境 - -> **创建sql表** - -```sql -CREATE TABLE `blog`( - `id` varchar(50) NOT NULL COMMENT '博客id', - `title` varchar(100) not null comment '博客标题', - `author` varchar(30) not null comment '博客作者', - `create_time` datetime not null comment '创建时间', - `views` int(30) not null comment '浏览量' -)engine = InnoDB default charset=utf8 -``` - -> **创建一个基础工程** - -1. 导包 - -2. 编写配置文件mybatis-config.xml和db.properties - -```xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -``` - -**mybatisUtils类省略** - -**编写IDUtils类,用于生成随机id** - -```java -import java.util.UUID; -public class IDUtils { - public static String getId() { - return UUID.randomUUID().toString().replaceAll("-", ""); - } -} -``` - - - - - - - -3. 编写实体类 - -```java -@Data -public class Blog { - private String id; - private String title; - private String author; - //data的属性名和字段名不一致,字段名为create_time - private Date createTime; - private int views; -} -``` - -4. 编写实体类对应Mapper接口和Mapper.xml文件 - -```java -public interface BlogMapper { - // 插入数据 - int addBlog(Blog blog); -} -``` - -```xml - - - insert into mybatis.blog(id, title, author, create_time, views) - values (#{id},#{title},#{author},#{createTime},#{views}); - - -``` - -**生成的数据库如下:** - -![image-20210808003003987](https://gitee.com/tianzhendong/img/raw/master//images/image-20210808003003987.png) - - - -## 动态sql-IF - -1. **BlogMapper.interface** - -```java - //查询博客 - List queryBlogIF(Map map); -``` - -2. **BlogMapper.xml** - -```xml - -``` - -where属性:*where* 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,*where* 元素也会将它们去除。 - -```xml -select * from mybatis.blog - - - and title = #{title} - - - and author = #{author} - - -``` - -3. **test** - -**传入空值,查询全部** - -```java -@Test -public void queryBlogIFTest() { - SqlSession sqlSession = MybatisUtils.getSqlSession(); - BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); - HashMap map = new HashMap(); - //传入空值,查询全部 - List blogs = mapper.queryBlogIF(map); - for (Blog blog : blogs) { - System.out.println(blog); - } - sqlSession.close(); -} -``` - -**传入值,过滤查询** - -```java -public void queryBlogIFTest() { - SqlSession sqlSession = MybatisUtils.getSqlSession(); - BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); - - HashMap map = new HashMap(); - //赋值,过滤 - map.put("title", "blog1"); - List blogs = mapper.queryBlogIF(map); - for (Blog blog : blogs) { - System.out.println(blog); - } - sqlSession.close(); -} -``` - -## 动态sql-choose(when,otherwise) - -从多个条件中选择一个使用,有点像 Java 中的 switch 语句 - -传入了 “title” 就按 “title” 查找,传入了 “author” 就按 “author” 查找的情形。若两者都没有传入,就根据views进行查找 - -```xml - -``` - - - -## set元素 - -用于动态更新语句的类似解决方案叫做 *set*。*set* 元素可以用于动态包含需要更新的列,忽略其它不更新的列。比如 - -```xml - - update Author - - username=#{username}, - password=#{password}, - email=#{email}, - bio=#{bio} - - where id=#{id} - -``` - -*set* 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。 - - - -## sql片段 - -抽取sql部分片段,方便重复代码复用 - -```xml - - - - and title = #{title} - - - and author = #{author} - - - - - SELECT * - FROM mybatis.blog - WHERE ID in - - - id = #{item} - - - -``` - -# 11、缓存 - -## 简介 - -> **什么是缓存** - -放在内存中的临时数据 - -一次查询的结果暂时放在内存中,再次查询相同数据的时候直接从缓存取,不用再走数据库了,从而提高查询雄安率,解决了高并发系统的性能问题 - -> **为什么使用缓存** - -减少和数据库的交互次数,减小系统开销,提高系统效率 - -> **什么样的数据能使用缓存** - -经常查询并且不经常改变的数据 - - - -## Mybatis缓存 - -> mybatis缓存介绍 - -mybatis默认定义了两级缓存:一级缓存和二级缓存 - -* 默认下,只有一级缓存开启,(sqlsession级别的缓存,也叫本地缓存,在sqlsession创建和关闭之间的部分) -* 二级需要手动开启和配置,基于namespace级别的缓存 -* 为了提高扩展性,mybatis定义了缓存接口cache,可通过实现cache接口自定义二级缓存 - - - -> **一级缓存** - -默认开启,本地缓存 - -sqlsession级别的缓存,在sqlsession创建和关闭之间,使用代码查询统一数据多次,只会和数据库交互一次 - -> **二级缓存** - -* 全局缓存 -* 基于namespace级别的缓存,一个名称空间,对应一个二级缓存 -* 工作机制 - * 一个会话查询一条数据,数据被放在当前会话的一级缓存中 - * 会话关闭后,对应的一级缓存就没了,但是我们想要的是会话关闭了,一级缓存中的数据保存到二级缓存中 - * 新的会话查询数据,可以从二级缓存中获取内容 - * 不同的mapper查出的数据会放在自己对应的缓存中 - - - -**步骤**: - -1. 开启全局缓存 - -mybatis-config.xml - -```xml - - - - -``` - -2. 在mapper.xml中加入``标签 - -使用:在mapper.xml中加标签 - -```xml - -``` - -高级配置: - -```xml - -``` - -这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。 - -## Mybatis缓存原理 - -![](https://gitee.com/tianzhendong/img/raw/master//images/image-20210808041200380.png) - diff --git a/source/_posts/Java/SSM整合.md b/source/_posts/Java/SSM整合.md deleted file mode 100644 index 5157586..0000000 --- a/source/_posts/Java/SSM整合.md +++ /dev/null @@ -1,921 +0,0 @@ ---- -title: SSM整合 -author: TianZD -top: true -cover: true -toc: true -mathjax: false -summary: SSM整合学习笔记,粗略学了一下,没有参考价值 -tags: - - SSM - - Java - - 学习笔记 -categories: - - java -reprintPolicy: cc_by -abbrlink: 254393f0 -date: 2022-04-29 11:10:43 -coverImg: -img: -password: ---- - - - -[toc] - - - -# 1、SpringMVC-SSM整合 - -## 1.1、环境 - -> 环境 - -* IDE:IDEA 2021 1.1 -* 项目管理:Maven ,apache-maven-3.8.1 -* JAVA:JDK 16.01 -* Web服务器:apache-tomcat-9.0.50 -* 数据库:MySQL 8.0.26 -* 数据库管理工具:Navicat premium 15 - -> 数据库环境 - -```sql -CREATE DATABASE `ssmbuild`; -USE `ssmbuild`; -DROP TABLE IF EXISTS `books`; -CREATE TABLE `books` ( - `bookID` INT(10) NOT NULL AUTO_INCREMENT COMMENT '书id', - `bookName` VARCHAR(100) NOT NULL COMMENT '书名', - `bookCounts` INT(11) NOT NULL COMMENT '数量', - `detail` VARCHAR(200) NOT NULL COMMENT '描述', - KEY `bookID` (`bookID`) -)ENGINE = INNODB DEFAULT CHARSET = utf8; - -INSERT INTO `books`(`bookID`,`bookName`,`bookCounts`,`detail`) -VALUES (1,'java',1,'从入门到放弃'), - (2,'MySQL',10,'从删库到跑路'), - (3,'Linux',5,'从进门到坐牢'); -``` - -> 基本环境搭建 - -* 新建maven工程 -* 配置pom.xml - * 导入依赖 - * 配置静态资源 - -```xml - - - - - junit - junit - 4.12 - test - - - - mysql - mysql-connector-java - 8.0.25 - - - - com.mchange - c3p0 - 0.9.5.2 - - - - javax.servlet - servlet-api - 2.5 - - - javax.servlet.jsp - jsp-api - 2.2 - - - javax.servlet - jstl - 1.2 - - - - org.mybatis - mybatis - 3.5.7 - - - - org.mybatis - mybatis-spring - 2.0.2 - - - - org.springframework - spring-webmvc - 5.3.9 - - - org.springframework - spring-jdbc - 5.3.9 - - - - - log4j - log4j - 1.2.17 - - - - - - - - - src/main/java - - **/*.properties - **/*.xml - - false - - - src/main/resources - - **/*.properties - **/*.xml - - false - - - -``` - -> IDEA连接数据库 - -![image-20210815222657080](https://gitee.com/tianzhendong/img/raw/master//images/image-20210815222657080.png) - -> 建立项目包结构 - -* dao -* pojo -* controller -* service - -> 建立核心配置文件 - -* spring:applicationContext.xml - -```xml - - - - -``` - -* mybatis:mybatis-config.xml和database.properties - -mybatis-config.xml - -```xml - - - - - - - - - - - - - - - - -``` - -database.properties - -```properties -jdbc.driver=com.mysql.cj.jdbc.Driver -#如果使用mysql8.0以上,需要增加时区设置 -jdbc.url=jdbc:mysql://localhost:3306/ssmbuild?useSSL=true&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai -jdbc.username=root -jdbc.password=123456 -``` - -* log4j.properties - -```properties -#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码 -log4j.rootLogger=DEBUG,console,file - -#控制台输出的相关设置 -log4j.appender.console = org.apache.log4j.ConsoleAppender -log4j.appender.console.Target = System.out -log4j.appender.console.Threshold=DEBUG -log4j.appender.console.layout = org.apache.log4j.PatternLayout -log4j.appender.console.layout.ConversionPattern=[%c]-%m%n - -#文件输出的相关设置 -log4j.appender.file = org.apache.log4j.RollingFileAppender -log4j.appender.file.File=./log/tian.log -log4j.appender.file.MaxFileSize=10mb -log4j.appender.file.Threshold=DEBUG -log4j.appender.file.layout=org.apache.log4j.PatternLayout -log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n - -#日志输出级别 -log4j.logger.org.mybatis=DEBUG -log4j.logger.java.sql=DEBUG -log4j.logger.java.sql.Statement=DEBUG -log4j.logger.java.sql.ResultSet=DEBUG -log4j.logger.java.sql.PreparedStatement=DEBUG -``` - -## 1.2、Mybatis层 - -主要是dao层和service层,底层相关,MVC的Model层,数据和业务 - - - -> pojo层 - -```java -public class Books { - private int bookID; - private String bookName; - private int bookCounts; - private String detail; -//get、set、toString、construct -} -``` - -> dao层 - -* 接口 - -```java -public interface BookMapper { - //add - int addBook(Books books); - - //delete - int deleteBook(@Param("bookID") int id); - - //update - int updateBook(Books books); - - //select one - Books selectBookById(@Param("bookID") int id); - - //select all - List selectBookAll(); -} -``` - -* Mapper.xml - -```xml - - - - - insert into ssmbuild.books (bookName, bookCounts, detail) - values (#{bookName},#{bookCounts},#{detail}); - - - - delete - from ssmbuild.books - where bookID = #{bookID}; - - - - update ssmbuild.books - set bookName = #{bookName}, bookCounts = #{bookCounts}, detail = #{detail} - where bookID = #{bookID}; - - - - - - - -``` - -* 绑定mapper.xml到mybatis-config.xml配置文件中 - -```xml - - - - - -``` - -> service层 - -* BookService接口 - -```java -public interface BookService { - //add - int addBook(Books books); - - //delete - int deleteBook(int id); - - //update - int updateBook(Books books); - - //select one - Books selectBookById(int id); - - //select all - List selectBookAll(); -} -``` - - - -* 接口实现类 - -```java -public class BookServiceImpl implements BookService{ - //业务层调用dao层:组合dao层 - private BookMapper bookMapper; - - public void setBookMapper(BookMapper bookMapper) { - this.bookMapper = bookMapper; - } - - @Override - public int addBook(Books books) { - return bookMapper.addBook(books); - } - - @Override - public int deleteBook(int id) { - return bookMapper.deleteBook(id); - } - - @Override - public int updateBook(Books books) { - return bookMapper.updateBook(books); - } - - @Override - public Books selectBookById(int id) { - return bookMapper.selectBookById(id); - } - - @Override - public List selectBookAll() { - return bookMapper.selectBookAll(); - } -} -``` - -## 1.3、Spring层 - -> dao层 - -spring-dao.xml - -* 关联数据库配置文件 -* 连接池 -* sqlSessionFactory -* sqlSession - -```xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -``` - -> service - -spring-service.xml - -* 扫描service下的包 -* 将业务类注入到spring,可以通过配置或者注解实现 -* 声明式事务配置 -* AOP事务支持 - -```xml - - - - - - - - - - - - - - - - - - - -``` - -## 1.4、SpringMVC层 - -> 增加web支持 - -> web.xml - -* dispatchservlet -* 乱码过滤 - -```xml - - - - - - dispatcherServlet - org.springframework.web.servlet.DispatcherServlet - - - contextConfigLocation - classpath:springmvc-servlet.xml - - - 1 - - - dispatcherServlet - / - - - - - encodingFilter - org.springframework.web.filter.CharacterEncodingFilter - - encoding - utf-8 - - - - encodingFilter - /* - - - - - 15 - - - -``` - -> springmvc-servlet.xml - -```xml - - - - - - - - - - - - - - - - - - -``` - -## 1.5、配置文件整合 - -applicationContext.xml - -```xml - - - - - - - -``` - -# 2、实际业务实现 - -## 2.1、查询书籍功能 - -### 查询 - -将controller和web交互 - -> controller - -*BookController.class* - -```java -@Controller -@RequestMapping("/book") -public class BookController { - //controller层调用service层 - @Autowired - @Qualifier("BookServiceImpl") - private BookService bookService; - - //查询全部书籍,并返回书籍展示页面 - public String selectAllBook(Model model) { - List books = bookService.selectBookAll(); - model.addAttribute("list", books); - return "allBook"; - } - -} -``` - -> jsp - -* allBook.jsp -* index.jsp,设置由首页跳转 - -```jsp -<%@ page contentType="text/html;charset=UTF-8" language="java" %> - - - 书籍展示 - - -

书籍展示

- - -``` - -```jsp -<%@ page contentType="text/html;charset=UTF-8" language="java" %> - - - $Title$ - - - $END$ -

- 进入书籍展示页面 -

- - -``` - -### 错误 - -```bash -org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.tian.service.BookService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier("BookServiceImpl")} -``` - -bean不存在 - -> 解决 - -web.xml中需要引入applicationContxt.xml而不是springmvc-config.xml - -### 美化 - -> 首页 - -```jsp -<%@ page contentType="text/html;charset=UTF-8" language="java" %> - - - 首页 - - - -

- 进入书籍展示页面 -

- - -``` - -![](https://gitee.com/tianzhendong/img/raw/master//images/image-20210816033535794.png) - -> 查询页 - -```jsp -<%@ page contentType="text/html;charset=UTF-8" language="java" %> - - - 书籍展示 - - - - - - - - - - - - - - - - - - - - -
书籍列表
书籍ID书籍名称书籍数量书籍描述
${books.bookID}${books.bookName}${books.bookCounts}${books.detail}
- - -``` - -![image-20210816201951940](https://gitee.com/tianzhendong/img/raw/master//images/image-20210816201951940.png) - -## 2.2、添加书籍功能 - -> 待跳转页面 - -addBook.jsp - -```jsp -<%@ page contentType="text/html;charset=UTF-8" language="java" %> - - - 添加书籍页面 - - -
- - - - - - - - - - - - - - - - - -
书籍名称:
书籍数量:
书籍描述:
-
- - -``` - -![image-20210816205546620](https://gitee.com/tianzhendong/img/raw/master//images/image-20210816205546620.png) - -> controller - -```java -// 跳转到添加书籍界面 -@RequestMapping("/toAddBook") -public String toAddBook() { - return "addBook"; -} - -//添加书籍请求 -@RequestMapping("/addBook") -public String addBook(Books book) { - bookService.addBook(book); - return "redirect:/book/allBook"; -} -``` - -> 跳转按钮 - -allBook.jsp - -```xml - - - - 添加书籍 - -``` - -![image-20210816210610577](https://gitee.com/tianzhendong/img/raw/master//images/image-20210816210610577.png) - -## 2.3、修改、删除书籍 - -> 按钮 - -allBook.jsp - -```jsp - -  修改 - | - 删除  - -``` - -![image-20210816221451118](https://gitee.com/tianzhendong/img/raw/master//images/image-20210816221451118.png) - -> controller - -```java - //跳转到修改请求页面 - @RequestMapping("/toUpdateBook/{bookId}") - public String toUpdateBook(@PathVariable("bookId") int id, Model model) { - Books books = bookService.selectBookById(id); - model.addAttribute("bookSelected", books); - return "updateBook"; - } - - //修改书籍 - @RequestMapping("/updateBook") - public String updateBook(Books books) { - bookService.updateBook(books); - return "redirect:/book/allBook"; - } - - //删除书籍 - @RequestMapping("/deleteBook/{bookId}") - public String deleteBook(@PathVariable("bookId") int id) { - bookService.deleteBook(id); - return "redirect:/book/allBook"; - } -} -``` - -> 待跳转页面 - -updateBook.jsp - -```jsp -<%@ page contentType="text/html;charset=UTF-8" language="java" %> - - - 修改书籍 - - -
- <%--隐藏于传递不需要用户修改的bookID--%> - - - - - - - - - - - - - - - - - - -
书籍名称:
书籍数量:
书籍描述:
-
- - -``` - -![image-20210816223906448](https://gitee.com/tianzhendong/img/raw/master//images/image-20210816223906448.png) - - - diff --git a/source/_posts/Java/Shiro学习.md b/source/_posts/Java/Shiro学习.md deleted file mode 100644 index cc2415b..0000000 --- a/source/_posts/Java/Shiro学习.md +++ /dev/null @@ -1,1397 +0,0 @@ ---- -title: Shiro学习 -author: TianZD -top: true -cover: true -toc: true -mathjax: false -summary: Shiro学习笔记,粗略学了一下,没有参考价值 -tags: - - Shiro - - java - - 学习笔记 -categories: - - java -reprintPolicy: cc_by -abbrlink: c0a48af9 -date: 2022-04-29 10:50:23 -coverImg: -img: -password: ---- - ---- -title: shiro -tags: java -notebook: JAVA - ---- - -# 1、Shiro简介 - -## 1.1、Shiro 是什么? - -* Apache Shiro 是 Java 的一个安全(权限)框架。 -* Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在 JavaSE 环境,也可以用在 JavaEE 环境。 -* Shiro 可以完成:认证、授权、加密、会话管理、与Web 集成、缓存等。 -* 下载地址 - * 官网:http://shiro.apache.org/ - * github:https://github.com/apache/shiro - -## 1.2、有哪些功能? - -[![image-20200729114647110](https://camo.githubusercontent.com/39792d7499fb7656be26a3573d232e7932aa10e43d2950cc47f3d1f53de77554/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303732393131343634373131302e706e67)](https://camo.githubusercontent.com/39792d7499fb7656be26a3573d232e7932aa10e43d2950cc47f3d1f53de77554/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303732393131343634373131302e706e67) - -* Authentication:身份认证/登录,验证用户是不是拥有相应的身份 -* Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能进行什么操作,如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限 -* Session Management:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境,也可以是Web 环境的 -* Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储 -* Web Support:Web 支持,可以非常容易的集成到Web 环境 -* Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率 -* Concurrency:Shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去 -* Testing:提供测试支持 -* "Run As":允许一个用户假装为另一个用户(如果他们允许)的身份进行访问 -* Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了 - -## 1.3、Shiro架构(外部) - -从外部来看Shiro,即从应用程序角度的来观察如何使用Shiro完成工作 - -[![image-20200729114702566](https://camo.githubusercontent.com/e34dd392bfaaa048ccc5967d91a82e0eb538cdec6fdbfd602817f3a9e890fc70/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303732393131343730323536362e706e67)](https://camo.githubusercontent.com/e34dd392bfaaa048ccc5967d91a82e0eb538cdec6fdbfd602817f3a9e890fc70/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303732393131343730323536362e706e67) - -* Subject:应用代码直接交互的对象是Subject,也就是说Shiro的对外API 核心就是Subject。Subject 代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;与Subject 的所有交互都会委托给SecurityManager;Subject 其实是一个门面,SecurityManager才是实际的执行者 -* SecurityManager:安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且其管理着所有Subject;可以看出它是Shiro的核心,它负责与Shiro的其他组件进行交互,它相当于SpringMVC中DispatcherServlet的角色 -* Realm:Shiro从Realm 获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm 得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm 看成DataSource - -## 1.4、Shiro架构(内部) - -[![image-20200729114720578](https://camo.githubusercontent.com/3c0eb32912aa456b96172a990316de48563666257f2b5bcb7889df7af26e4419/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303732393131343732303537382e706e67)](https://camo.githubusercontent.com/3c0eb32912aa456b96172a990316de48563666257f2b5bcb7889df7af26e4419/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303732393131343732303537382e706e67) - -* Subject:任何可以与应用交互的“用户”; -* SecurityManager:相当于SpringMVC中的DispatcherServlet;是Shiro的心脏;所有具体的交互都通过SecurityManager进行控制;它管理着所有Subject、且负责进行认证、授权、会话及缓存的管理。 -* Authenticator:负责Subject 认证,是一个扩展点,可以自定义实现;可以使用认证策略(Authentication Strategy),即什么情况下算用户认证通过了; -* Authorizer:授权器、即访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能; -* Realm:可以有1 个或多个Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是JDBC 实现,也可以是内存实现等等;由用户提供;所以一般在应用中都需要实现自己的Realm; -* SessionManager:管理Session 生命周期的组件;而Shiro并不仅仅可以用在Web 环境,也可以用在如普通的JavaSE环境 CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少改变,放到缓存中后可以提高访问的性能 -* Cryptography:密码模块,Shiro提高了一些常见的加密组件用于如密码加密/解密。 - -# 2、Hello World - -## 2.1、快速实践 - -* 查看官方文档:http://shiro.apache.org/tutorial.html - -* 官方的quickstart : https://github.com/apache/shiro/tree/master/samples/quickstart/ - - [![image-20200729115148574](https://camo.githubusercontent.com/f8297239e27c6e9dac7cba9c91042bd0276075bc593e48a36dcaf90e85cf49ef/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303732393131353134383537342e706e67)](https://camo.githubusercontent.com/f8297239e27c6e9dac7cba9c91042bd0276075bc593e48a36dcaf90e85cf49ef/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303732393131353134383537342e706e67) - -1. 创建一个maven父工程,用来学习Shiro,删掉不必要的部分 - -2. 创建一个普通的Maven子工程:hell-shiro - - [![image-20200729120114648](https://camo.githubusercontent.com/304172a69b541dc5d1019428828b8549f1b9f21d1911640cc6552ab0f96b9481/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303732393132303131343634382e706e67)](https://camo.githubusercontent.com/304172a69b541dc5d1019428828b8549f1b9f21d1911640cc6552ab0f96b9481/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303732393132303131343634382e706e67) - -3. 根据[官方文档](https://github.com/apache/shiro/blob/master/samples/quickstart/pom.xml),我们导入Shiro的依赖 - - [![image-20200729120207730](https://camo.githubusercontent.com/0465f0a1e9d59c306b118c89f971feac1805f79ac5d6349a4f7e16e8883493e7/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303732393132303230373733302e706e67)](https://camo.githubusercontent.com/0465f0a1e9d59c306b118c89f971feac1805f79ac5d6349a4f7e16e8883493e7/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303732393132303230373733302e706e67) - - [版本号点击这里](https://mvnrepository.com/artifact/org.apache.shiro/shiro-core) - - ``` - - - org.apache.shiro - shiro-core - 1.5.3 - - - - - org.slf4j - jcl-over-slf4j - 1.7.26 - - - org.slf4j - slf4j-log4j12 - 1.7.26 - - - log4j - log4j - 1.2.17 - - - ``` - -4. 相关配置文件 - - * log4j.properties——[官网](https://github.com/apache/shiro/blob/master/samples/quickstart/src/main/resources/log4j.properties) - - ``` - log4j.rootLogger=INFO, stdout - - log4j.appender.stdout=org.apache.log4j.ConsoleAppender - log4j.appender.stdout.layout=org.apache.log4j.PatternLayout - log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n - - # General Apache libraries - log4j.logger.org.apache=WARN - - # Spring - log4j.logger.org.springframework=WARN - - # Default Shiro logging - log4j.logger.org.apache.shiro=INFO - - # Disable verbose logging - log4j.logger.org.apache.shiro.util.ThreadContext=WARN - log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN - ``` - - * shiro.ini——[官网](https://github.com/apache/shiro/blob/master/samples/quickstart/src/main/resources/shiro.ini) - - ``` - [users] - # user 'root' with password 'secret' and the 'admin' role - root = secret, admin - # user 'guest' with the password 'guest' and the 'guest' role - guest = guest, guest - # user 'presidentskroob' with password '12345' ("That's the same combination on - # my luggage!!!" ;)), and role 'president' - presidentskroob = 12345, president - # user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz' - darkhelmet = ludicrousspeed, darklord, schwartz - # user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz' - lonestarr = vespa, goodguy, schwartz - - # ----------------------------------------------------------------------------- - # Roles with assigned permissions - # - # Each line conforms to the format defined in the - # org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc - # ----------------------------------------------------------------------------- - [roles] - # 'admin' role has all permissions, indicated by the wildcard '*' - admin = * - # The 'schwartz' role can do anything (*) with any lightsaber: - schwartz = lightsaber:* - # The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with - # license plate 'eagle5' (instance specific id) - goodguy = winnebago:drive:eagle5 - ``` - - * 启动类 Quickstart——[官网](https://github.com/apache/shiro/blob/master/samples/quickstart/src/main/java/Quickstart.java) - - ``` - /* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - - import org.apache.shiro.SecurityUtils; - import org.apache.shiro.authc.*; - import org.apache.shiro.config.IniSecurityManagerFactory; - import org.apache.shiro.mgt.SecurityManager; - import org.apache.shiro.session.Session; - import org.apache.shiro.subject.Subject; - import org.apache.shiro.util.Factory; - import org.slf4j.Logger; - import org.slf4j.LoggerFactory; - - - /** - * Simple Quickstart application showing how to use Shiro's API. - * 简单入门Shiro使用API - * - * @since 0.9 RC2 - */ - public class Quickstart { - - private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class); - - - public static void main(String[] args) { - - // The easiest way to create a Shiro SecurityManager with configured - // realms, users, roles and permissions is to use the simple INI config. - // We'll do that by using a factory that can ingest a .ini file and - // return a SecurityManager instance: - - // Use the shiro.ini file at the root of the classpath - // (file: and url: prefixes load from files and urls respectively): - // 读取配置文件: - Factory factory = new IniSecurityManagerFactory("classpath:shiro.ini"); - SecurityManager securityManager = factory.getInstance(); - - // for this simple example quickstart, make the SecurityManager - // accessible as a JVM singleton. Most applications wouldn't do this - // and instead rely on their container configuration or web.xml for - // webapps. That is outside the scope of this simple quickstart, so - // we'll just do the bare minimum so you can continue to get a feel - // for things. - SecurityUtils.setSecurityManager(securityManager); - - // Now that a simple Shiro environment is set up, let's see what you can do: - - // get the currently executing user: - // 获取当前的用户对象 Subject - Subject currentUser = SecurityUtils.getSubject(); - - // Do some stuff with a Session (no need for a web or EJB container!!!) - //通过当前用户拿到Shiro的Session 可以脱离web存值取值 - Session session = currentUser.getSession(); - session.setAttribute("someKey", "aValue"); - String value = (String) session.getAttribute("someKey"); - if (value.equals("aValue")) { - log.info("Retrieved the correct value! [" + value + "]"); - } - - // let's login the current user so we can check against roles and permissions: - //判断当前的用户是否被认证 - if (!currentUser.isAuthenticated()) { - //Token 令牌 - UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa"); - //设置记住我 - token.setRememberMe(true); - try { - //执行登录操作 - currentUser.login(token); - } catch (UnknownAccountException uae) { - log.info("There is no user with username of " + token.getPrincipal()); - } catch (IncorrectCredentialsException ice) { - log.info("Password for account " + token.getPrincipal() + " was incorrect!"); - } catch (LockedAccountException lae) { - log.info("The account for username " + token.getPrincipal() + " is locked. " + - "Please contact your administrator to unlock it."); - } - // ... catch more exceptions here (maybe custom ones specific to your application? - catch (AuthenticationException ae) { - //unexpected condition? error? - } - } - - //say who they are: - //print their identifying principal (in this case, a username): - log.info("User [" + currentUser.getPrincipal() + "] logged in successfully."); - - //test a role: - // 检查角色 - if (currentUser.hasRole("schwartz")) { - log.info("May the Schwartz be with you!"); - } else { - log.info("Hello, mere mortal."); - } - - //test a typed permission (not instance-level) - //粗粒度 - if (currentUser.isPermitted("lightsaber:wield")) { - log.info("You may use a lightsaber ring. Use it wisely."); - } else { - log.info("Sorry, lightsaber rings are for schwartz masters only."); - } - - //a (very powerful) Instance Level permission: - //细粒度 - if (currentUser.isPermitted("winnebago:drive:eagle5")) { - log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " + - "Here are the keys - have fun!"); - } else { - log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!"); - } - - //all done - log out! - //注销 - currentUser.logout(); - - //结束 - System.exit(0); - } - } - ``` - - [![image-20200729130649625](https://camo.githubusercontent.com/3e9d7c61a23719bee5050d36fe1b7b78b367c725c78cceb7e3257223cec104d8/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303732393133303634393632352e706e67)](https://camo.githubusercontent.com/3e9d7c61a23719bee5050d36fe1b7b78b367c725c78cceb7e3257223cec104d8/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303732393133303634393632352e706e67) - - * Spring Secutrry都有~(只是换了个名字) - - ``` - // 获取当前的用户对象 Subject - Subject currentUser = SecurityUtils.getSubject(); - Session session = currentUser.getSession(); - currentUser.isAuthenticated() - currentUser.getPrincipal() - currentUser.hasRole("schwartz") - currentUser.isPermitted("lightsaber:wield") - currentUser.logout(); - ``` - -# 3、SpringBoot集成 - -## 3.1、SpringBoot整合Shiro环境搭建 - -1. 新建一个项目或模块,勾选依赖 - - [![image-20200729174715011](https://camo.githubusercontent.com/98ca792576cb060f2f278b236c3887867ea62c4c8fbff32d7fe51db60841b562/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303732393137343731353031312e706e67)](https://camo.githubusercontent.com/98ca792576cb060f2f278b236c3887867ea62c4c8fbff32d7fe51db60841b562/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303732393137343731353031312e706e67) - - pom.xml - - ``` - - - - org.springframework.boot - spring-boot-starter-thymeleaf - - - - org.springframework.boot - spring-boot-starter-web - - - - org.springframework.boot - spring-boot-starter-test - test - - - org.junit.vintage - junit-vintage-engine - - - - - ``` - -2. 测试环境是否正常 - - * 新建一个controller页面 - - ``` - @Controller - public class MyController { - - @RequestMapping({"/","/index"}) - public String toIndex(Model model) { - model.addAttribute("msg","hello,Shiro"); - return "index"; - } - - @RequestMapping("/user/add") - public String add() { - return "user/add"; - } - - @RequestMapping("/user/update") - public String update() { - return "user/update"; - } - } - ``` - - * 新建一个index.html页面 - - ``` - - - - - 首页 - - -
-

首页

-

- -
- add | update -
- - - ``` - - * 新建一个add.html页面 - - ``` - - - - - Title - - -

add

- - - ``` - - * 新建一个update.html页面 - - ``` - - - - - Title - - -

update

- - - ``` - - * 项目结构 - - [![image-20200729190325307](https://camo.githubusercontent.com/8d47214d44b9e5d644acf03a1a8ee7f14bcf49be620a6124baab3e84371e36b9/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303732393139303332353330372e706e67)](https://camo.githubusercontent.com/8d47214d44b9e5d644acf03a1a8ee7f14bcf49be620a6124baab3e84371e36b9/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303732393139303332353330372e706e67) - - * 运行截图 - - [![image-20200729190548307](https://camo.githubusercontent.com/0690397defb63342a011bbe18b61a2b53786dc731053d47db48fb5a97e3d95fe/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303732393139303534383330372e706e67)](https://camo.githubusercontent.com/0690397defb63342a011bbe18b61a2b53786dc731053d47db48fb5a97e3d95fe/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303732393139303534383330372e706e67) - -3. 导入shiro整合spring的包——[官网](https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring),查看最新版本 - - ``` - - - - - org.apache.shiro - shiro-spring - 1.5.3 - - ``` - -4. 编写导入配置类 - - * 编写一个自定义类UserRealm - - ``` - //自定义的UserRealm - public class UserRealm extends AuthorizingRealm { - //授权 - @Override - protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { - System.out.println("执行了=>授权doGetAuthorizationInfo"); - return null; - } - - //认证 - @Override - protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { - System.out.println("执行了=>认证doGetAuthorizationInfo"); - return null; - } - } - ``` - - * 编写配置ShiroConfig - - * 创建realm对象,需要自定义类 - * DefaultWebSecurityManager - * ShiroFilterFactoryBean - - ``` - @Configuration - public class ShiroConfig { - - //3. shiroFilterFactoryBean - - @Bean - public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) { - ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); - // 设置安全管理器 - bean.setSecurityManager(defaultWebSecurityManager); - - return bean; - } - - //2. DefaultWebSecurityManager - - @Bean - public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) { - DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); - - // 关联userRealm - securityManager.setRealm(userRealm); - return securityManager; - } - //1. 创建realm对象,需要自定义类 - - @Bean - public UserRealm userRealm() { - return new UserRealm(); - } - } - ``` - -## 3.2、Shiro实现登录拦截 - -* 在`ShiroConfig`中的`getShiroFilterFactoryBean`方法中添加如下配置 - - * anon: 无需认证就可以访问 - * authc: 必须认证了才能访问 - * user: 必须拥有记住我功能才能用 - * perms: 拥有对某个资源的权限才能访问 - * role: 拥有某个角色权限 - - ``` - Map filterMap = new LinkedHashMap<>(); - filterMap.put("/user/add","authc"); - filterMap.put("/user/update","authc"); - bean.setFilterChainDefinitionMap(filterMap); - ``` - -* 点击首页的add或者update之后 - - [![image-20200729191619576](https://camo.githubusercontent.com/d148dd9cf41446cddf6f2f595b498350d7fbd40bd59407f39e99353d2fdefdd8/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303732393139313631393537362e706e67)](https://camo.githubusercontent.com/d148dd9cf41446cddf6f2f595b498350d7fbd40bd59407f39e99353d2fdefdd8/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303732393139313631393537362e706e67) - -* 添加拦截成功页面 - - * 登录页面login.html - - ``` - - - - - 登录页面 - - -

登录

-
- -
-

用户名:

-

密码:

-

密码:

-
- - - ``` - - * 在MyConfig中添加 - - ``` - @RequestMapping("/toLogin") - public String toLogin() { - return "login"; - } - ``` - - * 在`ShiroConfig`中的`getShiroFilterFactoryBean`方法中添加如下配置 - - ``` - //设置登录的请求 - bean.setLoginUrl("/toLogin"); - ``` - -* 拦截成功页面 - - [![image-20200729192409085](https://camo.githubusercontent.com/c30215e3b1528739cffad8d0497103cb9bac7220047ea6aabf7a5cf3ccfed6c6/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303732393139323430393038352e706e67)](https://camo.githubusercontent.com/c30215e3b1528739cffad8d0497103cb9bac7220047ea6aabf7a5cf3ccfed6c6/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303732393139323430393038352e706e67) - -## 3.3、Shiro实现用户认证 - -1. 在`MyController`中编写用户提交表单之后处理 - - ``` - @RequestMapping("/login") - public String login(String username, String password, Model model) { - //获取一个用户 - Subject subject = SecurityUtils.getSubject(); - // 封装用户的登录数据 - UsernamePasswordToken token = new UsernamePasswordToken(username, password); - - try { - subject.login(token);//执行登录的方法,如果没有异常就说明ok了 - return "index"; - } catch (UnknownAccountException e) {//用户名不存在 - model.addAttribute("msg","用户名错误"); - return "login"; - } catch (IncorrectCredentialsException e) {//密码不存在 - model.addAttribute("msg","密码错误"); - return "login"; - } - - } - ``` - -2. login.html的修改 - - ``` - - - - - 登录页面 - - -

登录

-
- -

-
-

用户名:

-

密码:

-

密码:

-
- - - ``` - -3. 用户输入登录信息 - - * 页面 - - [![image-20200729220647520](https://camo.githubusercontent.com/f18382061e7338a2ff3d1646e4d43f604d506897f62ed318b466f910a04fb2bc/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303732393232303634373532302e706e67)](https://camo.githubusercontent.com/f18382061e7338a2ff3d1646e4d43f604d506897f62ed318b466f910a04fb2bc/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303732393232303634373532302e706e67) - - * 控制台 - - [![image-20200729220926500](https://camo.githubusercontent.com/490b413c12125467e4f8916eea52ca4f8b1e1534217a97cc95ee336119aff81f/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303732393232303932363530302e706e67)](https://camo.githubusercontent.com/490b413c12125467e4f8916eea52ca4f8b1e1534217a97cc95ee336119aff81f/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303732393232303932363530302e706e67) - -4. 用户认证编写`UserRealm`中的认证(doGetAuthenticationInfo) - - ``` - //认证 - @Override - protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { - System.out.println("执行了=>认证doGetAuthorizationInfo"); - // 用户名、密码, 数据中取 - String name = "root"; - String password = "123456"; - - UsernamePasswordToken userToken = (UsernamePasswordToken) token; - - if (!userToken.getUsername().equals(name)) { - return null;//抛出异常 UnknownAccountException - } - - // 密码认证,shiro做 - return new SimpleAuthenticationInfo("",password,""); - } - ``` - -## 3.4、Shiro整合Mybatis - -1. 导入依赖 - - ``` - - org.projectlombok - lombok - - - mysql - mysql-connector-java - - - - log4j - log4j - 1.2.17 - - - - com.alibaba - druid - 1.1.23 - - - - - org.mybatis.spring.boot - mybatis-spring-boot-starter - 2.1.3 - - ``` - -2. 配置文件application.yml的编写 - - ``` - spring: - datasource: - username: root - password: admin - #?serverTimezone=UTC解决时区的报错 - url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8 - driver-class-name: com.mysql.cj.jdbc.Driver - type: com.alibaba.druid.pool.DruidDataSource - - #Spring Boot 默认是不注入这些属性值的,需要自己绑定 - #druid 数据源专有配置 - initialSize: 5 - minIdle: 5 - maxActive: 20 - maxWait: 60000 - timeBetweenEvictionRunsMillis: 60000 - minEvictableIdleTimeMillis: 300000 - validationQuery: SELECT 1 FROM DUAL - testWhileIdle: true - testOnBorrow: false - testOnReturn: false - poolPreparedStatements: true - - #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入 - #如果允许时报错 java.lang.ClassNotFoundException: org.apache.log4j.Priority - #则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j - filters: stat,wall,log4j - maxPoolPreparedStatementPerConnectionSize: 20 - useGlobalDataSourceStat: true - connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500 - - mybatis: - type-aliases-package: nuc.ss.pojo - mapper-locations: classpath:mapper/*.xml - ``` - -3. User类的编写 - - ``` - @Data - @AllArgsConstructor - @NoArgsConstructor - public class User { - private int id; - private String name; - private String pwd; - } - ``` - -4. UserMapper.xml映射 - - ``` - - - - - - - - - - - insert into mybatis.user (id, name, pwd) values (#{id},#{name},#{pwd}); - - - - update mybatis.user set name=#{name},pwd = #{pwd} where id = #{id}; - - - - delete from mybatis.user where id = #{id} - - - ``` - -5. UserService接口实现 - - ``` - public interface UserService { - - public User queryUserByName(String name); - } - ``` - -6. UserServiceImpl业务逻辑 - - ``` - @Service - public class UserServiceImpl implements UserService { - - @Autowired - UserMapper userMapper; - @Override - public User queryUserByName(String name) { - return userMapper.queryUserByName(name); - } - } - ``` - -7. 测试环境 - - ``` - @SpringBootTest - class ShiroSpringbootApplicationTests { - - @Autowired - UserService userService; - @Test - void contextLoads() { - System.out.println(userService.queryUserByName("狂神")); - } - - } - ``` - - [![image-20200730121720922](https://camo.githubusercontent.com/308c5e1c04ff9875ffcc4e2179d20eb918ce323646e2f6208a0d849aae6a262b/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303733303132313732303932322e706e67)](https://camo.githubusercontent.com/308c5e1c04ff9875ffcc4e2179d20eb918ce323646e2f6208a0d849aae6a262b/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303733303132313732303932322e706e67) - -8. `UserRealm`连接真实数据库 - - ``` - //认证 - @Override - protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { - System.out.println("执行了=>认证doGetAuthorizationInfo"); - - UsernamePasswordToken userToken = (UsernamePasswordToken) token; - - // 真实数据库 用户名、密码, 数据中取 - User user = userService.queryUserByName(userToken.getUsername()); - - if (user == null) {//没有这个人 - return null; - } - - // 密码认证,shiro做 - return new SimpleAuthenticationInfo("",user.getPwd(),""); - } - ``` - - [![image-20200730180019861](https://camo.githubusercontent.com/c9af4c0276c84116535b93571ccafcaa1b84dd7ffeb1eb9ecd885c31d5738144/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303733303138303031393836312e706e67)](https://camo.githubusercontent.com/c9af4c0276c84116535b93571ccafcaa1b84dd7ffeb1eb9ecd885c31d5738144/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303733303138303031393836312e706e67) - -9. 断点测试密码加密类型 - - * 打断点Debug - - [![image-20200730182621912](https://camo.githubusercontent.com/fff18c314ac225e18601b0ab9d48e3d87da840cbd7a919ce32e66d8966b96b11/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303733303138323632313931322e706e67)](https://camo.githubusercontent.com/fff18c314ac225e18601b0ab9d48e3d87da840cbd7a919ce32e66d8966b96b11/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303733303138323632313931322e706e67) - - * 默认是`SimpleCredentialsMatcher`加密 - - [![image-20200730181814293](https://camo.githubusercontent.com/1213c4a155a6cbf83c20593b4334363db71cc351482d4d87ae86f2e231ba204c/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303733303138313831343239332e706e67)](https://camo.githubusercontent.com/1213c4a155a6cbf83c20593b4334363db71cc351482d4d87ae86f2e231ba204c/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303733303138313831343239332e706e67) - - * MD5加密——[测试](http://tool.chinaz.com/tools/md5.aspx) - - 123456——E10ADC3949BA59ABBE56E057F20F883E - - * MD5盐值加密 - - * 所有加密 - - [![image-20200730181944253](https://camo.githubusercontent.com/8cf8498ea81bdcff2fb9917cfdd05525c2e65814464c181b5e636d1e9c1e24a1/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303733303138313934343235332e706e67)](https://camo.githubusercontent.com/8cf8498ea81bdcff2fb9917cfdd05525c2e65814464c181b5e636d1e9c1e24a1/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303733303138313934343235332e706e67) - -## 3.5、Shiro实现用户授权 - -1. `ShiroConfig`中的`getShiroFilterFactoryBean`方法添加认证代码 - - ``` - //授权,正常情况下,没有授权会跳转到为授权页面 - filterMap.put("/user/add","perms[user:add]"); - filterMap.put("/user/update","perms[user:update]"); - ``` - -2. 登录之后点击add按钮会弹出如下页面 - - [![image-20200730195133631](https://camo.githubusercontent.com/81387d3d3a6a48219f33ce3f53d6880f65cd73c05324208568f22b61c5d2f111/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303733303139353133333633312e706e67)](https://camo.githubusercontent.com/81387d3d3a6a48219f33ce3f53d6880f65cd73c05324208568f22b61c5d2f111/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303733303139353133333633312e706e67) - -3. 添加为授权页面 - - * MyController - - ``` - @RequestMapping("/noauto") - @ResponseBody - public String unauthorized() { - return "未经授权,无法访问此页面"; - } - ``` - - * `ShiroConfig`中的`getShiroFilterFactoryBean`方法中添加 - - ``` - //为授权页面 - bean.setUnauthorizedUrl("/noauto"); - ``` - -4. 再次测试 - - [![image-20200730195807437](https://camo.githubusercontent.com/416afcb2900fd3fdd2564dfb3cdff907d7b67a112ef0ab2c1889d1886fc1b42b/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303733303139353830373433372e706e67)](https://camo.githubusercontent.com/416afcb2900fd3fdd2564dfb3cdff907d7b67a112ef0ab2c1889d1886fc1b42b/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303733303139353830373433372e706e67) - - [![image-20200730195946692](https://camo.githubusercontent.com/7be97fcb647f93b4eee73a38f2a54888a5f95953f429981bbdbcfc142e473df6/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303733303139353934363639322e706e67)](https://camo.githubusercontent.com/7be97fcb647f93b4eee73a38f2a54888a5f95953f429981bbdbcfc142e473df6/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303733303139353934363639322e706e67) - - 所以需要在UserRealm中为用户进行真正授权 - -5. UserRealm类的修改 - - ``` - //自定义的UserRealm - public class UserRealm extends AuthorizingRealm { - - @Autowired - UserService userService; - //授权 - @Override - protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { - System.out.println("执行了=>授权doGetAuthorizationInfo"); - - SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); - - //拿到当前登录的这个对象 - Subject subject = SecurityUtils.getSubject(); - User currentUser = (User)subject.getPrincipal();//拿到user对象 - - //设置当前用户的权限 - info.addStringPermission(currentUser.getPerms()); - - return info; - } - - //认证 - @Override - protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { - ...... - // 密码认证,shiro做 - return new SimpleAuthenticationInfo(user,user.getPwd(),""); - } - } - ``` - -6. 再次测试 - - [![image-20200730202810034](https://camo.githubusercontent.com/31ac9e7acaee627de7b5b29aa307991405d5a80d7ed15a291955b34ddd6cc955/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303733303230323831303033342e706e67)](https://camo.githubusercontent.com/31ac9e7acaee627de7b5b29aa307991405d5a80d7ed15a291955b34ddd6cc955/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303733303230323831303033342e706e67) - -## 3.6、Shiro整合Thymeleaf - -1. shiro-thymeleaf整合包导入——[官网](https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro) - - ``` - - - com.github.theborakompanioni - thymeleaf-extras-shiro - 2.0.0 - - ``` - -2. 在ShiroConfig中整合ShiroDialect - - ``` - // 整合ShiroDialect: 用来整合 Shiro thymeleaf - @Bean - public ShiroDialect getShiroDialect() { - return new ShiroDialect(); - } - ``` - -3. index.html页面 - - ``` - - - - - 首页 - - - -
-

首页

-

- - - - -
- 登录 -
- -
- -
- add -
- -
- update -
- -
- - - ``` - -4. 页面显示 - - [![image-20200730205736153](https://camo.githubusercontent.com/802d55dc3d0600bf012f27959d4d743aaf19bbdead74f083c9797d14e1b57c58/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303733303230353733363135332e706e67)](https://camo.githubusercontent.com/802d55dc3d0600bf012f27959d4d743aaf19bbdead74f083c9797d14e1b57c58/68747470733a2f2f67697465652e636f6d2f6c7a685f67697465652f737072696e67626f6f745f696d6167652f7261772f6d61737465722f696d672f696d6167652d32303230303733303230353733363135332e706e67) - -## 3.7、所有代码 - -* ShiroConfig - - ``` - package nuc.ss.config; - - import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; - import org.apache.shiro.spring.web.ShiroFilterFactoryBean; - import org.apache.shiro.web.mgt.DefaultWebSecurityManager; - import org.springframework.beans.factory.annotation.Qualifier; - import org.springframework.context.annotation.Bean; - import org.springframework.context.annotation.Configuration; - - import java.util.LinkedHashMap; - import java.util.Map; - - @Configuration - public class ShiroConfig { - - //shiroFilterFactoryBean - - @Bean - public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) { - ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); - // 设置安全管理器 - bean.setSecurityManager(defaultWebSecurityManager); - - // 添加shiro的内置过滤器 - /* - anon: 无需认证就可以访问 - authc: 必须认证了才能访问 - user: 必须拥有记住我功能才能用 - perms: 拥有对某个资源的权限才能访问 - role: 拥有某个角色权限 - */ - - //拦截 - Map filterMap = new LinkedHashMap<>(); - //filterMap.put("/user/add","authc"); - //filterMap.put("/user/update","authc"); - - - //授权,正常情况下,没有授权会跳转到为授权页面 - filterMap.put("/user/add","perms[user:add]"); - filterMap.put("/user/update","perms[user:update]"); - - filterMap.put("/user/*","authc"); - - bean.setFilterChainDefinitionMap(filterMap); - - //设置登录的请求 - bean.setLoginUrl("/toLogin"); - - //为授权页面 - bean.setUnauthorizedUrl("/noauto"); - - return bean; - } - - //DefaultWebSecurityManager - - @Bean - public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) { - DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); - - // 关联userRealm - securityManager.setRealm(userRealm); - return securityManager; - } - //创建realm对象,需要自定义类 - - @Bean - public UserRealm userRealm() { - return new UserRealm(); - } - - // 整合ShiroDialect: 用来整合 Shiro thymeleaf - @Bean - public ShiroDialect getShiroDialect() { - return new ShiroDialect(); - } - } - ``` - -* UserRealm - - ``` - package nuc.ss.config; - - import nuc.ss.pojo.User; - import nuc.ss.service.UserService; - import org.apache.shiro.SecurityUtils; - import org.apache.shiro.authc.*; - import org.apache.shiro.authz.AuthorizationInfo; - import org.apache.shiro.authz.SimpleAuthorizationInfo; - import org.apache.shiro.realm.AuthorizingRealm; - import org.apache.shiro.session.Session; - import org.apache.shiro.subject.PrincipalCollection; - import org.apache.shiro.subject.Subject; - import org.springframework.beans.factory.annotation.Autowired; - - //自定义的UserRealm - public class UserRealm extends AuthorizingRealm { - - @Autowired - UserService userService; - //授权 - @Override - protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { - System.out.println("执行了=>授权doGetAuthorizationInfo"); - - SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); - - //info.addStringPermission("user:add"); - - //拿到当前登录的这个对象 - Subject subject = SecurityUtils.getSubject(); - User currentUser = (User)subject.getPrincipal();//拿到user对象 - - //设置当前用户的权限 - info.addStringPermission(currentUser.getPerms()); - - return info; - } - - //认证 - @Override - protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { - System.out.println("执行了=>认证doGetAuthorizationInfo"); - - UsernamePasswordToken userToken = (UsernamePasswordToken) token; - - // 虚拟用户 - //String name = "root"; - //String password = "123456"; - //if (!userToken.getUsername().equals(name)) { - // return null;//抛出异常 UnknownAccountException - //} - - // 真实数据库 用户名、密码, 数据中取 - User user = userService.queryUserByName(userToken.getUsername()); - - if (user == null) {//没有这个人 - return null; - } - - //首页 - //Subject currentSubject = SecurityUtils.getSubject(); - //Session session = currentSubject.getSession(); - //session.setAttribute("loginUser",user); - - - // 密码认证,shiro做 - return new SimpleAuthenticationInfo(user,user.getPwd(),""); - } - } - ``` - -* MyController - - ``` - package nuc.ss.controller; - - import org.apache.shiro.SecurityUtils; - import org.apache.shiro.authc.IncorrectCredentialsException; - import org.apache.shiro.authc.UnknownAccountException; - import org.apache.shiro.authc.UsernamePasswordToken; - import org.apache.shiro.subject.Subject; - import org.springframework.stereotype.Controller; - import org.springframework.ui.Model; - import org.springframework.web.bind.annotation.RequestMapping; - import org.springframework.web.bind.annotation.ResponseBody; - - @Controller - public class MyController { - - @RequestMapping({"/","/index"}) - public String toIndex(Model model) { - model.addAttribute("msg","hello,Shiro"); - return "index"; - } - - @RequestMapping("/user/add") - public String add() { - return "user/add"; - } - - @RequestMapping("/user/update") - public String update() { - return "user/update"; - } - - @RequestMapping("/toLogin") - public String toLogin() { - return "login"; - } - - @RequestMapping("/login") - public String login(String username, String password, Model model) { - //获取一个用户 - Subject subject = SecurityUtils.getSubject(); - // 封装用户的登录数据 - UsernamePasswordToken token = new UsernamePasswordToken(username, password); - - try { - subject.login(token);//执行登录的方法,如果没有异常就说明ok了 - return "index"; - } catch (UnknownAccountException e) {//用户名不存在 - model.addAttribute("msg","用户名错误"); - return "login"; - } catch (IncorrectCredentialsException e) {//密码不存在 - model.addAttribute("msg","密码错误"); - return "login"; - } - } - - @RequestMapping("/noauto") - @ResponseBody - public String unauthorized() { - return "未经授权,无法访问此页面"; - } - } - ``` - -* pom依赖 - - ``` - - - 4.0.0 - nuc.ss - shiro-springboot - 0.0.1-SNAPSHOT - shiro-springboot - Demo project for Spring Boot - - - 1.8 - UTF-8 - UTF-8 - 2.3.0.RELEASE - - - - - - - - - - com.github.theborakompanioni - thymeleaf-extras-shiro - 2.0.0 - - - - org.projectlombok - lombok - - - mysql - mysql-connector-java - - - - log4j - log4j - 1.2.17 - - - - com.alibaba - druid - 1.1.23 - - - - - org.mybatis.spring.boot - mybatis-spring-boot-starter - 2.1.3 - - - - - org.apache.shiro - shiro-spring - 1.5.3 - - - - org.springframework.boot - spring-boot-starter-thymeleaf - - - - org.springframework.boot - spring-boot-starter-web - - - - org.springframework.boot - spring-boot-starter-test - test - - - org.junit.vintage - junit-vintage-engine - - - - - - - - - org.springframework.boot - spring-boot-dependencies - ${spring-boot.version} - pom - import - - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - 1.8 - 1.8 - UTF-8 - - - - org.springframework.boot - spring-boot-maven-plugin - - - - - - ``` - - - -# 4、完美的解释 - -[让 Apache Shiro 保护你的应用](https://www.infoq.cn/article/apache-shiro/?itm_source=infoq_en&itm_medium=link_on_en_item&itm_campaign=item_in_other_langs) \ No newline at end of file diff --git a/source/_posts/Java/Spring5.md b/source/_posts/Java/Spring5.md deleted file mode 100644 index ca00241..0000000 --- a/source/_posts/Java/Spring5.md +++ /dev/null @@ -1,1844 +0,0 @@ ---- -title: Spring5 -author: TianZD -top: true -cover: true -toc: true -mathjax: false -summary: SSM中的Spring5框架学习笔记,粗略学了一下,没有参考价值 -tags: - - Spring - - java - - 学习笔记 -categories: - - java -reprintPolicy: cc_by -abbrlink: 7f09f5da -date: 2022-04-29 11:06:51 -coverImg: -img: -password: ---- - - - -[toc] - -# Spring - -# 1、概述 - -> 概述 - -Spring框架是一个开放源代码的J2EE应用程序框架,由Rod Johnson发起,**是针对bean的生命周期进行管理的轻量级容器**(lightweight container)。 Spring解决了开发者在J2EE开发中遇到的许多常见的问题,提供了功能强大**IOC、AOP及Web MVC**等功能。Spring可以单独应用于构筑应用程序,也可以和Struts、Webwork、Tapestry等众多Web框架组合使用,并且可以与 Swing等桌面应用程序AP组合。因此, Spring不仅仅能应用于J2EE应用程序之中,也可以应用于桌面应用程序以及小应用程序之中。 - -> 理念 - -使现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架 - -> SSH和SSM - -SSH:Struct2+Spring+Hibernate - -SSM:SpringMVC+Spring+Mybatis - -> 优点 - -* **开放源**代码的**免费**的J2EE应用程序框架(容器) -* **轻量级、非入侵式**的框架 -* 是针对bean的生命周期进行管理的**轻量级**容器 -* 提供了功能强大**IOC、AOP及Web MVC**等功能 -* **支持事务的处理** -* Spring可以单独应用于构筑应用程序,也可以和Struts、Webwork、Tapestry等众多Web框架**组合使用** - -**总结**:Spring就是一个**轻量级**的控制反转**IOC**和面向切面编程**AOP**的框架 - -> 缺点 - -* 配置十分繁琐,人称“配置地狱” - -> 组成 - -![查看源图像](https://gitee.com/tianzhendong/img/raw/master//images/403a0003482fc287bac5) - -Spring框架主要由七部分组成,分别是 Spring Core、 Spring AOP、 Spring ORM、 Spring DAO、Spring Context、 Spring Web和 Spring Web MVC。 - -> 扩展 - -现代化的java开发,就是基于spring的开发 - -![image-20210808065217942](https://gitee.com/tianzhendong/img/raw/master//images/image-20210808065217942.png) - -* Spring Boot - -快速开发的脚手架,基于springboot可以快速开发单个微服务 - -约定大于配置 - -* Spring Cloud - -基于Spring Boot实现 - -现在大多数公司都在使用springboot进行快速开发 - -学习springboot需要完全掌握spring 和springmvc - -# 2、IOC理论推导 - -## 原来方式 - -> 基础代码 - -1. 编写Dao层UserDao接口 - -```java -public interface UserDao { - void getUser(); -} -``` - -2. 编写Dao层UserDaoImpl实现类和UserDaoMysqlImpl实现类 - -```java -public class UserDaoImpl implements UserDao { - @Override - public void getUser() { - System.out.println("默认获取用户数据"); - } -} -``` - -```java -public class UserDaoMysqlImpl implements UserDao{ - @Override - public void getUser() { - System.out.println("Mysql获取用户数据"); - } -} -``` - -3. 编写Service层UserService接口 - -```java -public interface UserService { - void getUser(); -} -``` - -4. 编写Service层UserServiceImpl实现类 - -```java -public class UserServiceImpl implements UserService { - //创建dao层对应接口的实现类对象,从而调用dao层的方法 - //弊端:新增或者改变需求时,dao层实现类增加或改变,需要更改这里的代码 - private UserDao userDao = new UserDaoImpl(); - @Override - public void getUser() { - //调用dao层对应方法 - userDao.getUser(); - } -} -``` - -5. 编写用户代码MyTest类 - -```java -public class MyTest { - public static void main(String[] args) { - //用户实际调用的是业务层,不接触dao层 - UserService userService = new UserServiceImpl(); - userService.getUser(); - } -} -``` - -> 分析 - -用户实际调用的是业务层,不接触dao层,通过创建`userservice`接口引用指向一个`UserServiceImpl`实现类对象,调用`UserServiceImpl`中的`getUser()`方法。由于`UserServiceImpl`中创建dao层对应接口的实现类对象,从而调用dao层的`getUser()`方法 - -**弊端**: - -在我们之前的业务中,用户的需求可能会影响我们原来的代码,需要根据用户的需求去修改源代码,如果程序代码量大,修改的成本十分昂贵 - -* 新增或者改变需求时,dao层实现类增加或改变,需要更改这里的代码,比如要调用`UserDaoMysqlImpl`中的方法,需要改变UserServiceImpl中的代码 - - - -## 改进-IOC原型 - -> 代码 - -1. 改变Service层UserServiceImpl实现类 - -```java -public class UserServiceImpl implements UserService { - private UserDao userDao; - //通过set进行动态实现值的注入 - public void setUserDao(UserDao userDao) { - this.userDao = userDao; - } - @Override - public void getUser() { - //调用dao层对应方法 - userDao.getUser(); - } -} -``` - -2. 改变用户代码MyTest类 - -```java -public class MyTest { - public static void main(String[] args) { - //用户实际调用的是业务层,不接触dao层 - UserService userService = new UserServiceImpl(); - // 实现UserDaoImpl类中的方法 - ((UserServiceImpl) userService).setUserDao(new UserDaoImpl()); - userService.getUser(); - //实现UserDaoMysqlImpl方法 - ((UserServiceImpl) userService).setUserDao(new UserDaoMysqlImpl()); - userService.getUser(); - } -} -``` - -> 分析 - -在新增或者改变需求时,只需要更改MyTest类中的代码 - -使用一个set接口实现,实现了革命性的变化 - -```java - private UserDao userDao; - //通过set进行动态实现值的注入 - public void setUserDao(UserDao userDao) { - this.userDao = userDao; - } -``` - -* 之前程序是主动创建对象,控制权在程序员手上,用户访问业务层,由程序员代码决定用户调用什么 -* 使用**set注入**后,程序不具有主动性,通过把**接口给用户**,用户访问业务层,主动权在用户,由用户选择调用什么,程序变成了**被动接收对象**(来自用户) - -**这种思想即控制反转IOC的原型,从本质上解决了问题,程序员不需要再管理对象的创建了,系统的耦合性大大降低,可以更加专注在业务的实现上** - -## IOC本质 - -**控制反转是一种通过描述(XML或者注解)并通过第三方去生产或获取特定对象的方式,在Spring中实现控制反转的是IOC容器,实现方式是依赖注入** - -* 控制反转IOC(inversion of control)是一种设计思想,DI(依赖注入)是实现IOC的一种方式。 - -* 在没有IOC的程序中,使用面向对象编程,对象的创建和对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制 - -* 控制反转后,对象的创建转移给了第三方 -* 控制反转就是获得依赖对象的方式反转了 - -IOC是Spring框架的核心内容,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IOC。采用XML方式配置Bean的时候,Bean的的定义信息和实现是分离的,采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的 - -**Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建于组织对象存入容器中,程序使用时再从IOC容器中取出需要的对象** - -# 3、HelloSpring - -## 代码 - -> 创建实体类Hello.class - -该类为后续要生成的对象的类,**必须要有set方法(依赖注入要用)** - -```java -public class Hello { - private String str; - - public String getStr() { - return str; - } - - public void setStr(String str) { - this.str = str; - } - - public Hello() { - } - - public Hello(String str) { - this.str = str; - } - - public void helloSpring() { - System.out.println("hello " + str); - } -} -``` - -> Spring配置文件Beans.xml - -```xml - - - - - - - - - - -``` - -> 业务代码MyTest.class - -```java -import com.tian.pojo.Hello; -import org.springframework.context.ApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; - -public class MyTest { - public static void main(String[] args) { - //获取Spring的上下文对象,固定语句 - ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml"); - //对象都在Spring中进行管理,要使用的化直接去里面取 - Hello hello = (Hello) context.getBean("hello"); - hello.helloSpring(); - } -} -``` - -## 分析 - -对象由spring进行创建,对象的属性由Spring容器设置,这个过程就叫控制反转IOC,一句话说就是对象由Spring来创建、管理、装配 - -* **控制**:传统应用程序的对象由程序本身控制创建,使用SPring后,对象由Spring进行创建 - -* **反转**:程序本身不创建对象,变为被动的接收对象 - -* **依赖注入**:利用set方法进行注入,对象必须要有set方法 - - - -# 4、IOC创建对象的方式 - -在xml中使用<**bean**>后,不管有没有使用,都会创建 - -> 使用无参构造创建对象,**默认** - -必须有无参构造函数 - -```xml - - - -``` - - - -> 有参构造函数——参数名 - -**重点** - -```xml - - - -``` - - - -> 通过有参构造函数——参数下标 - -```xml - - - -``` - -> 有参构造函数——参数类型 - -不建议使用:**当类中有两个或以上属性类型一样时,会导致错误** - -```xml - - - -``` - -> 总结 - -**在配置文件加载的时候,容器中管理的对象就已经初始化了** - - - -# 5、Spring配置 - -> 别名alias - -起一个小名,两个名字hello和hello2都能用来创建对象 - -```xml - -``` - -> Bean的配置 - -```xml - - - - -``` - -> import - -一般用于多个团队开发,可以将多个配置文件导入合成一个 - -项目中有多个人开发,三个人负责不同的类的开发,不同的类需要注册在不同的bean中,可以利用import将所有的bean.xml合成一个总的。 - -```xml - -``` - - - -# 6、DI依赖注入 - -## 构造器注入 - -见`4、IOC创建对象的方式` - -## set方式注入【重点】 - -> 依赖注入:set注入 - -* 依赖:bean对象的创建依赖于容器 -* 注入:bean对象中的所有属性由容器注入 - -> 环境搭建 - -1. 复杂类型 - -```java -package com.tian.pojo; -public class Address { - private String address; -//get set tostring省略 -} -``` - -2. 真实类型 - -```java -public class Student { - private String name; - private Address address; - private String[] books; - private List hobbies; - private Map card; - private Set games; - private String wife; - private Properties info; - //省略了get 和set tostring -} -``` - -3. 配置文件 - -```xml - - - - -``` - - - -3. 测试代码 - -```java -public class MyTest { - public static void main(String[] args) { - ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); - Student student = (Student) context.getBean("student"); - System.out.println(student.getName()); - } -} -``` - -> 注入方式 - -* 普通值注入value - -* bean注入ref -* 数组注入 -* list -* map -* properties -* null - -```xml - - - - - - - - - - - - book1 - book2 - book3 - - - - - - 听歌 - 电影 - - - - - - - - - - - - - LOL - COD - - - - - - - - - - com.mysql.driver - ual - root - 123456 - - - -``` - -## 扩展C\P命名空间注入 - -这个的使用要在bean.xml文件的头目录里面加上两行语句 - -```xml -xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" - - - - - -``` - - - -# 7、Bean的作用域scope - -> 单例模式`scope="singleton"` - -默认就是单例,也可以手动设置 - -```xml - - - -``` - -> 原型模式`scope="prototype"` - -每次从容器中get的时候,都会产生一个新对象 - -```xml - - - -``` - -> 其余的request、session、application - -只能在web开发中使用 - -# 8、Bean的自动装配 - -自动装配:Spring会在上下文中自动寻找、装配属性 - -装配方式: - -* 在xml中显式的配置 -* 在java中显式的配置 -* 隐式的自动装配Bean【重要】 - -## 搭建环境 - -一个人有两个从宠物,dog和cat - -xml配置 - -```xml - - - - - - - -``` - -## byName、byType自动装配 - -> byName - -byname:会自动在容器上下文中查找和自己对象中set方法后面中的值对应的**bean id**,如果dog改成dog222,则不会成功 - -```xml - - - - - - -``` - -> byType - -byType:会自动在容器上下文中查找和自己对象属性**类型相同**的bean - -如果dog改成dog222 ,依然会成功,甚至注册dog时不需要id属性`` - -**注意,如果dog注册了多个对象,即同一个类型有两个对象,则bytype不可以使用** - -```xml - - - - - - -``` - -> 总结 - -* byname:需要保证所有bean的id唯一,并且bean需要和自动注入的属性的set方法的值一致 -* byType:需要保证所有bean的class唯一,并且bean需要和自动注入的属性类型一致 - -## 使用注解实现自动装配 - -Spring2.5开始支持注解 - -> 使用注解: - -* 导入约束:context约束`xmlns:context="http://www.springframework.org/schema/context"` -* 配置注解的支持:` ` - -```xml - - - - - - -``` - -* xml配置 - -```xml - - - -``` - -> @Autowired - -* 默认为**byType**方式 - -* 直接在属性上使用或者在set方法上使用 -* 可以不需要set方法 - -```java -public class Person { - @Autowired - private Cat cat; - @Autowired - private Dog dog; - private String name; -} -``` - -* 扩展:autowired有一个属性,required,默认为true,如果显式的定义了其为false,说明这个对象允许为null - -```java - @Autowired(required = false) -``` - -> @Qualifier - -Autowired默认为byType方式,如果IOC容器中同一个类型注册了多个对象,那么会出现问题,需要配置Qualifier使用,指定具体的对象 - -```xml - - -``` - - - -```java -public class Person { - @Autowired - @Qualifier(value = "cat1") - private Cat cat; -} -``` - -> @Resource - -Resource是java自带的注解 - -通过**byName方式** - -```java -public class Person { - //@Autowired - //@Qualifier(value = "cat1") - @Resource(name="cat1") - private Cat cat; -} -``` - -# 9、使用注解开发 - -## Bean - -1. 导包,必须有aop的包,`pop.xml` - -```xml - - - org.springframework - spring-webmvc - 5.3.9 - - -``` - -2. 导入context约束,增加注解支持,`applicationContext.xml` - -```xml - - - - - - - -``` - -3. 实体类@Component - -**在实体类上使用`@Component`注解等价于``,id默认为类的名字小写** - -```java -//等价于 -// id默认为类的名字小写 -@Component -public class User { - private String name; - //get set -} - -``` - -## 属性的注入@Value - -适用于简单的,复杂的还是通过配置文件 - -```java -//等价于 -// id默认为类的名字小写 -@Component -public class User { - @Value("tianzd") - private String name; - //get set -} -``` - -## 衍生的注解 - -@Component有几个衍生注解,在web开发中会按照MVC三层架构开发 - -* @Component 用于pojo层 -* @Service 用于service业务层 -* @Controller 用于Controller层 -* @Repository 用于Dao层 - -四个功能一样,都是代表将某个类注册到spring中,装配Bean - -## 自动装配注解 - -@Autowired - -@Resource - -## 作用域注解@Scope - -在某个类上标注,修改称单例或者多例模式 - -## 小结 - -> xml与注解 - -* xml更加万能,适用于任何场景,维护简单方便 - -* 注解不是自己的类使用不了,维护复杂 - -> 最佳实践 - -* xml用来管理Bean -* 注解只负责完成属性注入@Value - - - -# 10、使用java的方式配置Spring - -舍弃xml配置文件,使用config类实现 - -> 实体类 - -```java -package com.tian.pojo; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -//这个注解的意思就是说明这个类被注册到了容器中 -@Component -public class User { - private String name; - - public String getName() { - return name; - } - @Value("tianzhendong") - public void setName(String name) { - this.name = name; - } -} -``` - -> 配置类 - -```java -package com.tian.config; - -import com.tian.pojo.User; -import org.springframework.context.annotation.*; -//使其成为配置类,同beans.xml -//这个也会被spring容器托管,注册到容器中,因为他也是一个@component -@Configuration -@ComponentScan("com.tian") -//@Import(MyConfig1.class) 合并配置类,相当于xml中的import -public class MyConfig { - //注册一个bean,相当于一个bean标签 - //方法的名字相当于id属性 - //返回值类型,相当于class属性 - //返回值就是要注入到bean中的对象 - @Bean(name = "user1") - @Scope(value = "singleton") - public User getUser() { - return new User(); - } -} -``` - -> 测试类 - -```java -import com.tian.config.MyConfig; -import com.tian.pojo.User; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; - -public class MyTest { - public static void main(String[] args) { - ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class); - User user = context.getBean("user1", User.class); - System.out.println(user.getName()); - } -} -``` - -# 11、代理模式 - -**中介** - -> 为什么学习代理模式 - -![image-20210808200209343](https://gitee.com/tianzhendong/img/raw/master//images/image-20210808200209343.png) - -在业务增删改时,为了避免更改底层的代码 - -代理模式是SpringAop的底层 - -**SpringAOP的底层,面试重点** - -> 分类 - -* 静态代理 -* 动态代理 - - - -## 静态代理 - -租房:中介,房东,客户,出租房接口(中介和房东的) - -> 角色分析 - -* 抽象角色:一般会使用接口或者抽象类解决 -* 真实角色:被代理的角色,房东 -* 代理角色:代理真实角色,中介,代理真实角色后,一般会做一些附加操作 -* 客户:访问代理对象的角色 - -> 抽象接口 - -```java -package com.tian.demo1; - -public interface Rent { - public void rent(); -} -``` - -> 实现类 - -```java -package com.tian.demo1; -public class LandLord implements Rent{ - @Override - public void rent() { - System.out.println("房子租出去了"); - } -} -``` - -> 代理 - -```java -package com.tian.demo1; -public class Proxy implements Rent{ - private Rent landLord; - - public Proxy(LandLord landLord) { - this.landLord = landLord; - } - - public Proxy() { - } - - @Override - public void rent() { - landLord.rent(); - } -} -``` - -> 客户端 - -```java -package com.tian.demo1; -public class Client { - public static void main(String[] args) { - Proxy proxy = new Proxy(new LandLord()); - proxy.rent(); - } -} -``` - -> 优点 - -* 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务 -* 公共业务交给代理,实现了业务的分工 -* 公共业务发生扩展时,方便集中管理 - -> 缺点 - -* *代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。* -* *代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。* - -## 动态代理 - -每个代理类只能为一个接口服务,这样程序开发中必然会产生许多的代理类,为了弥补静态代理的缺点:一个真实角色就会产生一个代理,代码量翻倍,开发效率降低 - -> 概述 - -动态代理是在运行时,通过反射机制实现动态代理,并且能够代理各种类型的对象 - -* 角色和静态代理一样 -* 动态代理的代理类是动态生成的,不是直接写好的 - -**分类** - -* 基于接口的动态代理——JDK动态代理,这里使用这种 -* 基于类——cglib -* java字节码——javasist - -> JDK动态代理 - -在Java中要想实现动态代理机制,需要java.lang.reflect.InvocationHandler接口和 java.lang.reflect.Proxy 类的支持 - -* Proxy类:生成动态代理实例 -* InvocationHandler接口:调用处理程序并返回一个结果 - -> InvocationHandler接口 - -每一个代理实例都有一个关联的调用处理程序,InvocationHandler是由代理实例的调用处理程序实现的接口 - -方法:invoke(object, object[]) - -```java -//Object proxy:被代理的对象 -//Method method:要调用的方法 -//Object[] args:方法调用时所需要参数 -public interface InvocationHandler { - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; -} -``` - - - -> Proxy类 - -有个静态方法,可以创建动态代理类的实例 - -```java -//CLassLoader loader:类的加载器 -//Class interfaces:得到全部的接口 -//InvocationHandler h:得到InvocationHandler接口的子类的实例 -public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) throws IllegalArgumentException -``` - -> 动态创建代理对象的类 - -* 动态代理类只能代理接口(不支持抽象类),代理类都需要实现InvocationHandler类,实现invoke方法 -* invoke方法就是**调用被代理接口的所有方法时需要调用的**,该invoke方法返回的值是被代理接口的一个实现类 - -```java -//动态代理类只能代理接口(不支持抽象类),代理类都需要实现InvocationHandler类,实现invoke方法。 -//invoke方法就是调用被代理接口的所有方法时需要调用的,该invoke方法返回的值是被代理接口的一个实现类 -public class LogHandler implements InvocationHandler { - // 目标对象 - private Object targetObject; - //绑定关系,也就是关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke方法。 - public Object newProxyInstance(Object targetObject){ - this.targetObject=targetObject; - //该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例 - //第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器 - //第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口 - //第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法 - //根据传入的目标返回一个代理对象 - return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), - targetObject.getClass().getInterfaces(),this); - } - @Override - //关联的这个实现类的方法被调用时将被执行 - /*InvocationHandler接口的方法,proxy表示代理,method表示原对象被调用的方法,args表示方法的参数*/ - //invoke方法中实现了代理类要扩展的公共功能。 - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{ - //代理扩展逻辑,实现代理类要扩展的公共功能 - System.out.println("proxy do"); - - return method.invoke(targetObject, args); - } -} -``` - -> 优点 - -* 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务 -* 公共业务交给代理,实现了业务的分工 -* 公共业务发生扩展时,方便集中管理 -* 一个动态代理类代理的是一个接口,一般就是对应一个业务 -* 一个动态代理类可以代理多个类,只要是实现了同一个接口即可 - -# 12、AOP - -## 简介 - -> 什么是AOP - -**面向切面编程**,AOP是能够让我们在不影响原有功能的前提下,为软件**横向扩展**功能。 那么横向扩展怎么理解呢,我们在WEB项目开发中,通常都遵守三层原则,包括控制层(Controller)->业务层(Service)->数据层(dao),那么从这个结构下来的为纵向,它具**体的某一层就是我们所说的横向**。我们的AOP就是可以作用于这某一个横向模块当中的所有方法。 - -![](https://gitee.com/tianzhendong/img/raw/master//images/20210808215619.png) - - - -> 和oop区别 - -AOP和OOP的区别:AOP是OOP的补充,当我们需要为多个对象引入一个公共行为,比如日志,操作记录等,就需要在每个对象中引用公共行为,这样程序就产生了大量的重复代码,使用AOP可以完美解决这个问题。 - -> AOP在Spring中的作用 - -**提供声明式事务,允许用户自定义切面** - -- 横切关注点:跨越应用程序多个模块的方法或功能,即:与我们业务逻辑无关的,但我们需要关注的部分,如日志,安全,缓存,事务等 -- 切面:拦截器类,其中会定义切点以及通知 -- 切点:具体拦截的某个业务点 -- 通知:切面当中的方法,声明通知方法在目标业务层的执行位置,通知类型如下: - - 前置通知:@Before 在目标业务方法执行之前执行 - - 后置通知:@After 在目标业务方法执行之后执行 - - 返回通知:@AfterReturning 在目标业务方法返回结果之后执行 - - 异常通知:@AfterThrowing 在目标业务方法抛出异常之后 - - 环绕通知:@Around 功能强大,可代替以上四种通知,还可以控制目标业务方法是否执行以及何时执行 - -## 方式1:使用Spring的API接口实现AOP - -> 导包:注意 - -```xml - - - org.aspectj - aspectjweaver - 1.9.7 - - -``` - -> 接口 - -```java -public interface UserService { - public void add(); - public void delete(); - public void update(); - public void selete(); -} -``` - -> 接口实现类 - -```java -public class UserServiceImpl implements UserService{ - @Override - public void add() { - System.out.println("add"); - } - - @Override - public void delete() { - System.out.println("delete"); - } - - @Override - public void update() { - System.out.println("update"); - } - - @Override - public void selete() { - System.out.println("selete"); - } -} -``` - -> 增强方法log:注意 - -```java -package com.tian.log; - -import org.springframework.aop.MethodBeforeAdvice; -import org.springframework.stereotype.Component; - -import java.lang.reflect.Method; -@Component -public class Log implements MethodBeforeAdvice { - - @Override - //method:要执行的目标对象的方法 - //objects: 参数 - //target:目标对象 - public void before(Method method, Object[] args, Object target) throws Throwable { - System.out.println("目标对象为:" + target.getClass().getName()); - System.out.println("执行的方法为:" + method.getName()); - for (int i = 0; i < args.length; i++) { - System.out.println("第"+i+"个参数为:"+args[i]); - } - } -} -``` - -> xml配置:注意 - -```xml - - - - - - - - - - - -``` - -> 测试:注意 - -```java -import com.tian.service.UserService; -import com.tian.service.UserServiceImpl; -import org.springframework.context.ApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; -public class MyTest { - public static void main(String[] args) { - ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); - //动态代理代理的是接口 - UserService userService = context.getBean("userService", UserService.class); - userService.add(); - } -} -``` - -## 方式2:自定义切入类 - -> 切入类 - -```java -public class DiyPointCut { - - public void before() { - System.out.println("---------方法执行前---------"); - } - - public void after() { - System.out.println("---------方法执行后---------"); - } -} -``` - -> 配置 - -```xml - - - - - - - - - - -``` - -## 方式3:注解 - -> 增强类 - -```java -//声明该类是一个切面 -@Aspect -public class AnnotationPointCut { - - //声明前置方法 - @Before("execution(* com.service.UserServiceImpl.*(..))") - public void before() { - System.out.println("这是使用注解的前置增强"); - } - - //声明后置方法 - @After("execution(* com.service.UserServiceImpl.*(..))") - public void after() { - System.out.println("使用注解的后置增强"); - } - - - //环绕增强的优先级更高 - @Around("execution(* com.service.UserServiceImpl.*(..))") - public void around(ProceedingJoinPoint jp) throws Throwable { - System.out.println("环绕前"); - System.out.println("签名:" + jp.getSignature()); - //执行目标方法proceed - Object proceed = jp.proceed(); - System.out.println("环绕后"); - System.out.println("proceed对象:"+proceed); - } -} - -``` - -> 配置 - -```xml - - - -``` - -# 13、整合Mybatis - -## 步骤: - -1. 导入jar包 - - junit单元测试 - - mybatis - - mysql - - spring - - aop - - mybatis-spring(新包,用于整合) - -```xml - - - junit - junit - 4.12 - - - mysql - mysql-connector-java - 8.0.25 - - - org.mybatis - mybatis - 3.5.7 - - - org.springframework - spring-webmvc - 5.3.9 - - - - org.springframework - spring-jdbc - 5.3.9 - - - org.aspectj - aspectjweaver - 1.9.7 - - - org.mybatis - mybatis-spring - 2.0.2 - - -``` - -2. 编写配置文件 - -3. 测试 - -## 回忆mybatis - -数据: - -![image-20210810222253418](https://gitee.com/tianzhendong/img/raw/master//images/image-20210810222253418.png) - -1. 编写实体类 - -```java -package com.tian.pojo; - -/** - * @program: SpringStudy - * @description: - * @author: TianZD - * @create: 2021-08-10 21:00 - **/ -public class User { - private int id; - private String name; - private String password; - - @Override - public String toString() { - return "User{" + - "id=" + id + - ", name='" + name + '\'' + - ", password='" + password + '\'' + - '}'; - } - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public User() { - } - - public User(int id, String name, String password) { - this.id = id; - this.name = name; - this.password = password; - } -} -``` - -2. 编写核心配置文件 - -```xml - - - - - - - - - - - - - - - - - - - - - - -``` - -3. 编写核心配置类 - -```java -package com.tian.utils; - -import org.apache.ibatis.io.Resources; -import org.apache.ibatis.session.SqlSession; -import org.apache.ibatis.session.SqlSessionFactory; -import org.apache.ibatis.session.SqlSessionFactoryBuilder; -import java.io.IOException; -import java.io.InputStream; - -/** - * @program: SpringStudy - * @description: - * @author: TianZD - * @create: 2021-08-10 21:07 - **/ -public class MybatisUtils { - public static SqlSessionFactory sqlSessionFactory; - static { - try { - String resources = "mybatis-config.xml"; - InputStream inputStream = Resources.getResourceAsStream(resources); - sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); - } catch (IOException e) { - e.printStackTrace(); - } - } - public static SqlSession getSqlSession() { - return sqlSessionFactory.openSession(); - } -} -``` - -4. 编写接口 - -```java -package com.tian.dao; - -import com.tian.pojo.User; - -import javax.sound.midi.VoiceStatus; -import java.util.List; -import java.util.Map; - -/** - * @program: SpringStudy - * @description: - * @author: TianZD - * @create: 2021-08-10 21:18 - **/ -public interface UserMapper { - - public int insert(User user); - - public int delete(int id); - - public int update(User user); - - public User select(int id); -} -``` - -5. 编写Mapper.xml - -```xml - - - - - - - - - - - - delete - from mybatis.user - where id = #{id}; - - - - update mybatis.user - set name = #{name},pwd = #{password} - where id =#{id}; - - - - insert into mybatis.user (id, name, pwd) - values (#{id},#{name},#{password}); - - - - - - -``` - -6. 测试 - -## Mybatis-Spring - -http://mybatis.org/spring/zh/ - -* 不变内容: - -实体类User.class - -UserMapper.interface接口 - -* 编写数据源 - -```xml - - - - - - - -``` - -* SqlSessionFactory - -```xml - - - - - - - - -``` - -* SqlSessionTemplate - -```xml - - - - - -``` - -* 给接口加实现类 - -```java -package com.tian.dao; - -import com.tian.pojo.User; -import org.mybatis.spring.SqlSessionTemplate; - -/** - * @program: SpringStudy - * @description: - * @author: TianZD - * @create: 2021-08-10 22:44 - **/ -public class UserMapperImpl implements UserMapper{ - UserMapper userMapper = null; - private SqlSessionTemplate sqlSessionTemplate; - public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) { - this.sqlSessionTemplate = sqlSessionTemplate; - } - - - @Override - public int insert(User user) { - userMapper = sqlSessionTemplate.getMapper(UserMapper.class); - return userMapper.insert(user); - } - - @Override - public int delete(int id) { - userMapper = sqlSessionTemplate.getMapper(UserMapper.class); - return userMapper.delete(id); - } - - @Override - public int update(User user) { - userMapper = sqlSessionTemplate.getMapper(UserMapper.class); - return userMapper.update(user); - } - - @Override - public User select(int id) { - userMapper = sqlSessionTemplate.getMapper(UserMapper.class); - return userMapper.select(id); - } -} -``` - -* 将实现类注入到spring中 - -```xml - - - -``` - -* 测试 - -```xml -import com.tian.dao.UserMapper; -import com.tian.pojo.User; -import com.tian.utils.MybatisUtils; -import org.apache.ibatis.session.SqlSession; -import org.apache.ibatis.session.SqlSessionFactory; -import org.junit.Test; -import org.springframework.context.ApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * @program: SpringStudy - * @description: - * @author: TianZD - * @create: 2021-08-10 21:33 - **/ -public class MyTest { - @Test - public void userMapperTest() { - ApplicationContext context = new ClassPathXmlApplicationContext("spring-mybatis.xml"); - UserMapper userMapper = context.getBean("userMapperImpl", UserMapper.class); - System.out.println(userMapper.select(2)); - } -} -``` - - - - - -## 进一步简化:SqlSessionDaoSupport - - - -`SqlSessionDaoSupport` 是一个抽象的支持类,用来为你提供 `SqlSession`。调用 `getSqlSession()` 方法你会得到一个 `SqlSessionTemplate`,之后可以用于执行 SQL 方法 - -```java -package com.tian.dao; - -import com.tian.pojo.User; -import org.apache.ibatis.session.SqlSession; -import org.mybatis.spring.support.SqlSessionDaoSupport; - -/** - * @program: SpringStudy - * @description: - * @author: TianZD - * @create: 2021-08-10 23:17 - **/ -public class UserMapperImpl_2 extends SqlSessionDaoSupport implements UserMapper{ - SqlSession sqlSession = getSqlSession(); - UserMapper userMapper = sqlSession.getMapper(UserMapper.class); - - @Override - public int insert(User user) { - return userMapper.insert(user); - } - - @Override - public int delete(int id) { - return userMapper.delete(id); - } - - @Override - public int update(User user) { - return userMapper.update(user); - } - - @Override - public User select(int id) { - return userMapper.select(id); - } - -} -``` - -注册:略去了sqlsessiontemplate注册 - -需要注册userMapperimpl2,SqlSessionDaoSupport` 需要通过属性设置一个 `sqlSessionFactory` 或 `SqlSessionTemplate - -```xml - - - - - - - - - - - - - - - - - - - -``` - - - -# 14、声明式事务 - -事务分为: - -* 声明式事务:AOP实现 -* 编程式事务 - - - -## 使用声明式事务 - -> 配置声明式事务 - -要开启 Spring 的事务处理功能,在 Spring 的配置文件中创建一个 `DataSourceTransactionManager` 对象: - -```xml - - - -``` - - - -> aop实现事务 - -```xml - - - - - - - - - - - - - - - - - - -``` - -> 事务的传播特性 - -事务传播特性,就是多个事务方法调用时如何定义方法间事务的传播。Spring 定义了 7 种传播行为: - -- propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是Spring默认的选择。 -- propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。 -- propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。 -- propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。 -- propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 -- propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。 -- propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作。 - diff --git a/source/_posts/Java/SpringBoot.md b/source/_posts/Java/SpringBoot.md deleted file mode 100644 index 469da0b..0000000 --- a/source/_posts/Java/SpringBoot.md +++ /dev/null @@ -1,3642 +0,0 @@ ---- -title: SpringBoot -author: TianZD -top: true -cover: true -toc: true -mathjax: false -summary: SpringBoot框架学习笔记,粗略学了一下,没有参考价值 -tags: - - SpringBoot - - 学习笔记 -categories: - - java -reprintPolicy: cc_by -abbrlink: 6f2612a2 -date: 2022-04-29 11:07:58 -coverImg: -img: -password: ---- - - - -[toc] - -# SpringBoot - -# 1、概述 - -## 1.1、学习总结 - -* JavaSE:oop -* mysql:持久化 -* html+css+js+jquery+框架:视图 -* javaWeb:独立开发MVC三层架构的网站 -* ssm:框架:简化了开发的流程,配置繁琐 - -war包:tomcat运行 - -* spring简化:SpringBoot,jar包,内嵌tomcat,微服务架构 -* springcloud:管理微服务 - -## 1.2、spring - -**Spring理念、目的** - -spring是**为了解决企业级应用开发的复杂性**创建的,**简化开发** - -**Spring如何简化开发** - -1. 基于pojo的轻量级和最小入侵编程 -2. 通过IOC,依赖注入(DI)和面向接口实现松耦合 -3. 基于切面(AOP)和惯例进行声明式编程 -4. 通过切面和模板(template)减少样式代码 - - - -> 区别 - -程序=数据结构+算法:程序员 - -程序=面向对象+框架:码农 - -## 1.3、微服务 - -> 什么是微服务 - -微服务是一种**架构风格**,要求在开发一个应用的时候,应用必须构建成一系列小服务的组合,可以通过http的方式通信 - -> 单体运行架构 - -all in one,所用的应用服务都封装在一个应用中 - -> 微服务架构 - -打破all in one的架构方式,把每个功能元素苏里出来,把苏里出来的功能元素动态组合,需要的功能元素才去拿来组合,需要多一时可以整合多个功能元素,所以微服务架构是对功能元素进行复制,没有对整个应用进行复制 - -* 节省了调用资源 -* 每个功能元素的服务都是一个可替换的,可独立升级的软件代码 - -## 1.4、SpringBoot - -> 概述 - -Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。用我的话来理解,就是 Spring Boot 其实不是什么新的框架,它默认配置了很多框架的使用方式,就像 Maven 整合了所有的 Jar 包,Spring Boot 整合了所有的框架。 - -> 优势 - -其实就是简单、快速、方便!平时如果我们需要搭建一个 Spring Web 项目的时候需要怎么做呢? - -* 1)配置 web.xml,加载 Spring 和 Spring mvc -* 2)配置数据库连接、配置 Spring 事务 -* 3)配置加载配置文件的读取,开启注解 -* 4)配置日志文件 -* … -* 配置完成之后部署 Tomcat 调试 -* … - -现在非常流行微服务,如果我这个项目仅仅只是需要发送一个邮件,如果我的项目仅仅是生产一个积分;我都需要这样折腾一遍! - -但是如果使用 Spring Boot 呢? -很简单,我仅仅只需要非常少的几个配置就可以迅速方便的搭建起来一套 Web 项目或者是构建一个微服务! - -## 1.5、Spring Boot、Spring MVC 和 Spring - -分别描述各自的特征: - -* Spring 框架就像一个**家族**,有众多衍生产品例如 boot、security、jpa等等;但他们的基础都是Spring 的**ioc和 aop**,**ioc 提供了依赖注入的容器**, **aop解决了面向切面编程**,然后在此两者的基础上实现了其他延伸产品的高级功能。 - -* Spring MVC提供了一种**轻度耦合的方式来开发web应用**;它是**Spring的一个模块**,是**一个web框架**;通过**DispatcherServlet,** ModelAndView 和 View Resolver,开发web应用变得很容易;解决的问题领域是网站应用程序或者服务开发——URL路由、Session、模板引擎、静态Web资源等等。 - -* Spring Boot实现了auto-configuration**自动配置**(另外三大神器actuator监控,cli命令行接口,starter依赖),降低了项目搭建的复杂度。它主要是为了解决使用Spring框架需要进行大量的配置太麻烦的问题,所以它并不是用来替代Spring的解决方案,而是和Spring框架紧密结合用于提升Spring开发者体验的工具;同时它集成了大量常用的第三方库配置(例如Jackson, JDBC, Mongo, Redis, Mail等等),Spring Boot应用中这些第三方库几乎可以零配置的开箱即用(out-of-the-box)。 - -所以,用最简练的语言概括就是: - -* Spring 是一个“引擎”; - -* Spring MVC 是基于Spring的一个 MVC 框架; - -* Spring Boot 是基于Spring4的条件注册的一套快速开发整合包。 - -# 2、SpringBoot程序 - -## 2.1、初始化 - -> 创建: - -* IDEA新建项目,选择SpringInitializr - -![image-20210822110418427](https://i.loli.net/2021/08/22/8Tph41FdNJseuf9.png) - -* 勾选SpringWeb - -![image-20210822110442198](https://i.loli.net/2021/08/22/5FcsUVz69rJuNRa.png) - -* 新建之后,删除多余文件 - -![image-20210822112126048](https://i.loli.net/2021/08/22/Xt3I1BbJvcAu9MQ.png) - -> 分析 - -默认自动生成一下文件 - -* springboot启动文件,启动器 - -```java -package com.tian.springboot01helloworld; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class Springboot01HelloworldApplication { - - public static void main(String[] args) { - SpringApplication.run(Springboot01HelloworldApplication.class, args); - } - -} -``` - -* application.properties,默认为空 -* pop.xml 依赖 - -```xml - - - - - - - - 4.0.0 - org.springframework.boot - spring-boot-starter-web - 2.5.4 - spring-boot-starter-web - Starter for building web, including RESTful, applications using Spring MVC. Uses Tomcat as the default embedded container - https://spring.io/projects/spring-boot - - Pivotal Software, Inc. - https://spring.io - - - - Apache License, Version 2.0 - https://www.apache.org/licenses/LICENSE-2.0 - - - - - Pivotal - info@pivotal.io - Pivotal Software, Inc. - https://www.spring.io - - - - scm:git:git://github.com/spring-projects/spring-boot.git - scm:git:ssh://git@github.com/spring-projects/spring-boot.git - https://github.com/spring-projects/spring-boot - - - GitHub - https://github.com/spring-projects/spring-boot/issues - - - - org.springframework.boot - spring-boot-starter - 2.5.4 - compile - - - org.springframework.boot - spring-boot-starter-json - 2.5.4 - compile - - - org.springframework.boot - spring-boot-starter-tomcat - 2.5.4 - compile - - - org.springframework - spring-web - 5.3.9 - compile - - - org.springframework - spring-webmvc - 5.3.9 - compile - - - -``` - -* 测试文件 - -```java -package com.tian.springboot01helloworld; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class Springboot01HelloworldApplicationTests { - - @Test - void contextLoads() { - } - -} -``` - -> 打jar包 - -* 在右侧maven中,双击package,自动打jar包 - -![image-20210822113501487](https://i.loli.net/2021/08/22/CFH5hvoyu1OsqE3.png) - -* 在target目录下,生成一个jar包 - -![image-20210822113642680](https://i.loli.net/2021/08/22/LaDJh6kZs1glytP.png) - -* 在命令行进入jar包所在的目录 -* 执行`java -jar .\springboot-01-helloworld-0.0.1-SNAPSHOT.jar`![image-20210822114010844](https://i.loli.net/2021/08/22/WiYaEIflxM93GtZ.png) - -> 新建目录结构 - -在Springboot启动程序的同级目录下新建controller、dao、pojo、service - - - -![image-20210822112537801](https://i.loli.net/2021/08/22/P3STa8FxzvYUDp1.png) - -> 新建简单接口 - -```java -package com.tian.springboot01helloworld.controller; - -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -public class HelloController { - @RequestMapping("/hello") - public String hello() { - // 调用业务,接收前端参数 - return "hello,world!"; - } -} -``` - -`@RestController` 的意思就是 Controller 里面的方法都以 json 格式输出,不用再写什么 jackjson 配置的了! - -> 浏览器访问 - - - -## 1.2、配置 - -> 更改项目端口号 - -application.properties - -```properties -# 更改端口号 -server.port=8081 -``` - -> 更改banner - -* 百度搜索springboot banner在线生成,并复制 - -* 在resources下创建`banner.txt`,并粘贴进去 - -![image-20210822115259816](https://i.loli.net/2021/08/22/5C9B4gzlGaODU7p.png) - -> 开发环境的热启动 - -热启动在正常开发项目中已经很常见了吧,虽然平时开发web项目过程中,改动项目启重启总是报错;但springBoot对调试支持很好,修改之后可以实时生效,需要添加以下的配置: - -```xml - - - org.springframework.boot - spring-boot-devtools - true - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - true - - - - -``` - -该模块在完整的打包环境下运行的时候会被禁用。如果你使用 `java -jar`启动应用或者用一个特定的 classloader 启动,它会认为这是一个“生产环境”。 - -# 3、原理 - -## 3.1、启动执行流程 - -![img](https://i.loli.net/2021/08/22/tBX3JQApne9H8Mb.png) - -上图为SpringBoot启动结构图,我们发现启动流程主要分为三个部分: - -* 第一部分进行SpringApplication的初始化模块,配置一些基本的环境变量、资源、构造器、监听器; -* 第二部分实现了应用具体的启动方案,包括启动流程的监听模块、加载配置环境模块、及核心的创建上下文环境模块; -* 第三部分是自动化配置模块,该模块作为springboot自动配置核心,在后面的分析中会详细讨论。在下面的启动程序中我们会串联起结构中的主要功能。 - -## 3.2、入口 - -springboot有自己独立的启动类(独立程序) - -```java -@SpringBootApplication -public class Springboot01HelloworldApplication { - - public static void main(String[] args) { - SpringApplication.run(Springboot01HelloworldApplication.class, args); - } - -} -``` - -主要包括: - -* Annotation定义(@SpringBootApplication),是Spring Boot的核心注解,它其实是一个组合注解 -* 类定义(SpringApplication.run):启动整个spring-boot程序,该方法所在类需要使用 - - - -## 3.3、自动配置 - -![preview](https://i.loli.net/2021/08/22/AW2O3vsFH4CQGuM.png) - -### 3.3.1、@SpringBootApplication - -```java -@Target``(ElementType.TYPE) ``// 注解的适用范围,其中TYPE用于描述类、接口(包括包注解类型)或enum声明 -@Retention``(RetentionPolicy.RUNTIME) ``// 注解的生命周期,保留到class文件中(三个生命周期) -@Documented` `// 表明这个注解应该被javadoc记录 -@Inherited` `// 子类可以继承该注解 -@SpringBootConfiguration` `// 继承了Configuration,表示当前是注解类 -@EnableAutoConfiguration` `// 开启springboot的注解功能,springboot的四大神器之一,其借助@import的帮助 -@ComponentScan``(excludeFilters = { ``// 扫描路径设置 -@Filter``(type = FilterType.CUSTOM, classes = TypeExcludeFilter.``class``), -@Filter``(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.``class``) }) -public` `@interface` `SpringBootApplication { -... -}   -``` - -![img](https://i.loli.net/2021/08/22/XyWueDcvs4SgZVa.png) - -在其中比较重要的有三个注解,分别是: - -* @SpringBootConfiguration // 继承了Configuration,表示当前是注解类 -* @EnableAutoConfiguration // 开启自动配置 -* @ComponentScan(excludeFilters = { // 扫描路径设置(具体使用待确认)扫描主类所在的同级包以及下级包里的Bean - -接下来对三个注解一一详解,增加对springbootApplication的理解: - -#### **@SpringBootConfiguration** - -在springboot中我们大多用配置类来解决配置问题 - -@SpringBootConfiguration继承了Configuration,表示当前是注解类 - -@Configuration实际上就是一个@Component,表示一个受Spring管理的组件 - -#### **@ComponentScan** - -* ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,将这些bean定义加载到**IoC**容器中 - -* 我们可以通过basePackages等属性来**细粒度**的定制@ComponentScan自动扫描的范围,如果不指定,则**默认**Spring框架实现会从声明@ComponentScan所在类的package进行扫描。 - -#### **@EnableAutoConfiguration**:自动配置核心 - -此注解顾名思义是可以**自动配置**,所以应该是springboot中**最为重要的注解**。 - -```java -@Target({ElementType.TYPE}) -@Retention(RetentionPolicy.RUNTIME) -@Documented -@Inherited -@AutoConfigurationPackage -@Import({AutoConfigurationImportSelector.class}) -``` - -其中最重要的两个注解:1、@AutoConfigurationPackage【重点注解】2、@Import(AutoConfigurationImportSelector.class)【重点注解】 - -> `@AutoConfigurationPackage` - -自动配置包。内部是采用了@Import,来给容器导入一个Registrar组件,**程序运行到这里,会去加载启动类所在包下面的所有类** - -`@Import`:将一个组件注入容器中 - -1. **`@Import({Registrar.class})`** - -```java -static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { - Registrar() { - } - - public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { - AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0])); - } - - public Set determineImports(AnnotationMetadata metadata) { - return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata)); - } -} -``` - -该类有一个主要方法是**registerBeanDefinitions()**,该方法对容器中进行了批量注册 - -* AnnotationMetadata(注解元数据类):该类存储了注解相关的位置、属性值等信息 -* PackageImports(metadata).getPackageNames():该方法用于返回该注解所在位置的包名信息。(由此可以得到,该位置包名本质其实就是主程序所在的位置): - -进入到**register()**方法的底层,发现其功能是将该包名位置下的所有组件添加到容器当中去。 - -由此可以得到,该注解@AutoConfigurationPackage本质上就是通过导入Register类,将@SpringBootApplication核心注解(即主程序入口)所在位置同层级包下的所有组件扫描注册到Spring容器当中去。这也就解释了为什么在建立dao、service等目录结构的时候,应该在启动程序同级目录下新建 - -![image-20210822133236798](https://i.loli.net/2021/08/22/c1SkgxVbsMWRQAn.png) - -> `@Import(AutoConfigurationImportSelector.class)` - -![img](https://i.loli.net/2021/08/22/rZwnhQWpfHlFgVR.png) - -* 通过@Import往容器中导入注册了一个特殊类**AutoConfigurationImportSelector**。该类中有一个主要方法是**selectImports()** -* selectImports()方法主要利用**getAutoConfigurationEntry**(annotationMetadata)给容器中批量导入了一些组件 -* getAutoConfigurationEntry方法的主要作用是调用List configurations = getCandidateConfigurations(annotationMetadata, attributes)获取了所有的131个初始候选配置类,并将他们通过一系列筛选过滤后返回上层,最后进一步批量注册到容器中发挥作用。 -* 利用工厂加载 Map **loadSpringFactories**(@Nullable ClassLoader classLoader),得到所有的组件 -* 从**META-INF/spring.factories**位置来加载一个文件。默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件。 - -我们发现在项目初始导入的每个Jar包下,都可能包含META-INF/spring.factories这个文件,文件中配置声明了一系列系统类的全类名。而其中比较核心的是spring-boot-autoconfigure-2.5.3.jar这个自动配置包,该文件中使用autoconfigure.EnableAutoConfiguration=声明了131个初始配置类 - -**总结**: - -该注解实际上是通过导入了AutoConfigurationImportSelector这个类,初始化来自动扫描加载所有META-INF/spring.factories这个文件下写死的资源(SpringBoot帮我们配置好了),其中比较核心的是spring-boot-autoconfigure-2.5.3.jar这个自动配置包,该包帮我们初始化加载了131个初始配置类,帮助我们进一步完成SpringBoot的启动和配置功能! - -> 总结 - -![在这里插入图片描述](https://i.loli.net/2021/08/22/7QDXaMqZxFvNAmb.png) - -### 3.3.2、自动配置流程总结 - -![在这里插入图片描述](https://i.loli.net/2021/08/22/Ge8bvq3DUyktcg2.png) - - - -### 3.3.3、修改默认配置 - -由上可以知道,自动配置会从META-INF/spring.factories加载初始配置类,初始配置类以XXXAutoConfiguration结尾,如org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration, - -![image-20210822174506126](https://i.loli.net/2021/08/22/bxo7Se3w1ZQDFa9.png) - -点进去是`WebMvcAutoConfiguration.java`配置类 - -修改其默认配置可以通过`application.yaml`修改`WebMvcAutoConfiguration.java`配置类中对应的参数 - -如: - -```yaml -spring: - mvc: - static-path-pattern: /** -``` - -### 3.3.4、总结 - -**一句话总结 :根据当前不同的条件判断,决定这个配置类是否生效!** - -* 一但这个配置类生效;这个配置类就会给容器中添加各种组件; -* 这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的; -* 所有在配置文件中能配置的属性都是在xxxxProperties类中封装着; -* 配置文件能配置什么就可以参照某个功能对应的这个属性类 - -> 精髓 - -1、SpringBoot启动会加载大量的自动配置类 - -2、我们看我们需要的功能有没有在SpringBoot默认写好的自动配置类当中; - -3、我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件存在在其中,我们就不需要再手动配置了) - -4、给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们只需要在配置文件中指定这些属性的值即可; - -**xxxxAutoConfigurartion:自动配置类;**给容器中添加组件 - -**xxxxProperties:封装配置文件中相关属性;** - -> 了解:@Conditional - -了解完自动装配的原理后,我们来关注一个细节问题,**自动配置类必须在一定的条件下才能生效;** - -**@Conditional派生注解(Spring注解版原生的@Conditional作用)** - -作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效; - -![图片](https://mmbiz.qpic.cn/mmbiz_png/uJDAUKrGC7IPEXZtUAUBhnSZvUmrPzbDGcJRvdK3PtqHPAWYBBmpe1XBVjQJeiatU4vasEaxckHlOga1BV9RPaw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) - -**那么多的自动配置类,必须在一定的条件下才能生效;也就是说,我们加载了这么多的配置类,但不是所有的都生效了。** - -我们怎么知道哪些自动配置类生效? - -**我们可以通过启用 debug=true属性;来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效;** - -``` -#开启springboot的调试类 -debug=true -``` - -**Positive matches:(自动配置类启用的:正匹配)** - -**Negative matches:(没有启动,没有匹配成功的自动配置类:负匹配)** - -**Unconditional classes: (没有条件的类)** - - - -## 3.4、依赖管理 - -> 父工程 - -* springboot的父工程中,进行了**资源过滤**、**核心依赖**导入 -* 在写或者引入依赖的时候,不需要指定版本 - -```xml - - - org.springframework.boot - spring-boot-starter-parent - 2.5.4 - - -``` - -点进去,发现其父项目为:spring-boot-dependencies - -```xml - - org.springframework.boot - spring-boot-dependencies - 2.5.4 - -``` - -spring-boot-dependencies声明了很多开发中常用的jar包 - -![image-20210822150254879](https://i.loli.net/2021/08/22/NSDrvB8fOjHwYGF.png) - -所以在你pom.xml文件中引入jar的时候,如果该jar在`spring-boot-dependencies`中定义了版本号,那么你可以不写。如果你想使用其他的版本号,那么也可以在pom.xml中定义version,遵循就近原则。比如你想使用自定义版本号的mysql驱动,只需在pom.xml中进行定义 - -```xml - - 5.1.43 - -``` - - - -> 启动器 - -在SpringBoot项目中,我们只需要引入`spring-boot-starter-web`包就可以写接口并且进行访问,因为在这个starter中整合了我们之前写Spring项目时引入的`spring-aop`、`spring-context`、`spring-webmvc`等jar包,包括tomcat,所以SpringBoot项目不需要外部的tomcat,只需要启动application类使用内置的tomcat服务器即可。 - -```xml - - - - org.springframework.boot - spring-boot-starter-web - -``` - -```xml - - - org.springframework.boot - spring-boot-starter - 2.5.4 - compile - - - org.springframework.boot - spring-boot-starter-json - 2.5.4 - compile - - - org.springframework.boot - spring-boot-starter-tomcat - 2.5.4 - compile - - - org.springframework - spring-web - 5.3.9 - compile - - - org.springframework - spring-webmvc - 5.3.9 - compile - - -``` - -在SpringBoot项目中,根据官方文档,有各种场景的`spring-boot-starter-*`可以使用,只要引入了starter,这个场景所有常规需要的依赖就会自动引入。 - -所有场景启动器最底层的依赖就是`spring-boot-starter`,该jar包是核心启动包,包含了自动配置的支持,日志以及YAML。Core starter, including auto-configuration support, logging and YAML,这是官方对它的描述。 - -```xml - - - org.springframework.boot - spring-boot - 2.5.4 - compile - - - org.springframework.boot - spring-boot-autoconfigure - 2.5.4 - compile - - - org.springframework.boot - spring-boot-starter-logging - 2.5.4 - compile - - - jakarta.annotation - jakarta.annotation-api - 1.3.5 - compile - - - org.springframework - spring-core - 5.3.9 - compile - - - org.yaml - snakeyaml - 1.28 - compile - - -``` - -# 4、yaml - -## 4.1、配置文件 - -SpringBoot使用一个全局的配置文件,配置文件的名称是固定的 - -* application.properties - * 语法:`key=value` -* application.yml - * 语法:`key: 空格 value` - -配置文件的作用:修改SpringBoot自动配置的默认值 - -## 4.2、yaml - -以前的配置文件都是用xml配置 - -xml配置: - -```xml - - 8081 - -``` - -yaml配置 - -```yaml -server: - port: 8081 -``` - -> 基本语法规则 - -**同一级的字段要对齐,冒号后面要带上空格。** - -* 大小写敏感 -* 使用缩进表示层级关系 -* **缩进时不允许使用Tab键,只允许使用空格**。 -* 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可 -* **#** 表示注释,从这个字符一直到行尾,都会被解析器忽略。 - -YAML 支持的数据结构有三种。 - -* 对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary) -* 数组:一组按次序排列的值,又称为序列(sequence) / 列表(list) -* 纯量(scalars):单个的、不可再分的值 - -```yaml -# 普通的key-value -name: tian -# 对象 -student: - name: chen - age: 18 - sex: 男 - class: 11 -student: {name: tian,age: 18} -# 数组 -# 一维数组 -list: - - A - - B - - C -list: [A,B,C] -``` - -## 4.3、给属性赋值 - -> 实体类 - -Dog.class - -```java -@Component -public class Dog { - private String name; - private Integer age; - // get,set,tostring,constuctor -} -``` - -Person.class - -```java -@Component -public class Person { - private String name; - private Integer age; - private boolean happy; - private Date birth; - private Map map; - private List list; - private Dog dog; - // get,set,tostring,constuctor -} -``` - -> spring赋值 - -@Value(“value”) - -@Autowired - -```java -public class Dog { - @Value("wangzai") - private String name; - @Value("1") - private Integer age; -} -``` - -```java -@SpringBootTest -class Springboot02AddvalueApplicationTests { - @Autowired - private Dog dog; - @Test - void contextLoads() { - System.out.println(dog); - } -} -``` - -> yaml赋值 - -application.yaml - -```yaml -person: - name: tian - age: 2 - happy: false - birth: 2020-01-01 - map: {k1: v1,k2: v2} - list: - - code - - jf - - fas - dog: - name: wangcai - age: 1 -``` - -person.class - -```java -@Component -@ConfigurationProperties(prefix = "person") -public class Person { - ... -} -``` - -> Spring Boot Configuration Annotation Processor not configured 问题解决 - -![image-20210822163721616](https://i.loli.net/2021/08/22/QIUvbnGBXkZWFzM.png) - -* 分析 - -它的意思是“Spring Boot配置注解执行器没有配置”,配置注解执行器的好处是什么。 - -配置注解执行器配置完成后,当执行类中已经定义了对象和该对象的字段后,在配置文件中对该类赋值时,便会非常方便的弹出提示信息。 - -* 解决 - -在pom.xml文件中引入依赖 - -```xml - - org.springframework.boot - spring-boot-configuration-processor - true - -``` - -> 松散绑定 - -在yaml中写的是`last-name`,等同于`lastName` - -> JSR303数据校验@Validated - -可以在字段增加一层过滤器验证,保证数据的合法性 - -* 导入依赖 - -pom.xml - -```xml - - org.springframework.boot - spring-boot-starter-validation - -``` - -* 添加注解 - -```java -@Validated -public class Person { - // message为数据不合法时候的提示信息 - @Email(message = "邮箱格式错误") - private String email; -} -``` - -* 常见参数 - -![在这里插入图片描述](https://i.loli.net/2021/08/22/q89vAVmwOb1frNI.png) - - - -* 附加 - -![在这里插入图片描述](https://i.loli.net/2021/08/22/wgMAXsk58H4mpWB.png) - -```java -@NotNull(message="名字不能为空") -private String userName; -@Max(value=120,message="年龄最大不能查过120") -private int age; -@Email(message="邮箱格式错误") -private String email; - -空检查 -@Null 验证对象是否为null -@NotNull 验证对象是否不为null, 无法查检长度为0的字符串 -@NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格. -@NotEmpty 检查约束元素是否为NULL或者是EMPTY. - -Booelan检查 -@AssertTrue 验证 Boolean 对象是否为 true -@AssertFalse 验证 Boolean 对象是否为 false - -长度检查 -@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内 -@Length(min=, max=) string is between min and max included. - -日期检查 -@Past 验证 Date 和 Calendar 对象是否在当前时间之前 -@Future 验证 Date 和 Calendar 对象是否在当前时间之后 -@Pattern(value) 验证 String 对象是否符合正则表达式的规则 -``` - -# 5、配置文件 - -配置文件必须以`application`开头,`application`后面可以有其他东西,`application-test` - -## 5.1、配置文件位置 - -配置文件可以放的位置:从上到下,优先级降低 - -1. `file:./config/` -2. `file:./` -3. `classpath:/config/` - -4. `classpath:/` - -`file:./`为项目根目录,即`pom.xml`所在的同级目录 - -`classpath:/`为`src/main/resources`下的目录 - -## 5.2、多环境配置 - -一个项目在开发环境dev、测试环境test、生产环境prod、预发布环境uat,不同的环境会有不同的配置,比如数据库的配置就不同 - -### 方法1:修改配置文件 - - - -![img](https://i.loli.net/2021/08/22/6GBxLqInEfD8umt.png) - -这种方式的好处就是可以把公共的变量配置在application.properties文件中,不同环境需要的变量配置在不同的文件中,比如数据库信息,线程池的大小,redis信息等。 - -**修改application.yml配置文件,具体内容如下图:** - -![img](https://i.loli.net/2021/08/22/m1WZKIfgTzoQAMD.png) - -在启动服务时,服务器就会通过application.yml文件去调用application-dev.yml文件 - -### 方法2:命令启动服务,命令中带参数方式(此方式可以没有application.yml文件) - -第一步:进入到项目目录下,先用maven对项目进行打包,会在target目录下生成项目的jar包 - -第二步:进入target目录,执行命令:java -jar 生成的jar包 --spring.profiles.active=prod - -项目就会调用application-prod.yml配置文件,即以生产环境的配置要求启动服务。同理,若是开发环境,只需将prod改为dev即可。 - -# 6、SpringBoot Web开发 - -自动配置会从`META-INF/spring.factories`加载初始配置类,初始配置类以`XXXAutoConfiguration`结尾 - -springboot中webMVC开发的自动配置为`org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration`,配置类为:`WebMvcAutoConfiguration.java` - -## 6.1、静态资源路径 - -静态资源路径是指系统可以直接访问的路径,且路径下的所有文件均可被用户通过浏览器直接读取。 - -> 默认配置 - -`WebMvcAutoConfiguration.java`配置类中的静态资源路径设置: - -```java -@Override -public void addResourceHandlers(ResourceHandlerRegistry registry) { - //用户自定义后,会覆盖默认的 - if (!this.resourceProperties.isAddMappings()) { - logger.debug("Default resource handling disabled"); - return; - } - //默认路径 - addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/"); - addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> { - registration.addResourceLocations(this.resourceProperties.getStaticLocations()); - if (this.servletContext != null) { - ServletContextResource resource = new ServletContextResource(this.servletContext, SERVLET_LOCATION); - registration.addResourceLocations(resource); - } - }); -} -``` - -Spring Boot 对静态资源映射提供了默认配置 - -* **自定义**:自定义后,会把下面两个默认的覆盖 -* **webjars(不建议使用)**:`localhost:8080/webjars/` -* **classpath**:`localhost:8080/` - * `classpath:/static` - * `classpath:/public` - * `classpath:/resources` - * `classpath:/META-INF/resources` - -![image-20210822181213875](https://i.loli.net/2021/08/22/c76gZ3X1kUfJVS5.png) - -优先级:resources>**static**>public - -**注意:templates不是静态资源路径** - -在src/main/resources目录下新建 public、resources、static 三个目录,并分别放入 1.jpg 2.jpg 3.jpg 三张图片。然后通过浏览器分别访问: - -``` -http://localhost:8080/1.jpg -http://localhost:8080/2.jpg -http://localhost:8080/3.jpg -``` - -地址均可以正常访问,Spring Boot 默认会从 public resources static 三个目录里面查找是否存在相应的资源。 - -> 自定义资源路径 - -`properties.yaml` - -```yaml -spring: - mvc: - static-path-pattern: /upload/** -``` - -通过spring.mvc.static-path-pattern这种方式配置,会使Spring Boot的默认配置失效,也就是说,/public , /resources 等默认配置不能使用。 -配置中配置了静态模式为/upload/**,访问时候就只能通过/upload/xx 来访问。 - -``` -http://localhost:8080/upload/1.jpg -``` - -## 6.2、首页定制 - -首页可以放在public、resources、static目录下,通过`localhost:8080`直接访问 - -![image-20210822182234904](https://i.loli.net/2021/08/22/hNDoU21k9ZXFMYO.png) - -在templates下的只能通过controller进行访问 - -## 6.3、Thymeleaf模板引擎 - -### 模板 - -模板的诞生是为了将显示与数据分离,模板技术多种多样,但其本质是将模板文件和数据通过模板引擎生成最终的HTML代码。 - -![ZWPF0M5W_CR_NEUY6H46__W](https://oss-cn-hangzhou.aliyuncs.com/yqfiles/6c258773c5fc6fca7229b978a7a27b8ff1b326f0.png) - -模板技术并不是什么神秘技术,干的是拼接字符串的体力活。模板引擎就是利用正则表达式识别模板标识,并利用数据替换其中的标识符。比如: - -```js -Hello, <%= name%> -``` - -数据是`{name: '木的树'}`,那么通过模板引擎解析后,我们希望得到`Hello, 木的树`。模板的前半部分是普通字符串,后半部分是模板标识,我们需要将其中的标识符替换为表达式。模板的渲染过程如下: -![7J8ICGIRY_4PH_0N_6COAXO](https://oss-cn-hangzhou.aliyuncs.com/yqfiles/e437978f5ecc9f4046d63de6cdcce42bdc308ea2.png) - -### 模板引擎 - -> 什么是模板引擎 - -**模板引擎**(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的html文档。从字面上理解模板引擎,最重要的就是模板二字,这个意思就是做好一个模板后套入对应位置的数据,最终以html的格式展示出来,这就是模板引擎的作用。 - -如:**Thymeleaf**,FreeMarker,Enjoy,Velocity,**JSP** - -> JSP - -虽然是一款功能比较强大的模板引擎,并被广大开发者熟悉,但它前后端耦合比较高。比如说前端的html页面还要手动修改成jsp页面,大大加重了工作量,而且动态和静态资源也是耦合性太高。 - -其次是JSP页面的效率没有HTML高,因为JSP是同步加载。而且JSP需要tomcat,但又不支持nginx等,已经跟不上时代的潮流。 - -综上:目前开发中已经很少用JSP了,只是我们很多时候会在碰到一些以前的框架里有用到JSP技术,但是技多不压身,推荐还是学一下(如果工作不需要,可以不学)。 - -### Thymeleaf介绍 - -[Thymeleaf 官网](https://www.thymeleaf.org/) - -> 特点 - -Thymeleaf 是一个跟 Velocity、FreeMarker 类似的模板引擎,它可以完全替代 JSP 。相较与其他的模板引擎,它有如下三个极吸引人的特点: - -* Thymeleaf 在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它支持 html 原型,然后在 html 标签里增加额外的属性来达到模板+数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以 thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示。 - -* Thymeleaf 开箱即用的特性。它提供标准和spring标准两种方言,可以直接套用模板实现JSTL、 OGNL表达式效果,避免每天套模板、该jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。 - -* Thymeleaf 提供spring标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。 - -> **动静分离**: - -浏览器无法直接识别`.jsp`文件,需要借助网络(服务端)才能进行访问;而Thymeleaf用html做模板可以直接在浏览器中打开。开发者充分考虑html页面特性,将Thymeleaf的语法通过html的标签属性来定义完成,这些标签属性不会影响html页面的完整性和显示。如果通过后台服务端访问页面服务端会寻找这些标签将服务端对应的数据替换到相应位置实现动态页面!大体区别可以参照下图: - -![在这里插入图片描述](https://i.loli.net/2021/08/22/wQybk5IetuFZx1M.png) - -上图的意思就是如果直接打开这个html那么浏览器会对`th`等标签忽视而显示原始的内容。如果通过服务端访问那么服务端将先寻找`th`标签将服务端储存的数据替换到对应位置。具体效果可以参照下图,下图即为一个动静结合的实例。 - -### 使用Thymeleaf - -对于构建一个完整程序,创建第一个Thymeleaf程序需要以下几个步骤: - -1. 创建程序,添加依赖 -2. 编写Controller -3. 编写Thymeleaf页面:需要放在`templates`目录下 -4. 访问页面 - -> 创建程序,添加依赖 - -* 方法1:在创建程序的时候,勾选其中Web 模块的Spring web依赖以及Template 模块的Thymeleaf依赖 - -![image-20210822201028792](https://i.loli.net/2021/08/22/loQriWv6zZKFAmR.png) - -* 方法2:在pom.xml中添加以下依赖 - -```xml - - org.springframework.boot - spring-boot-starter-thymeleaf - - - org.springframework.boot - spring-boot-starter-web - -``` - -> 编写controller - -```java -@Controller -public class ThymeleafController { - @RequestMapping("/test") - public String test1(Model model) { - model.addAttribute("msg", "hello,thymeleaf"); - return "test"; - } -} -``` - -> 编写Thymeleaf页面 - -咱们在项目的resources目录下的templates文件夹下面创建一个叫index.html的文件,咱们在这个html文件中的``标签修改为``这样在Thymeleaf中就可以使用Thymeleaf的语法和规范啦。 - -`test.html` - -```html - - - - - Title - - -

tian

- - -``` - -> 浏览器测试 - -* 直接打开页面 - -![image-20210822204040165](https://i.loli.net/2021/08/22/Z7koXvl2EKx4SNt.png) - -* 通过服务端访问 - -会进行动态替换 - -![image-20210822204025137](https://i.loli.net/2021/08/22/CeE5Xuxaf7mKt1o.png) - - - -### thymeleaf语法 - -> 标签 - -Thymeleaf通过特殊的标签来寻找属于Thymeleaf的部分,并渲染该部分内容。Thymeleaf也主要通过标签来识别替换对应位置内容,Thymeleaf标签有很多很多,功能也很丰富,这里列举一些比较常用的标签如下: - -| 标签 | 作用 | 示例 | -| :-------- | :----------------- | :----------------------------------------------------------- | -| th:id | 替换id | `` | -| th:text | 文本替换 | `

bigsai

` | -| th:utext | 支持html的文本替换 | `

content

` | -| th:object | 替换对象 | `
` | -| th:value | 替换值 | `` | -| th:each | 迭代 | `` | -| th:href | 替换超链接 | `超链接` | -| th:src | 替换资源 | `` | - -> 链接表达式@{…} - -上面我们已经学习到Thymeleaf是一个基于html的模板引擎,但是我们还是需要加入特定标签来声明和使用Thymeleaf的语法。我们需要在Thymeleaf的头部加Thymeleaf标识: - -```html - -``` - -在Thymeleaf 中,如果想引入链接比如link,href,src,需要使用`@{资源地址}`引入资源。其中资源地址可以static目录下的静态资源,也可以是互联网中的绝对资源。 - -**引入css** - -```html - -``` - -**引入JavaScript:** - -```html - -``` - -**超链接:** - -```html -超链接 -``` - -> 变量表达式: ${...} - -在Thymeleaf中可以通过${…}进行取值,这点和ONGL表达式语法一致。 - -**取普通字符串:** -如果在controller中的Model直接存储某字符串,我们可以直接`${对象名}`进行取值。完整代码如下: - -```html -

普通字符串

- - - - -
-``` - -**取JavaBean对象:** -取JavaBean对象也很容易,因为JavaBean自身有一些其他属性,所以咱们就可以使用`${对象名.对象属性}`或者`${对象名['对象属性']}`来取值,这和JavaScript语法是不是很相似呢!除此之外,如果该JavaBean如果写了get方法,咱们也可以通过get方法取值例如`${对象.get方法名}`完整代码如下: - -```html -

JavaBean对象

- - - - - - - - - - - - - -
介绍
年龄
介绍
-``` - -**取List集合(each):** -因为List集合是个有序列表,里面内容可能不止一个,你需要遍历List对其中对象取值,而遍历需要用到标签:`th:each`,具体使用为` `,其中item就相当于遍历每一次的对象名,在下面的作用域可以直接使用,而userlist就是你在Model中储存的List的名称。完整的代码为: - -```html -

List取值

- - - - -
-``` - -**直接取Map:** -很多时候我们不存JavaBean而是将一些值放入Map中,再将Map存在Model中,我们就需要对Map取值,对于Map取值你可以`${Map名['key']}`来进行取值。也可以通过`${Map名.key}`取值,当然你也可以使用`${map.get('key')}`(java语法)来取值,完整代码如下: - -```html -

Map取值

- - - - - - - - - -
place:
feeling:
-``` - -**遍历Map:** -如果说你想遍历Map获取它的key和value那也是可以的,这里就要使用和List相似的遍历方法,使用`th:each="item:${Map名}"`进行遍历,在下面只需使用`item.key`和`item.value`即可获得值。完整代码如下: - -```html -

Map遍历

- - - - - -
-``` - -> 选择表达式 - -* if-then:`(if) ? (then)` -* if-then-else:`(if) ? (then) : (else)` -* default : `(value) ?: (defaultvalue)` - -> 选择变量表达式: *{...} - -变量表达式不仅可以写成${...},而且还可以写成*{...}。 - -但是,有一个重要的区别:星号语法对选定对象而不是整个上下文评估表达式。也就是说,只要没有选定的对象,美元(`${…}`)和星号(`*{...}`)的语法就完全一样。 - -什么是选定对象?使用`th:object`属性的表达式的结果。就可以选定对象,具体实例如下: - -```html -
-

Name: .

-

Age: 18.

-

Detail: 好好学习.

-
-``` - -当然`*{…}`也可和`${…}`混用。上面的代码如果不使用选定对象,完全等价于: - -```html -
-

Name: .

-

Age: 18.

-

Detail: 好好学习.

-
-``` - -> 消息表达: #{...} - -文本外部化是从模板文件中提取模板代码的片段,以便可以将它们保存在单独的文件(通常是.properties文件)中,文本的外部化片段通常称为“消息”。通俗易懂的来说`#{…}`语法就是用来**读取配置文件中数据**的。在Thymeleaf你可以使用`#{...}`语法获取消息,具体实例代码如下: -首先在templates目录下建立`home.properties`中写入以下内容: - -```properties -bigsai.nane=bigsai -bigsai.age=22 -province=Jiang Su -``` - -在`application.properties`中加入以下内容: - -```properties -spring.messages.basename=templates/home -``` - -这样我们就可以在Thymeleaf中读取配置的文件了,完整代码如下: - -```html -

消息表达

- - - - - - - - - - - - - -
name
年龄
province
-``` - -> 抽取公共页面 - -网页中很多具有公共的部分,可以把公共的部分抽取出来,放在resources/commons/commons.html下 - -* 定义公共部分 - -```html -

- //公共部分 -

-``` - -* 使用公共部分 - -```html -
- -或者用th:replace -``` - - - -## 6.4、扩展SpringMVC - -> 扩展 - -Springboot在自动配置很多组件的时候,先看容器中有没有用户自己配置的,如果有就用用户自己配置的,如果没有就用自动配置的,如果有些组件可以存在多个,比如视图解析器,就将用户配置的和自己默认的组合起来 - -* 编写一个**@Configuration注解类**,类型要为**WebMvcConfigurer**,还**不能标注@EnableWebMve**注解,一般放在config包下、 - -* 重写相应的方法 - -```java -@Configuration -public class MyMvcConfig implements WebMvcConfigurer { - //视图跳转 - @Override - public void addViewControllers(ViewControllerRegistry registry) { - registry.addViewController("/tiaozhuan").setViewName("test"); - } -} -``` - -如上,重写了addViewControllers方法,当浏览器访问:`http://localhost:8080/tiaozhuan`时,会跳转到templates下的test.html页面 - - - -# 7、员工管理系统 - -## 7.1、准备工作 - -> 资源下载 - -[资源下载](https://www.kuangstudy.com/app/code) - -下载完成后放置资源 - -![image-20210822221308597](https://i.loli.net/2021/08/22/KzmHa4GXAy3PrMj.png) - -> 数据 - - - - - - - - - -## 7.2、首页 - -```html - - - - - - - - Signin Template for Bootstrap - - - - - - - - - - - - -``` - -> 首页访问 - -首页`index.html`在templates目录下,不能直接访问 - -通过下面两种方法设置后,可以通过`http://localhost:8080`和`http://localhost:8080/index.html`访问 - -* 方法1:通过增加controller - -```java -@Controller -public class IndexController { - @RequestMapping({"/","/index.html"}) - public String index() { - return "index"; - } -} -``` - - - -* 方法2:通过扩展springmvc - -```java -@Configuration -public class MyMvcCfonfig implements WebMvcConfigurer { - @Override - public void addViewControllers(ViewControllerRegistry registry) { - registry.addViewController("/").setViewName("index"); - registry.addViewController("/index.html").setViewName("index"); - - } -} -``` - -![image-20210823012558877](https://i.loli.net/2021/08/23/SIHr3X4TCnJDx9L.png) - -> 首页Thymeleaf - -所有页面的静态资源都需要使用thymeleaf接管:`th:href=“@{}”` - -使用thymeleaf语法对首页中的链接进行更改 - -```html - - # 略 - - - - - # 略 - -``` - -![image-20210823014357633](https://i.loli.net/2021/08/23/MosWyf5FaOxSzHj.png) - -## 7.3、页面国际化 - -中英文语言切换 - -> 设置idea - -设置为utf-8 - -![image-20210823015246261](https://i.loli.net/2021/08/23/gsAdhG4NM157Fjc.png) - -> 编写国际化配置文件 - -![image-20210823021454836](https://i.loli.net/2021/08/23/noXT1emdif7EILB.png) - - - -* login.properties - -```properties -login.btn=登录 -login.password=密码 -login.remember=记住我 -login.tip=请登录 -login.username=用户名 -``` - -* login_en_US.properties - -```properties -login.btn=sign in -login.password=password -login.remember=remember me -login.tip=please sign in -login.username=username -``` - -* login_zh_CN.properties - -```properties -login.btn=登录 -login.password=密码 -login.remember=记住我 -login.tip=请登录 -login.username=用户名 -``` - -> 配置文件位置 - -国际化自动配置类:MessageSourceAutoConfiguration.class - -在application.properties中配置 - -```properties -# 配置g国际化文件位置 -spring.messages.basename=i18n.login -``` - -> 页面中设置国际化消息Thymeleaf - -Thymeleaf语法:`th:text=“#{}”` - -```html - -

Please sign in

- -

Please sign in

- -``` - -![image-20210823022834729](https://i.loli.net/2021/08/23/fk2Stqug5PNR1mO.png) - - - -> 语言切换 - -国际化语言的切换主要是因为有一个区域信息解析器在其作用。 - -是根据HttpServletRequest中的locale属性来判定启用哪个语言文件的。 - -我们的需求是通过点击链接来切换语言,那么我们可以自定义一个区域信息解析器来替代这个默认的解析器。 - -* 首先,配置类扩展mvc - -```java -@Bean -public LocaleResolver localeResolver(){ - return new NativeLocaleResolver(); -} - -protected static class NativeLocaleResolver implements LocaleResolver{ - - @Override - public Locale resolveLocale(HttpServletRequest request) { - String language = request.getParameter("language"); - Locale locale = Locale.getDefault(); - if(!StringUtils.isEmpty(language)){ - String[] split = language.split("_"); - locale = new Locale(split[0],split[1]); - } - return locale; - } - - @Override - public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) { - - } -} -``` - -* 编写页面 - -```html -中文 -English -``` - -## 7.4、登录 - -> index.html - -```html -