admin 管理员组文章数量: 887019
Linux操作系统
重点在于使用,理论可以几乎忽略
学习原因:
我们一般是在Windows上开发,Linux上去做部署
市面上的常见的操作系统
Windows系列,unix系统,Linux系统,mac系统
开发场景
Windows系列,mac系统
部署场景
unix系统(中大型服务器使用),收费
Linux系统,免费
Windows和Linux的区别(了解)
1、Windows是微软收费闭源的操作系统,Linux是免费开源的一套系统
2、Windows就性能比Linux差,因为Windows内置了太多的应用程序
3、Windows在完全性上来讲,安全性比Linux低,因为木马程序,病毒程序几乎都是exe执行文件,但是exe这类文件在Linux上面无法执行
Linux分类(基础知识)
内核版:指Linux操作系统的内核发布的版本,由李拉斯进行维护
发行版:指例如阿里等这些商业公司,下载内核版,在内核版基础之上开发出的版本
常见的发行版:
CentOS RedHat Ubuntu
我们使用最多的是:RedHat(收费版) CentOS(免费),作为服务器版,使用最多
CentOS常用指令(重点)
Linux的文件类型
以 “-”开头:文本文件和二进制文件(普通文件)
以字母d开头(目录文件)
以字母l开头(链接文件)
设备文件:
分为块设备文件和字符设备文件
块设备文件以字母b开头
字符设备文件以字母c开头
Linux操作系统磁盘
Linux下的一级子目录
/bin 所有二进制命令所在的目录(所有用户)
/boot Linux内核及引导系统程序所需的目录,相当于C盘
/dev 所有设备文件的目录(如声卡、磁盘、光驱)
/etc二进制安装包(yum,rpm)配置文件默认路径,服务启动命令存放目录
/home普通用户的家目录默认数据存放目录
/lib库文件存放目录
/opt自定义软件安装存放目录
/proc进程及内核信息存放目录
/root Root账号的家目录
/sbin系统管理命令存放目录(超级管理员使用的命令)
/tmp临时文件目录,程序运行时产生的临时文件存放目录
/usr系统存放程序的目录(命令和帮助文件)
/var存放内容常变动的文件目录
/media 存放多媒体的目录
简单快捷键:
Ctrl + L 清理屏幕
Ctrl + Alt 退出Vmware 返回Windows操作系统
常见命令
文件操作命令
cd 切换目录
cd /某个文件名 :根目录下的某个文件
cd 某个文件名:当前目录下的某个文件
cd … :返回上一级目录
cd …/…返回上一级目录的上一级目录
ls----list 罗列清单,当前目录下所有非隐藏文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SaDWmnU3-1615995719786)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201109143147500.png)]
ls -a查询目录下的所有文件包括隐藏文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vPiaTHPD-1615995719787)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201109144848058.png)]
ls -a -l详细的查看Linux的某个目录
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3MqhxxuR-1615995719790)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201109161116153.png)]
格式内容:文件类型(权限) 文件数 拥有者 所属组 文件大小 建档日期 文件名
上述的ls -l 可以简写为ll
如果要显示隐藏文件,输入ll -a即可
在ll后接目录名可以查看指定目录名下的文件
pwd :输出当前工作目录
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HOjD84gb-1615995719793)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201109161710273.png)]
mkdir :创建目录(文件夹)
mkdir a :创建a目录
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ubPNjVek-1615995719795)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201109161921372.png)]
mkdir a b:同时创建两个目录 a,b
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bERaUHUU-1615995719795)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201109162007821.png)]
mkdir /root/a 创建root目录下的a目录
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CRUwyjY9-1615995719796)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201109162451765.png)]
递归性创建目录结构 -p
mkdir -p a/b :创建一个目录树,即a与a中的b
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T7vXBNJX-1615995719797)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201109162557984.png)]
删除文件或目录命令
rmdir:删除目录:只能删除目录下面没有目录或文件的
删除目录:rm -rf 目录名
注意:tab键可以补全目录名
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L4bSU9n7-1615995719799)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201109163204037.png)]
删除文件:rm -f 文件的路径
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7Yyo3C0Z-1615995719800)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201109164408693.png)]
clear 清屏命令
cd命令
进入到某一个目录的内部去
语法:cd 目录路径
特殊含义的目录路径
. :当前目录
… :上层目录
…/…/:上层的上层
touch创建文件目录
touch 文件目录路径
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qJJkCkB6-1615995719801)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201109164248056.png)]
复制文件命令
cp 原来文件路径 复制到的文件路径
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EjhMsMir-1615995719802)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201109164723657.png)]
1. cp a.txt b.txt 将当前目录下的文件拷贝到当前目录下,新文件名为b.txt`
2. cp a.txt dir 将当前目录下的a.txt拷贝到dir下`
3. cp a.txt dir/b.txt 将当前目录下的a.txt拷贝到dir下,并命名为b.txt`
4. cp ../a.txt c.txt 将上一级目录下的a.txt拷贝到当前目录下,并命名为c.txt`
mv 剪切命令(移动命令)
mv a.txt b.txt 将a.txt重命名为b.txt
mv a.txt dir 将a.txt移动到dir里
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S2s3rfA2-1615995719803)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201109170846564.png)]
vim文本编辑器
touch 命令 可以创建文件,我们可以通过vim编辑器对内容进行添加
vi 文件路径|vim 文件路径:用vim文本编辑器打开文件
i/a 进入编辑模式
esc 退出编辑模式
: 进入命令模式
:w 保存
:q 退出
:wq 保存切退出
:!q !强制退出
1、先使用vim 命令,编辑某一个文件
2、使用i或者a 进入到编辑模式
3、编辑完毕之后,使用ESC退出编辑
4、使用: 加命令,完成对编辑状态的保存
显示文件内容行号的命令:set nu
隐藏文件内容行号命令:set nonu
cat:查看文件内容
cat 文件路径 用于一次性显示文件的所有内容
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6YOht9LE-1615995719806)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201109172518426.png)]
head -行数 文件名:查看文件的前多少行
tail -行数 文件名:查看文件的后多少行
more 分页查看文件的内容(常用)
不能回看
more 文件路径:
回车:查看下一行
空格:查看下一页
q:表示退出查看
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ht7Sk5oc-1615995719806)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201109172947522.png)]
less命令分页查看文件内容(常用),可以回看
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oucwyIQf-1615995719807)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201110103544307.png)]
less a.txt
PgDn 查看下一页
PgUp 查看前一页
ctrl+d 查看下半页
ctrl+u 查看上半页
回车 下一行
空格 下一页
q 退出Less分页查
g 跳转到文件的开头
G 跳转到文件的结尾
/查找的内容 匹配相应关键字
n 向下匹配关键字
N 向上匹配关键字
管道符 “|”
| 两侧可以放置不同的命令,但是它是将前面命令的执行结果,作为后面命令的输入
例如:
ls -al/etc|less
使用场合:目录中的内容,进行分页查看
grep 分析文件的内容
该命令主要用于分析文件的内容,或用于查找文件中的东西
分析一行信息,如果其中有我们需要的信息,就将该行显示出来
cat aaa/c.txt | grep -n ‘a’:表示找到c.txt文件中包含a所在的行
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fQBPQdBP-1615995719808)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201110105349530.png)]
一般分析出来之后会使用vim对文件进行修改
cat aaa/c.txt | grep -n -c ‘a’ 统计c.txt中包含’a’的行数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VVY1M4HL-1615995719809)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201110105611810.png)]
cat aaa/c.txt | grep -n -v ‘a’ 查看包含a的行的内容
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rz51HYz4-1615995719810)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201110105931513.png)]
cat aaa/c.txt | grep -n -v -c ‘a’ 统计不包含a的行数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dyb52czt-1615995719811)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201110110034812.png)]
帮助命令:查看命令的用法以及选项的详细内容: man 命令
例如:
man cat
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-22LklGHP-1615995719812)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201110110238942.png)]
find 查找文件,文件夹
文件内部的内容,查找方式:可以使用grep
文件|文件的查找,我们就使用find
find模糊查找文件
命令:find 查找范围 -type f -print
作用:查找某个目录下的所有的文件,包含子目录下的所有的文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tQ8gU6V0-1615995719813)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201110113426488.png)]
find 精准查询某个文件或目录
精确查询文件命令:find 查找范围 -name ‘文件名字’ -type f
精确查询目录命令:find 查找范围 -name ‘目录名字’ -type d
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o2GjTj3j-1615995719814)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201110113402355.png)]
通配符查找命令:find 查找范围 -name ‘a*’ -type d
作用:查找当前范围下以a开头的目录
find 模糊查找目录
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C7YtimZz-1615995719816)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201110114020430.png)]
命令:find 查找范围 -type d -print
作用:查找某个目录下的所有的目录包含子目录
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aeJZ2emJ-1615995719816)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201110113454183.png)]
用户的权限(仅次于掌握)
在Linux系统中,有三种类型的用户
分别:
拥有者:文件或目录的创建者
所属组:创建者,或者拥有者所在的团队
其他人:处理拥有者、所属组之外的人,都是其他人
权限:由三个符号表示
r 可读
w 可写
x 可执行
查看用户组信息:
cat /etc/group:打开用户组文件,查看用户组信息
查看拥有者信息:
cart /etc/shadow:当前系统中所有用户的密码信息
查看用户和用户组的匹配信息
cat /etc/passwd :存储当前系统中所有的用户信息
创建用户(了解+)
命令:useradd 用户名
注意:这条命令只能root用户创建
该命令执行后
1.会在/etc/passwd文件中添加xxx用户的信息
2.会在/etc/group文件中添加一个名为xxx的用户组信息
可选参数
-d 设置用户的主目录
eg: useradd -d /home/abc aaa 创建用户aaa,并将/home/abc设置为其主目录
-u 设置userid
eg: useradd -u 666 aaa 创建用户aaa,指定用户id为666
-g 设置组
eg: useradd -g root aaa 创建用户aaa,并将其加入root组
修改用户信息
usermod [选项] 用户名
-l 指定新的用户名
eg: usermod -l abc aaa 将aaa的用户名改为abc
-u 修改userid
eg: usermod -u 999 aaa 将aaa的userid设置为999
-d 修改user的home目录
-g 修改user的组
-L 锁定用户,让其不能再登录
-U 解除锁定
usermod -l userpiao userpu 将userpu的用户名改为userpiao
usermod -g root userpiao 将userpiao的所属组,修改为root所在的所属组
删除用户
userdel [选项] 用户名
-r 删除用户的同时将其home目录也删除
-f 强制删除用户
用户组的新增
命令:groupadd 组名
用户组的修改
命令:groupmod [选项]
-n 修改组名
eg: groupmod -n 新组名 旧组名
-g 修改组id
eg: groupmod -g 新组id 旧组id
用户组的删除
命令:groupdel 组名
给用户设置密码
命令:passwd 用户名
注意:给用户添加密码时不能太简单也不能太短
推荐:数字+大小写字母+_
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OPJ4pigw-1615995719818)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201110142109546.png)]
用户yc密码:yangcan741147
模拟其他用户登陆:
退出系统:exit
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ojJZvhDo-1615995719819)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201110142232076.png)]
注意:使用yc登陆后,默认位置是:自己的home目录
跳出home目录后yc用户是没有操作权限的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OcoyJkEM-1615995719820)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201110142839944.png)]
授权:
d:代表的是目录
rwx:拥有者权限
r_x:所属组的权限
r_x:其他人的权限
授权是root用户的权利,所以需要切换到root
切换用户命令:su 用户名
修改文件或目录的权限
chmod:
rw- 属主的权限,创建这个文件的人的权限
rw- 属组的权限
r-- 其他人的权限
方式一:
chmod u=rwx,g=r–,o=rwx kangyang.txt
方式二:使用= + -符号
chmod -R o+w kangyang.txt
-R 递归修改权限
注意:u代表用户,g代表组,o代表其他人
方式三:在Linux系统中,也可以用数字来表示权限:
4-r 2-w 1-x
chmod 644 文件 u=rw g=r o=r
r:代表读的权限
对于目录:是否可以查看目录下的内容
对于文件:是否可以查看文件的具体内容
w:代表写的权限
对于目录:是否可以在目录中创建新的子目录,是否可以修改目录的名称,是否可以删除目录
对于文件:是否可以针对文件进行追加,修改文件内部的内容
x:代表可执行权限
对于目录:能否进入到目录内部
对于文件:是否可以执行该文件
普通用户,好像是没法删除root用户创建的文件夹;root用户可以删除普通用户创建的文件
压缩和解压缩
在Linux中常见的压缩格式:.zip、.tar、.tar.gz
.tar压缩文件:
压缩命令:tar -cf 压缩名 压缩源文件
显示压缩文件目录:tar -tf 压缩文件名
解压:tar -xf 压缩包的名称
.gz针对tar压缩后的包再次进行压缩
压缩命令:tar -czf 增强压缩的文件名 压缩的目录|文件名称
注意:-czf中的z不可以写在后面,尽量写在第一位
解压命令:tar -zxf 增强压缩的文件名
.zip压缩文件:
-q:快速压缩
-r:递归性压缩
压缩命令:zip -qr 压缩后文件名 原来的文件或目录
-n 解压时,不覆盖原有文件
-d 解压时 ,指定解压的位置
解压命令:unzip -n -d 解压后的目录 压缩文件
关机命令:
注意:不要随便使用,可能关机了就开不了
shutdown -h now
poweroff
重启命令:
shutdown -r now
网络连接(Vmware软件配置)
桥接方式:以物理机的网卡为桥梁,向路由器申请一个新的网络地址
Nat方式:采用vmnet8为网卡,进行与物理机进行地址转换
主机模式:虚拟机只同物理机进行通信,不需要连接外网
桥接模式:
1、修改网络连接方式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L0V5MrnD-1615995719821)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201110163405583.png)]
2、开机并登陆
3、进入到网络连接配置目录
命令:cd /etc/sysconfig/network-scripts/
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PqMiGoxg-1615995719822)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201110163511477.png)]
4、使用vim编辑上述的ifcfg-ens33文件
命令:vim ifcfg-ens33
ONBOOT=yes
BOOTPROTO=“static”
IPADDR=192.168.6.90
GATEWAY=192.168.6.1
DNS1=61.139.2.69
NETMASK=255.255.255.0
注意如果以上存在的修改即可
5、重启网卡
命令:systemctl restart network
如果出现错误Restarting network (via systemctl): Job for network.service failed
关闭网络管理命令:
[root@localhost ~] systemctl stop NetworkManager
[root@localhost ~] systemctl disable NetworkManager
[root@localhost ~]# service network restart
Restarting network (via systemctl): [ OK ]
[root@localhost ~]#
6、检查网络
ping www.baidu
查看是否ping通
Xshell连接工具
实际上就是Linux系统的外在连接工具
文件上传以及下载
通过Xshell完成不同系统之间的文件传输,如果没有Xshell可以使用winCp也可以做到
准备工作
在Linux中执行如下命令:
yum -y install lrzsz
这条命令是让Linux系统支持Xshell来完成文件的上传下载
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mnqqPJSx-1615995719823)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201111103831528.png)]
文件上传
rz 上传命令
sz 文件名 下载命令
Linux操作系统上的软件安装
注意:Linux的服务器上面,几乎不会使用图形界面(保证安全)
Linux中安装软件(镜像源安装-yum)了解
镜像源文件中 已经存在了大量的内置软件包,我们可以使用yum命令进行安装
命令 解析
yum -y install [package] 下载并安装一个rpm包
yum localinstall [package.rpm] 安装一个rpm包,使用你自己的软件仓库解决所有依赖关系
yum -y update 更新当前系统中安装的所有rpm包
yum update [package] 更新一个rpm包
yum remove [package] 删除一个rpm包
yum list 列出当前系统中安装的所有包
yum search [package] 在rpm仓库中搜寻软件包
yum clean [package] 清除缓存目录(/var/cache/yum)下的软件包
yum clean headers 删除所有头文件
yum clean all 删除所有缓存的包和头文件
例如安装jdk1.8:
yum -y install java-1.8.0-openjdk.x86_64
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OfWQLpGl-1615995719825)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201111110553534.png)]
查看自己的安装目录
rpm -ql 包的名字
-ql:表示查询包的安装详细目录
例如查看jdk安装位置:rpm -ql java-1.8.0-openjdk.x86_64
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JIxuotbH-1615995719826)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201111111827237.png)]
举例:安装tomcat
1、搜索包的名称
yum search “tomcat”
2、安装tomcat:
yum -y install tomcat-webapps.noarch
3、查看安装位置
rpm -ql tomcat-webapps
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BHRrVwkB-1615995719827)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201111112658572.png)]
启动tomcat
启动:systemctl start tomcat
关闭:systemctl stop tomcat
重启:systemctl restart tomcat
查看端口的占用情况
netstat -lnpt | grep 8080
查看所有占用端口
netstat -lnpt
通过外部浏览器可以查看tomcat
http://192.168.6.126:8080/
注意ip地址是Linux的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oGeTy3z2-1615995719828)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201111114037481.png)]
如果打不开可能是防护墙的问题
Linux系统的防火墙
检查防火墙状态
systemctl status firewalld
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pq4Bd3Dv-1615995719829)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201111113919331.png)]
改变防火墙的状态
关闭(不推荐):
systemctl status firewalld
启动:
systemctl start firewalld
重启:
systemctl restart firewalld
针对防火墙端口操作
添加端口:让端口配置永久生效
firewall-cmd --permanent --add-port=8080/tcp --permanent
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c3pXniTK-1615995719829)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201111114422281.png)]
添加端口后,需要重新加载端口
firewall-cmd --reload
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Px5CScQC-1615995719831)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201111114510672.png)]
外部浏览器访问结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hD7Rvj4v-1615995719832)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201111114546448.png)]
删除端口
firewall-cmd --permanent --remove-port=8080/tcp --permanent
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IamKEN4y-1615995719833)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201111114633336.png)]
检查防火墙都开了多少个端口
firewall-cmd --list-all
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OvNDuIoc-1615995719834)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201111114749879.png)]
设置防火墙开机启动
systemctl enable firewalld
设置防火墙开机不启动
systemctl disable firewalld
删除某一个yum安装的包
yum remove 包名
yum remove -y mysql-connector-java.noarch
切换镜像源
CentOS 镜像安装包,默认安装包的路径都指向国外,为了提升下载的效率,这个时候我们需要将国外的镜像源修改为国内的
先安装wget工具
yum 0y install wget
进入/etc/yum.repos.d中去
wget工具:它是一个可以从互联网上下载资源的工具
下载阿里镜像源命令:wget http://mirrors.aliyun/repo/Centos-7.repo`
替换本地的镜像源:
在目录中执行备份命令:mv CentOS-Base.repo CentOS-Base.repo.back
再替换下载镜像源的名字
mv Centos-7.repo CentOS-Base.repo
刷新镜像源:
依次执行:
yum clean all //清理所有的之前的安装包的缓存
yum makecache //将现有的新的安装包 进入到缓存中去
yum update //执行刷新
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YEjIx0UN-1615995719835)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201111143954578.png)]
刷新完毕后,我们执行 yum -y install 某个软件时,将会从国内的阿里之间下载
安装软件方式二:下载二进制包(编译后的)安装
例如下载了一个 软件的二进制包
用rz命令上传到usr/local下
解压;tar -zxf apache-tomcat-8.5.59.tar.gz
进入到tomcat的bin目录下
启动tomcat:./startup.sh
关闭tomcat:./shutdown.sh
JavaEE
java三大平台
javaSE 标准版(开发桌面)
javaME 微型版(移动端应用)
JavaEE: 企业版(大型的企业应用)
大型企业应用
1、高并发(支持大量用户同时在线 几万到几十上百万)
2、大数据量(单表的数据可能达到 几千万条)
3、支持分布式事务
4、支持集群,支持负载均衡等一系列的要求
单台tomcat在单位时间能支持 0-500个并发请求
大型企业应用需要的技术
轻量级框架+POJOs
POJOs:
普通Java类,普通的JavaBean
所谓的普通Java类,就是指一个Java类,没有强制性的要求必须要继承什么类或者实现什么接口的说法
POJOs的作用:传输数据,处理业务
重量级与轻量级框架
轻量级框架是相对于重量级框架来定义的
重量级框架(EJB)的特点:
既可以处理业务,又可以传输数据,还可以提供集群支持,也可以提供分布式事务
但是:
1、它提倡面向过程编程
2、它过于依赖某些特殊的API 如:Servlet 继承HttpServlet
3、它过于依赖服务器(对于开发测试部署都非常麻烦)
4、对于代码的侵入式很高
轻量级框架特点:
1、把业务和其他的框架任务进行分离,业务交给POJOs来处理 组件的管理,AOP切面的实现,数据库的访问,这些任务就交给轻量级框架来处理
2、它对我们代码的侵入式不高(甚至有的框架在我们代码中都看不到它的代码)
3、它提倡面向对象编程
4、不依赖于外部特殊服务器,直接使用main函数或者Junit测试框架即可完成功能的测试
轻量级框架
表现层:struts1/2 springmvc 架构模式(mvc)
业务层: (面向对象编程,面向过程编程)
持久层:hibernate Mybatis spring-data-jpa (orm)
spring框架来作为全体把控!!!
spring:整合框架,管理各层组件,维护组件的关系,AOP(事务,日志)
spring衍生的框架:springioc springaop springmvc springboot springcloud
准备工作:
配置maven环境
maven是一个项目管理工具
作用:
1、自动下载jar包,并管理jar包间的依赖关系
2、我们测试,打包,自动部署
maven是apache开源平台上的一个免费开源项目
安装maven
首先,在D:\Program Files 目录下,解压apache-maven-3.6.3-bin.zip
记录bin目录以上的位置:D:\Program Files\apache-maven-3.6.2
配置maven环境变量
在系统变量中,添加M2_HOME
M2_HOME=D:\Program Files\apache-maven-3.6.0-bin\apache-maven-3.6.0
在path中添加
%M2_HOME%/bin
验证是否安装成功
mvn -v
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cR5L7HEX-1615995719837)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201111162846928.png)]
maven的工作原理:
配置本地仓库目录
替换国外的maven源
在settings.xml中mirrors节点中添加如下内容
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>central</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun/nexus/content/groups/public</url>
</mirror>
安装IDEA
JavaBean的编程规范
JavaBean是领域对象,来自于对客户需求的分析
要求:
1、所有的JavaBean都必须实现序列化接口
2、JavaBean中的所有属性,都必须是私有的,并按需提供对应的getter()和setter()
3、如果需要定义有参构造
Java程序的设计原则
开发程序:高内聚、低耦合
如何做到:
满足:7大设计原则,23个设计模式
JavaBean7大设计原则:
单一原则:一个类只干一个事
开闭原则:新需求来了,需要对新功能的扩展是开放的,而对旧功能的修改是关闭的
接口隔离原则:接口最小化原则,设计接口时接口中的方法要尽量的少
依赖倒置原则:层次之间相互依赖,应该是上层依赖下层的抽象,而不是依赖于下层的具体实现:下层应该对上层无知
里氏替换原则:父类出现的任何地方子类都可以替代 子类不要去重写父类已经实现过的方法
迪米特法则:最少知道原则:定义类的时候,只和朋友类(方法的参数,方法的返回,成员变量)进行交互
组合、聚合原则:通常会遇到重复代码的问题, 解决重复的代码:以前是通过继承,但是继承破坏的封装,以及增大了继承的冗余度
解决方案: 使用组合,聚合
Spring
maven项目的目录结构:
注意创键时讲maven换为本地的
src/main/java:用于存放核心代码
src/main/resources:配置文件存放的位置
src/test/java:测试代码
src/test/resources:测试的配置文件存放的位置
每个maven项目中,都有一篇默认的配置文件pom.xml
pom == 项目对象模型 这篇配置文件主要用来描述项目是如何组成的
pom.xml
每个项目都由三部分构成
<!--项目的坐标-->
<groupId>com.springioc</groupId>//叫做组织机构
<artifactId>springIoc</artifactId>//项目名称
<version>1.0-SNAPSHOT</version>//项目当前的版本
<!--全局配置信息-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!--jdk版本-->
<mavenpiler.source>1.8</mavenpiler.source>
<mavenpiler.target>1.8</mavenpiler.target>
</properties>
定义依赖关系
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
<!--
表示该jar包只在测试的时候有用
<scope>test</scope>
表示该jar包只在编译的时候有用
<scope>compile</scope>
表示该jar包在运行时的时候有用
<scope>runtime</scope>
-->
</dependency>
</dependencies>
Mybatis中:#{}与${}的区别:
1、#{}是采用预编译,可以防止SQL注入,而${}采用直接编译,不能防止SQL注入
2、#{}会在参数两侧添加’ ',而${} 则不会添加
凡是用户输入框输入的,统一使用#{},防止SQL注入
Spring:
spring框架03年出来,该框架主要作用,让我们的应用程序满足高内聚,低耦合,并始终遵循面向接口编程的思想,来做到松散耦合,主要解决业务逻辑层和其他各层的松耦合问题
核心
Spring 框架的核心:IOC(容器) 、AOP(面向切面编程)
Spring框架的特点:
1、免费开源,功能不够可以自己去扩展
2、它使用IOC容器管理项目中的所有组件,以及维护组件之间的关系
3、它为企业应用开发,或者互联网应用开发,提供了一套非常完整的解决方案
Spring框架的优点
1、方便程序松散耦合,简化开发
它的底层采用的是:工厂+反射+配置文件来实现
1)它可以帮助程序员去创键组件的实例
2)它可以帮助程序员去管理组件之间的依赖关系
2、AOP编程的支持
提供面向切面编程,可以方便的实现你对程序进行权限拦截,运行监控等功能
3、声明式事务的支持
4、方便测试
5、方便集成其他的优秀框架
6、降低JavaEE 的API使用难度
工厂模式:
Spring体系架构的七大功能模块(面试)
Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式,如图所示。
组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:
**核心容器:**核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转 (IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
**Spring上下文:**Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
**Spring AOP:**通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。
**Spring DAO:**JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
**Spring ORM:**Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
**Spring Web 模块:**Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
**Spring MVC 框架:**MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。
Spring 框架的功能可以用在任何 J2EE 服务器中,大多数功能也适用于不受管理的环境。Spring 的
核心要点是:支持不绑定到特定 J2EE 服务的可重用业务和数据访问对象。毫无疑问,这样的对象可以在不同 J2EE 环境 (Web 或 EJB)、独立应用程序、测试环境之间重用。
Spring核心类
BeanFactory接口
ApplicationContext接口
ApplicationContext接口继承了BeanFactory接口,他们两个的实现类都可以称为Spring的容器
Spring的容器实际上就是一个超大型的工厂,底层实际就是工厂+反射来实现的
BeanFactory提供了容器的所有功能,出现ApplicationContext的原因是,BeanFactory在产生实例的时候,是调用getBean()方法时才产生实例,而ApplicationContext在创键容器实例的时候,就开始初始化创键所有的组件的实例
我们一般用得更多的是ApplicationContext作为容器
ApplicationContext除了实现了BeanFactory所有功能外,还扩展了其他功能:支持i18n(国际化) 支持任务调度、支持邮件服务,支持WebSocket
ApplicationContext接口
ClassPathXmlApplicationContext、FileSystemXmlApplicationContext、AnnotationConfigApplicationContext.……
上述的都是 ApplicationContext 的实现类
区别:
ClassPathXmlApplicationContext 使用相对路径加载applicationContext.xml配置文件
FileSystemXmlApplicationContext使用绝对路径加载applicationContext.xml配置文件
AnnotationConfigApplicationContext 提供注解支持
但是他们在管理组件,和维护组件上都是一样的
在代码中如何启动容器
开发步骤(使用Spring容器)
1、创键一个maven项目
2、导入spring依赖
3、定义需要的JavaBean
4、针对业务定义三层的各层接口,并编写接口中的方法
5、针对接口编写实现类,并完成面向接口编程
6、编写applicationContext.xml配置文件,在配置文件中使用bean标记来申明需要被容器管理的组件
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework/schema/beans"
xmlns:xsi="http://www.w3/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework/schema/beans
https://www.springframework/schema/beans/spring-beans.xsd">
<bean id="userService" class="com.woniuxy.springIoc.service.impl.UserServiceImpl"></bean>
<bean id="userController" class="com.woniuxy.springIoc.controller.UserController">
<property name="userService" ref="userService"></property>
</bean>
</beans>
其中id就是组件在容器中唯一表示,class就是组件在容器的类的全路径
7、测试
IOC(控制反转)(面试)
如果没有Spring容器,程序在使用某些组件的实例时,需要自己创键,也就是创建组件实例的控制器是一种主动关系,但是有了Spring容器之后,现在就是有容器来创建,创建好了之后,再传递给我们,程序现在的创建对象的控制器,已经由之前的主动创建,被反转为:被动接收
这就被称为:控制反转
控制反转的好处:
1、降低程序之间的耦合,由容器帮我们松散了组件之间的耦合关系
2、可以让我们可以真正的做到,面向抽象编程(依赖倒置原则)
正是靠这种反转机制,Spring容器才可以做到:”松散程序间的耦合“
DI(依赖注入)(面试)
DI和IOC实际上是一回事,没有区别,至少各自的描述不同
依赖注入:如果没有Spring容器的情况下,程序在完成某项业务时,需要直接依赖某一个实例,有了Spring容器之后,依赖关系将由直接依赖实例,编程依赖容器,让容器去创建实例,并将实例重新注入到程序的过程。
IOC和DI的区别(面试题)
实际上没有区别,至少关注点不同,IOC关注的是创建对象的控制权(由主动变为被动)
DI关注的是依赖关系(由直接依赖对象,变为依赖Spring容器)
(依赖注入的两种方式)容器将实例传递给程序的两种方式
设值注入
设置注入,实际上就是 让你提供setter() ,由Spring容器调用该方法,将容器中的实例注入到程序中来
具体操作方式:
1、给属性提供set方法
2、在spring容器的配置文件中,使用property标记完成组件之间的装配
构造注入
需要程序中提供构造器,Spring容器通过调用构造器的方式,给属性进行赋值
具体代码:
1、在程序中创建一个有参构造
2、在spring容器的配置文件中,使用constructor-arg来给属性进行赋值
构造注入和set注入的区别(面试题)
1、注入原理不同,一个是通过set方法来装配,一个是通过构造器来装配
2、设值注入是先产生对象再注入值,构造注入产生对象就把参数带上了
两种没有谁好谁坏,只是使用的场合不同
1、设置注入通常容易被Java程序员接收而已
2、如果对注入顺序有严格要求,就比较适合构造注入
3、如果对创建对象的顺序也有要求的话,也比较适合构造注入
单例模式
单例模式,使用场景:当程序中需要一个类只具备唯一实例的时候,就可以使用这种模式
懒汉模式
是在调用类的方法的时候,才产生实例
package com.designmode.singleton;
/**
* 单例模式
* 懒汉模式
* 在用的时候才创建
*/
public class Hunger {
private static Hunger hunger;
private Hunger(){}
public static Hunger getInstance(){
if (hunger!=null){
return hunger;
}else {
synchronized (Hunger.class){
//这一重循环的意思是:如果有同时有两个线程,一个在外面一个在代码块里面,
// 当里面的执行完后面的也会跟着进,如果没有这个判断就会重复创建对象
if(hunger==null){
hunger=new Hunger();
}
}
}
return hunger;
}
}
饿汉模式
在类加载的时候,实例本身就已经产生了
package com.designmode.singleton;
/**
* 单例模式
* 饿汉模式
* 绝对的线程安全,类加载时就已经创建了对象
*/
public class Lazy {
private static Lazy lazy=new Lazy();
private Lazy(){
}
public static Lazy getLazy(){
return lazy;
}
}
克隆模式(原型模式)
使用场景:当一个对象拥有了大量的属性,并且属性都有值,现在需要大量相同类型的对象出来
克隆模式:以某一个东西为原型,批量产生新的对象的方式
克隆模式,除了产生新的对象,还会复制对象的数据到新的对象身上
具体实现:需要实现一个Cloneable的接口重写clone方法
浅克隆:关联对象不实现一个Cloneable的接口重写clone方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nUnCqCjW-1615995719844)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201113123915819.png)]
由结果可以分析,关联对象并没有被克隆,只是克隆了对象本身
深克隆:关联对象实现一个Cloneable的接口重写clone方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-54385qB7-1615995719845)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201113124516513.png)]
出来克隆对象本身关联对象也会被克隆
Spring容器创建的实例的生命周期(作用域)
Spring容器支持的作用域类型:
1、singleton(重点):单实例(默认) Spring容器默认产生组件时,组件在容器中就只有一个唯一的实例
组件实例的整个生命周期,都是由Spring容器来负责,容器在创建时就产生组件的实例,容器在销毁时,组件的实例根据销毁
2、prototype(重点):原型模式(克隆模式):以单实例为原型,克隆出新的实例
组件的实例,由Spring容器创建,但是创建后组件的使用,以及何时销毁,Spring容器是不负责,由程序员负责(JVM垃圾回收)
<bean id="studentController" class="com.woniuxy.controller.StudentController" scope="prototype">
<constructor-arg name="studentService" ref="studentServiceImpl"></constructor-arg>
</bean>
3、request(了解):组件的实例与HttpRequest有关,它能做到在同一个HttpRequest范围内组件的实例只有一个,即不同的HttpRequest拥有不同的组件实例
4、session(了解):组件的实例与HttpSession有关,它能做到在同一个会话范围内组件的实例只有一个,即不同的会话拥有不同的组件实例
5、application(了解):组件的实例与ServletContext有关,它能做到在同一个ServletContext范围内组件的实例只有一个,ServletContext的生命周期通常和web服务器有关
6、webSocket(了解):组件的实例与webSocket有关,它能做到在同一个webSocket范围内组件的实例只有一个
自动装配
通过程序员自定义组件之间的关系,我们叫手动装配
但是这种方式很麻烦
自动装配的方式
byName :Spring容器在产生组件实例时,如果发现该组件需要装配其他组件,并且装配方式定义byName ,Spring容器将会查找属性的所有名字,回到Spring容器中问询是否存在如果存在则正常装配,如果不存在,Spring容器也不会抛出异常,但是在代码执行的时候程序会抛出空指针异常
<bean id="studentController" class="com.woniuxy.controller.StudentController" autowire="byName">
</bean>
注意byName需要给属性定义set方法
byType:Spring容器在产生组件实例时,如果发现该组件需要装配其他组件,并且装配方式定义byType,Spring将会查找所有属性的类型,回到Spring容器问询是否有这种类型的实例存在,如果找到一个就装配,如果有多个将抛出异常,如果一个都没有找到,容器不好抛出任何异常,但是程序执行的时候也会抛出空指针异常
注意byType需要给属性定义set方法
<bean id="studentController" class="com.woniuxy.controller.StudentController" autowire="byName">
</bean>
constructor(构造装配):调用构造器来完成组件的装配,在装配时,构造参数按照ByType的模式来进行!
一定要提供构造器!
<bean id="studentController" class="com.woniuxy.controller.StudentController" autowire="constructor">
</bean>
no、default:就是手动装配
注解
通过配置文件声明组件的方式,如果组件少还好说,如果组件超过1000个
就会增加编程的难度
约定优于配置
程序中通过接口,注解定义约定
配置既需要维护Java代码,又需要维护配置代码,而注解,只需要维护Java代码!
注解的作用:
1、注解具有注释的作用,解释代码
2、和第3方达成一定的约定 :
@Override和 编译器达成预定,说这是一个重写的方法
@Deprecated 和 编译器达成预定,说这是一个过时的方法
3、注解中的代码,可以在程序中再次使用
Spring框架的常用注解
@Component
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Indexedpublic @interface Component { String value() default "";}
该组件:通常用于普通Java类身上,表示这是一个需要被Spring容器进行管理的组件
@Componentpublic class StudentServiceImpl implements IStudentService{} ===<bean id="studentService"class="com.woniuxy.service.impl.StudentServiceImpl"></bean>
@Controller
用于描述表现层的类,说这个类被Spring容器进行管理
效果===@Component
@Service
用于描述业务层的类,说这个类被Spring容器进行管理
效果===@Component
@Repository
用于描述持久层的类,说这个类被Spring容器进行管理
效果===@Component
使用注解的条件
开启容器的自动扫描功能
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework/schema/beans"
xmlns:xsi="http://www.w3/2001/XMLSchema-instance"
xmlns:context="http://www.springframework/schema/context"
xsi:schemaLocation="http://www.springframework/schema/beans
https://www.springframework/schema/beans/spring-beans.xsd
http://www.springframework/schema/context
https://www.springframework/schema/context/spring-context.xsd">
<!-- 开启容器的自动扫描功能-->
<context:component-scan base-package="com.woniuxy"></context:component-scan>
</beans>
base-package 基准包: 以此包为基础,扫描该包以及所有子包 Java类是否使用@Component @Service @Controller @Repository 这种Java类就需要被纳入到Spring容器中
默认会将类的类名,首字母小写作为类的实例,在容器中的ID
可以通过下面的方法来查看:
context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
System.out.println(Arrays.toString(context.getBeanDefinitionNames()));
想取名字,就自己写
@Controller("sc")
@Autowired
自动装配,由Spring框架提供,它装配规则是:先byType 再byName
@Resource
自动装配,由Spring框架提供,它装配规则是:先按照名称装配 如果不行再按照类型装配,但是如果指定name属性那么就只能按照名称装配
上述两个注解不需要写get、set可以直接加注解就行
Spring兼容Junit框架
第一步
在pom.xml 文件中,导入Spring兼容Junit框架的兼容包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
第二步,修改测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class GoodsServiceImplTest {
private ApplicationContext context;
@Autowired
private GoodsController goodsController;
@Test
public void insertGoods() {
Goods goods=new Goods(1,"电脑");
goodsController.insertGoods(goods);
}
}
@RunWith(SpringJUnit4ClassRunner.class) //在JUnit单元测试,启动一个Spring容器
@ContextConfiguration(value={“classpath:applicationContext.xml”}) //将applicationContext.xml 交给上述的Spring容器
@Scope 作用域注解
@Scope(value="prototype")
@Controller
public class StudentController { ……}
该注解的作用是:修改组件的作用域范围,默认是:singleton
拓展两个不常用的注解(了解)
@PostConstruct 组件实例化时,需要执行的初始化代码
@PreDestroy 组件实例在销毁时,需要执行的souwei
这两个写在任何由Spring管理的组件的方法上都可以
/**
* 组件实例化时,需要执行的初始化代码
* 替代的是 实例初始化块 { }
*/
@PostConstruct
public void init(){
System.out.println("妈妈,我准备好了!!!!");
}
/**
* 组件实例在销毁时,需要执行的收尾代码
*/
@PreDestroy
public void destroy(){
System.out.println("妈妈,我出门了!!!!");
}
AOP(面向切面编程)
代理模式
使用场景:如果想隐藏自己,让其他类帮我们去做任务的情况下,我们就可以使用它
分类
静态代理
看得到代理类的代理模式
package com.woniuxy.proxy.statics;
public interface IProxy {
void sendLetter();
}
package com.woniuxy.proxy.statics;
/**
* 真实对象
*/
public class RealObject implements IProxy{
private GrilBean grilBean;
public RealObject() {
}
public RealObject(GrilBean grilBean) {
this.grilBean = grilBean;
}
/**
* 给女孩送信
*/
public void sendLetter(){
System.out.println("给[" + grilBean.getGirlName() + "]" + "送信!!!");
}
public void getLetter(){
System.out.println("亲爱的["+grilBean.getGirlName()+"],我喜欢你!!!");
}
}
package com.woniuxy.proxy.statics;
/**
* 代理对象
*/
public class ProxyObject implements IProxy{
private GrilBean grilBean;
private RealObject realObject;
public ProxyObject(GrilBean grilBean) {
this.grilBean = grilBean;
//代理对象,肯定认识 真实对象
realObject = new RealObject(grilBean);
}
/**
* 代理对象的送信行为
*/
@Override
public void sendLetter() {
realObject.getLetter();
//送信之前
System.out.println("将信的署名修改为代理对象的!!!!");
//送信
realObject.sendLetter();
//送信之后
}
}
package com.woniuxy.proxy.statics;
import java.io.Serializable;
/**
* 目标对象
*/
public class GrilBean implements Serializable {
private String girlName;
public GrilBean() {
}
public GrilBean(String girlName) {
this.girlName = girlName;
}
public String getGirlName() {
return girlName;
}
public void setGirlName(String girlName) {
this.girlName = girlName;
}
}
package com.woniuxy.proxy;
import com.woniuxy.proxy.statics.GrilBean;
import com.woniuxy.proxy.statics.IProxy;
import com.woniuxy.proxy.statics.ProxyObject;
public class Main {
public static void main(String[] args) {
// write your code here
GrilBean grilBean = new GrilBean("冯程程");
//将女孩交给代理对象
IProxy proxy = new ProxyObject(grilBean);
//代理对象给它送信
proxy.sendLetter();
}
}
动态代理
看不到代理类的代理模式
JDK动态代理:
当目标类有实现接口时使用
package com.woniuxy.proxy.auto.jdk;
public interface IUserService {
void saveUserBean();
void updateUserBean();
}
package com.woniuxy.proxy.auto.jdk;
public class UserServiceImpl implements IUserService{
@Override
public void saveUserBean() {
System.out.println("我在做保存!!!!");
}
@Override
public void updateUserBean() {
System.out.println("我在做更新!!!!");
}
}
package com.woniuxy.proxy.auto.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkProxyFactory {
private Object target;
public void setTarget(Object target) {
this.target = target;
}
public Object getProxyIntance(){
//返回一个目标对象的,代理对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
//接口方法,在调用时,如何实现
new InvocationHandler() {
/**
*
* @param proxy 代理对象
* @param method 正在执行的方法
* @param args 方法的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//调用代理对象执行目标方法
return method.invoke(target,args);
}
});
}
}
package com.woniuxy.proxy.auto.jdk;
public class MainEnter {
public static void main(String[] args) {
JdkProxyFactory proxyFactory = new JdkProxyFactory();
//真实对象
IUserService userService = new UserServiceImpl();
proxyFactory.setTarget(userService);
//产生一个代理对象
IUserService proxyInstance = (IUserService)proxyFactory.getProxyIntance();
//通过代理对象,调用代理方法
proxyInstance.saveUserBean();
proxyInstance.updateUserBean();
//返回对象的类的父类
System.out.println(userService.getClass().getSuperclass());
System.out.println(proxyInstance.getClass().getSuperclass());
}
}
注意:JDK代理,是属于接口代理模式,真实类身上一定要有实现接口;代理类和真实类之间,就是兄弟关系!!!
CGLIB:当目标类没有实现接口时使用
cglib代码,它不再是接口那种兄弟代理方法,它采用的是 子类代理
cglib 的功能,在spring-core.jar包中有完整的实现,所以需要引入
<!--导入spring框架的依赖包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
package com.woniuxy.springaop.bean;
public class UserBean {
}
package com.woniuxy.springaop.service.impl;
import com.woniuxy.springaop.bean.UserBean;
public class UserServiceImpl {
/**
* 新增用户
* @param userBean
*/
public void saveUserBean(UserBean userBean){
System.out.println("saveUserBean()" + userBean);
}
/**
* 修改用户
* @param userBean
*/
void updateUserBean(UserBean userBean) {
System.out.println("updateUserBean()" + userBean);
}
}
package com.woniuxy.springaop.proxy.cglib;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxyFactory implements MethodInterceptor {
/**
* 目标对象
*/
private Object target;
public void setTarget(Object target) {
this.target = target;
}
public Object getProxyInstance(){
//1. 工具类
Enhancer en = new Enhancer();
//2. 设置父类
en.setSuperclass(target.getClass());
//3. 设置回调函数
en.setCallback(this);
//4. 创建子类(代理对象)
return en.create();
}
/**
*
* @param o 代理对象
* @param method 目标方法
* @param objects 目标参数
* @param methodProxy 代理方法
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//目标方法执行之前(操作事务,记录日志……)
//代理方法正式执行
Object obj = methodProxy.invoke(target,objects);
//放行,给该类授权,授权可以访问其他类的非 public元素
method.setAccessible(true);
//目标方法执行之后
return obj;
}
}
package com.woniuxy.springaop.service.impl;
import com.woniuxy.springaop.bean.UserBean;
import com.woniuxy.springaop.proxy.cglib.CglibProxyFactory;
import org.junit.Test;
import static org.junit.Assert.*;
public class UserServiceImplTest {
@Test
public void saveUserBean() {
UserServiceImpl userService = new UserServiceImpl();
CglibProxyFactory proxyFactory = new CglibProxyFactory();
proxyFactory.setTarget(userService);
//产生代理实例
UserServiceImpl proxyInstance = (UserServiceImpl)proxyFactory.getProxyInstance();
//调用代理实例的方法
UserBean userBean = new UserBean();
proxyInstance.saveUserBean(userBean);
}
@Test
public void updateUserBean() {
}
}
JDK代理和CGLIB代理的区别(面试题)
1、两种代理的原理不一样,JDK代理是属于接口代理,代理类和真真实类之间的关系是,朋友关系;而CGLIB代理是属于子类代理,代理类和真实类之间的关系是:父子关系(真实类是父,代理是子)
2、CGLIB是通过ASM这种字节码处理框架ASM来转换字节码并生成新的类,而JDK 是通过JVM底层在内存中根据接口,动态产生一个代理实例出来
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zHDqkqqQ-1615995719847)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201116140206675.png)]
代理模式的作用
1、隐藏真实对象本身
2、给真实对象的方法,执行之前,可以增强一些功能
AOP(面向切面编程)
我们之前所写的代码,一般是从上至下依次执行,也就是我们之前一般纵向的关注代码
而aop是横向的关注代码
AOP最大的作用:就是采用代理模式,将核心业务,与非核心业务进行分离该关注,核心业务,我们采用纵向业务,而非核心业务,我们采用横向关注
交叉业务:不同的功能模块中,都拥有的业务(几乎都是非核心功能)
常见的概念:
1、切面 又叫方面 每个切面实际上就是我们的非核心功能,一般一个非核心功能一个切面、
2、切入点,切面根据条件,能够适配的功能点,都切面的切入点。这个切入点可以是一个方法,也可以是方法的结束,甚至可以是方法的异常
3、连接点:切面正式进入到某个功能点上的切入点时,就叫连接点
4、通知,也叫增强 定义在切面上,给目标方法执行之前,或执行之后,需要新增的方法,也就是提供在切面上的功能(前置通知,后置通知,后置返回通知,后置异常通知,环绕通知)
5、目标对象:就是指被切面需要增强的对象
6、代理对象:就是AOP切面动态给目标对象产生出来的代理实例,这个实例往往采用JDK代理和CGLIB代理产生出的(目标对象有接口的采用JDK,目标对象没有接口的采用CGLIB动态代理)
7、织入 :AOP切面应用到代理对象的过程,就叫织入
Spring提供切面的两种方式
Spring自带的Schema-based 基于配置文件(了解)
Spring自带的切面技术,1、需要实现1套接口,2、编写配置文件
非常的麻烦
<dependencies>
<!--导入spring框架的依赖包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
<scope>runtime</scope>
</dependency>
<!--spring兼容Junit4-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.9.RELEASE</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>runtime</scope>
</dependency>
</dependencies>
package com.woniuxy.springaop.bean;
public class GoodsBean {
}
package com.woniuxy.springaop.bean;
public class UserBean {
}
package com.woniuxy.springaop.service;
import com.woniuxy.springaop.bean.UserBean;
public interface IUserService {
/**
* 新增用户
* @param userBean
*/
void saveUserBean(UserBean userBean);
/**
* 修改用户
* @param userBean
*/
void updateUserBean(UserBean userBean);
}
package com.woniuxy.springaop.service;
import com.woniuxy.springaop.bean.GoodsBean;
public interface IGoodsService {
void saveGoodsBean(GoodsBean goodsBean);
}
package com.woniuxy.springaop.service.impl;
import com.woniuxy.springaop.bean.GoodsBean;
import com.woniuxy.springaop.service.IGoodsService;
import org.springframework.stereotype.Service;
@Service
public class GoodsServiceImpl implements IGoodsService {
@Override
public void saveGoodsBean(GoodsBean goodsBean) {
System.out.println("saveGoodsBean()" + goodsBean);
}
}
package com.woniuxy.springaop.service.impl;
import com.woniuxy.springaop.bean.UserBean;
import com.woniuxy.springaop.service.IUserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements IUserService {
/**
* 新增用户
* @param userBean
*/
public void saveUserBean(UserBean userBean){
System.out.println("saveUserBean()" + userBean);
}
/**
* 修改用户
* @param userBean
*/
public void updateUserBean(UserBean userBean) {
System.out.println("updateUserBean()" + userBean);
}
}
配置文件的内容:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework/schema/beans"
xmlns:xsi="http://www.w3/2001/XMLSchema-instance"
xmlns:context="http://www.springframework/schema/context"
xmlns:aop="http://www.springframework/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启容器的自动扫描-->
<context:component-scan base-package="com.woniuxy"></context:component-scan>
<!--配置一个切面-->
<aop:config>
<!--execution(public * com.woniuxy.springaop.service.impl.UserServiceImpl.*(..) throws NullPointerException)
切入点的条件:
方法必须是public 修饰的,返回类型无所谓,
必须是com.woniuxy.springaop.service.impl.UserServiceImpl下的方法
必须要抛出throws NullPointerException
-->
<aop:pointcut id="poincutOne" expression="execution(* com.woniuxy.springaop.service.impl.*ServiceImpl.*(..))"/>
<aop:advisor advice-ref="beforAdvice" pointcut-ref="poincutOne"></aop:advisor>
<aop:advisor advice-ref="afterRuturningAvice" pointcut-ref="poincutOne"></aop:advisor>
</aop:config>
</beans>
测试代码:
package com.woniuxy.springaop.service.impl;
import com.woniuxy.springaop.bean.GoodsBean;
import com.woniuxy.springaop.service.IGoodsService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value={"classpath:applicationContext.xml"})
public class GoodsServiceImplTest {
@Resource
private IGoodsService goodsServiceImpl;
@Test
public void saveGoodsBean() {
GoodsBean goodsBean = new GoodsBean();
goodsServiceImpl.saveGoodsBean(goodsBean);
}
}
package com.woniuxy.springaop.service.impl;
import com.woniuxy.springaop.bean.UserBean;
import com.woniuxy.springaop.service.IUserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* 启动测试类
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value={"classpath:applicationContext.xml"})
public class UserServiceImplTest {
@Autowired
private IUserService userServiceImpl;
@Test
public void saveUserBean() {
UserBean userBean = new UserBean();
userServiceImpl.saveUserBean(userBean);
}
@Test
public void updateUserBean() {
UserBean userBean = new UserBean();
userServiceImpl.updateUserBean(userBean);
}
}
Spring兼容AspectJ(掌握)
需要使用第三方的切面技术,AspectsJ
推荐原因:1、不需要继承或实现任何接口,2、提倡使用注解
实现步骤:导入依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.6</version>
</dependency>
2、开启切面的动态代理支持
<!--开启aop切面支持-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
3、定义切面类
package com.springaop.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Arrays;
@Component
@Aspect
public class MyAspect {
/**
* 切入点
*/
@Pointcut("execution(* com.springaop.service.impl.*ServiceImpl.*(..))")
public void pointcut(){}
/**
* 前置通知
* @param joinPoint:表示连接点,可以获取到目标对象以及目标方法的信息
*/
@Before(value = "pointcut()")
public void beforeAdvice(JoinPoint joinPoint){
System.out.println("前置通知执行了");
String name = joinPoint.getSignature().getName();
System.out.println("目标方法是:"+name);
Object target = joinPoint.getTarget();
System.out.println("目标对象是:"+target);
Object[] args = joinPoint.getArgs();
System.out.println("目标参数是:"+ Arrays.toString(args));
}
/**
* 后置返回通知
* @param joinPoint
* @param result:目标方法的返回值
*/
@AfterReturning(value = "pointcut()",returning = "result")
public void afterReturningAdvice(JoinPoint joinPoint,Object result){
System.out.println("后置返回通知执行了");
System.out.println("目标方法的返回值是"+result);
}
/**
* 后置异常通知
* @param joinPoint
* @param throwable:目标方法所抛出的异常
*/
@AfterThrowing(value = "pointcut()",throwing = "throwable")
public void afterThrowsAdvice(JoinPoint joinPoint,Exception throwable){
System.out.println("异常通知执行了");
System.out.println("异常名称是:"+throwable.getMessage());
}
@After(value = "pointcut()")
public void afterAdvice(JoinPoint joinPoint){
System.out.println("这里是后置通知,执行在目标方法,结束之前");
}
/**
* 环绕通知 约等于 Before+After
* 能力:1、可以修改目标方法的参数
* 2、修改目标方法执行的返回
* 3、可以控制目标方法是否执行
*
* @param proceedingJoinPoint
* @return
*/
@Around(value = "pointcut()")
public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("我是环绕通知,我约等于:@Before + @After");
Object[] args = proceedingJoinPoint.getArgs();
args[0]=null;//修改第一个参数为null
//相当于method.invoke
Object obj = proceedingJoinPoint.proceed(args);
obj=1;//所有方法返回值固定为1
return obj;
}
}
环绕通知(了解):
环绕通知是五种通知类型中最强大的一种,因为它可以:修改参数,修改返回,甚至可以决定目标方法是否需要继续执行
它越等于Before+After
记住:能用Before+After来干的事,千万不要使用Around
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a1YqXSPu-1615995719848)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201117103652856.png)]
aop在项目中的应用(面试题):
事务控制,日志记录,异常处理,敏感词过滤
切入点表达式的格式(带?是指可以省略)
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
modifiers-pattern 访问修饰符 可以省
ret-type-pattern 返回类型 不能省略
declaring-type-pattern 类的类路径 可以省略
name-pattern 方法的名称 不能省略
param-pattern 参数列表 不能省略
throws-pattern? 异常列表 可以省略
Spring整合Mybatis
好处;业务层+持久层就可以整合起来了
以Spring框架为主
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w8WtZg2d-1615995719849)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201117105124002.png)]
准备工作:
1、明确需要的依赖
spring、mybatis、MySQL、junit、lombok、log4j
整合:
1、导入依赖关系
<dependencies>
<!--导入spring框架的依赖包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
<scope>runtime</scope>
</dependency>
<!--aspectj-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.6</version>
</dependency>
<!--spring兼容Junit4-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.9.RELEASE</version>
<scope>runtime</scope>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<!--mybatis整合spring-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.3</version>
</dependency>
<!--数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--日志信息-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
2、准备项目包结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lY0sCP3i-1615995719851)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201117112219727.png)]
3、编写Spring框架的配置文件(重点)applicationContext.xml
Spring框架配置文件的步骤
1、配置数据源连接池 提供连接
数据源:就是数据的来源,来源可能很多:数据库,NOSQL,Excel,其他系统,我们通常指的是:关系型数据库
商业连接池
C3P0,DBCP(apache)、Druid
DBCP的配置:
1)引入DBCP的依赖
<dependency>
<groupId>org.apachemons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.7.0</version>
</dependency>
2)创建jdbc.properties
jdbc.username=root
jdbc.password=123456
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/sm?serverTimezone=CTT
jdbc.max=30
3)在applicationContext中配置数据源连接池的相关信息
<!--配置加载jdbc.propertiesSpring容器-->
<context:property-placeholder location="jdbc.properties"/>
<!--配置dataSource,数据库连接池-->
<bean id="dataSource" class="org.apachemons.dbcp2.BasicDataSource">
<!--配置连接数据库的基本信息-->
<property name="url" value="${jdbc.url}"/>
<property name="password" value="${jdbc.password}"/>
<property name="username" value="${jdbc.username}"/>
<property name="driver" value="${jdbc.driver}"/>
<!--额外的配置-->
<!--连接池创建连接数的初始容量-->
<property name="initialSize" value="5"/>
<!--设置连接池中最大连接数-->
<property name="maxTotal" value="50"/>
<!--设置连接池中空闲区的连接最大空闲数-->
<property name="maxIdle" value="10"/>
<!--设置是否支持预编译-->
<property name="poolPreparedStatements" value="true"/>
<!--设置预编译对象默认为20个-->
<property name="maxOpenPreparedStatements" value="20"/>
<!--创建连接时校验连接是否有效-->
<property name="testOnCreate" value="true"/>
<!--设置校验句-->
<property name="validationQuery" value="select now() from dual"/>
<!--取消自动提交-->
<property name="defaultAutoCommit" value="false"/>
<!--取消连接只读-->
<property name="defaultReadOnly" value="false"/>
</bean>
2、配置SessionFactory 提供SqlSession
SqlSessionFactory :根据数据源产生对象
<!--配置session工厂-->
<bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--配置数据源-->
<property name="dataSource" ref="dataSource"/>
<!--给JavaBean配置别名:放置的是JavaBean所在的目录-->
<property name="typeAliasesPackage" value="com.springmybatis.bean"/>
<!--配置开启延迟加载-->
<property name="configuration">
<bean class="org.apache.ibatis.session.Configuration">
<!--开启延迟加载-->
<property name="lazyLoadingEnabled" value="true"/>
<!--修改积极的加载方式为延迟的-->
<property name="aggressiveLazyLoading" value="false"/>
</bean>
</property>
</bean>
3、配置MapperScanner 扫描SQL映射文件
MapperScanner :扫描mapper.xml文件
<!--配置MapperScanner扫描器-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--放置Mapper接口以及配置文件所在路径,如果有多个路径,可以使用,或者分号进行分隔-->
<property name="basePackage" value="com.springmybatis.mapper"/>
<!--将映射文件和session工厂进行连接,通过jdk代理为mapper接口动态产生代理类-->
<property name="sqlSessionFactoryBeanName" value="sessionFactory"/>
</bean>
4、配置事务管理器
事务管理器:一个专门用来管理事务的容器/工具
学习阶段,我们采用dao手动的控制事务,但是这种方式没办法保证整个业务逻辑是处于事务控制下的,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EH6R6c45-1615995719852)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201117151640709.png)]
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--配置数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
5、使用AOP切面技术管理事务 控制事务的管理范围
(1)使用配置文件
<!--定义切面上的通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--配置事务属性-->
<tx:attributes>
<!--给具体方法配置事务属性-->
<tx:method name="*" read-only="true"/>
<tx:method name="save*" rollback-for="java.lang.Exception" isolation="REPEATABLE_READ" propagation="REQUIRED"></tx:method>
<tx:method name="add*" rollback-for="java.lang.Exception" isolation="REPEATABLE_READ" propagation="REQUIRED"></tx:method>
<tx:method name="insert*" rollback-for="java.lang.Exception" isolation="REPEATABLE_READ" propagation="REQUIRED"></tx:method>
</tx:attributes>
</tx:advice>
<!--配置aop-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.springmybatis.service.impl.*ServiceImpl(..))"/>
<!--配置增强器-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"></aop:advisor>
</aop:config>
(2)
编写代码,测试代码
安装Lombok插件
本地磁盘安装,安装的包是:
lombok-plugin-0.30-2020.1.zip
在settings -> plugins - > 设置 -> install plugin from disk
安装好后,重启Idea工具
在pom.xml 中引入 lombok插件
<dependency>
<groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> <scope>provided</scope>
</dependency>
@Data 该注解定义在JavaBean上,作用:给JavaBean产生getter() setter()
无参构造器,tostring() hashcode() equals()
@NoArgsConstructor 产生无参构造器
@Getter 产生 getter()
@Setter 产生 setter()
@ToString 产生toString()
@RequiredArgsConstructor + @NonNull 可以用来定义有参构造器
声明式事务的处理方案2
1)导入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
2)在applicationContext.xml中加入
<!--开启注解事务支持-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
3)在ServiceImpl中
package com.springmybatis.service.impl;
import com.springmybatis.bean.UserBean;
import com.springmybatis.mapper.UserMapper;
import com.springmybatis.service.IUserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
@Transactional(readOnly = true)
@Service
public class UserServiceImpl implements IUserService {
@Resource
private UserMapper userMapper;
@Transactional(isolation = Isolation.REPEATABLE_READ,rollbackFor = Exception.class)
@Override
public void saveUserBean(UserBean userBean) {
userMapper.saveUserBean(userBean);
}
@Transactional(isolation = Isolation.REPEATABLE_READ,rollbackFor = Exception.class)
@Override
public void updateUserBean(UserBean userBean) {
userMapper.updateUserBean(userBean);
}
@Override
public UserBean getOneById(Integer id) {
return userMapper.getOneById(id);
}
@Transactional(isolation = Isolation.REPEATABLE_READ,rollbackFor = Exception.class)
@Override
public void delOneById(Integer id) {
userMapper.delOneById(id);
}
}
Spring框架提供的事务处理方案(面试题)
1、编程式事务解决方案
在业务层代码中使用,TransactionTemplate,
这种方案已经废弃了,原因是它将事务控制代码,和核心代码再度进行了耦合
2、声明式事务解决方案
使用aop的思想,将事务控制定义在切面中,而核心代码中没有任何事务逻辑代码
事务
事务:一个具有明确边界的,而且执行顺序是有序的一段序列。
Eg: 张三给李四转钱
Eg: 一次HTTP请求
事务拥有4大特性:
原子性:事务是一个完整的个体,不能再次分割(要么统一成功,要么统一失败)
隔离性:事务和事务之间是相互隔离的,互不影响的
一致性:同一个事务范围内,数据需要保证准确性以及完整性(质量守恒)
持久性:事务一旦提交,结果具有永久性(持久化到数据库中去)
事务隔离级别和数据库的隔离级别是一个意思。数据库的隔离级别(mysql)有4个:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iianJhGn-1615995719853)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201117144107442.png)]
降低隔离级别除了会引发幻读、不可重复读、脏读外,还可能会引发两种数据更新丢失问题
胀读:读到其他事务还没有提交是数据
不可重复读:两次读的数据不一致
幻读:第一次读到的是一条记录,第二次由于其他事务添加了一条就变成了两条
更新丢失:
如果多个线程操作,基于同一个查询结构对表中数据修改,那么后修改的记录将会覆盖前面修改的记录,前面已经修改的就丢失掉了,这就叫做更新丢失,这是因为数据库没有执行任何锁操作,因此引发的更新丢失
解决方法:加上数据库锁
第一类更新丢失:
事务A撤销操作,,把已经提交的事务B的更新数据覆盖了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TU6E4lfF-1615995719854)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201117144631554.png)]
第二类更新丢失:
事务A覆盖事务B已经提交的数据造成B所做的操作丢失了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kZHaWCff-1615995719855)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201117145316979.png)]
隔离级别与更新丢失的情况
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ti09K8FP-1615995719856)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201117144350256.png)]
正因为:读已提交,可能会导致第二类的更新丢失,以及会导致不可重复读的问题,所以它的数据一致性的问题,比可重复读高,所以我们一般使用:可重复读作为我们默认的隔离级别
事务的传播行为:
事务的传播:当多个同时具有事务能力的方法,在进行相互调用时,事务如何控制,或者任何传播的问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lVgX41gH-1615995719857)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201117145552629.png)]
记住两个:
REQUIRED:不管怎么样,一定要有事务,没有事务就创建事务,有的话就继续用,通常用于新增,删除,修改
SUPPORTS:有事务,就支持事务,没有事务,就非事务方式执行
添加Log4J日志框架
1、导入依赖
<!--日志信息-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2、加入配置文件log4j
log4j.rootLogger=DEBUG, console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%5p [%t] - %m%n
#Print OUT SQL
log4j.logger.java.sql.ResultSet=INFO
log4j.logger.apache=INFO
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
3、在applicationContext.xml中配置
<!--开启log4j的日志记录-->
<property name="logImpl" value="org.apache.ibatis.logging.log4j.Log4jImpl"></property>
数据库锁
降低隔离级别会导致数据不一致的问题,隔离越低,准确性越差
方案:悲观锁,乐观锁
悲观锁:
数据库中一种非常严格的行级锁,悲观锁的概率是:程序悲观的认为,当某一个用户在操作数据库中数据时,一定存在有其他用户和它一起操作同样的一批数据,而且是同时,于是为了解决这种问题,程序直接针对数据库底层 的数据进行上锁,上锁之后的效果是:用户A 针对某些数据上了锁,其他用户就无法针对相同的数据进行查看以及操作效果等同于,你们在代码中使用synchronized
具体的步骤: 在你的查询语句中 添加
for update;
例子:
select * from user for update;
上述的这条语句,会导致其他用户无法查看user_info中的数据,除非:当前使用者已经 提交事务,回滚事务,关闭事务
这种方式,准确性高,但效率太低! 通常适用在: 购买火车票
乐观锁
乐观锁:是解决数据一致性问题,最为频繁的一种锁。它不是针对数据库底层上锁,相反它可以理解为是一种逻辑锁
乐观锁的概念: 程序乐观的认为: 当某一个用户在操作一批数据时,一定没有人和他同时操作同一批数据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jgkEQ6fw-1615995719857)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201118152223556.png)]
乐观锁的解决方案: 添加版本控制
添加版本控制之后,每次提交都会将自己上的version版本 + 1,然后与数据库版本号进行对比,看是否 大于 数据库的版本号, 如果
大于,则正常的提交,如果不大于,将提交不成功
乐观锁的具体实现
核心逻辑:
@Transactional(isolation = Isolation.REPEATABLE_READ,rollbackFor = Exception.class)
@Override
public String buyGoodsBean(OrderBean orderBean) {
//查询想要购买的商品的版本信息和库存量
GoodsBean goodsBean = goodsMapper.getOneById(orderBean.getGoodsBean().getId());
//如果库存充足
if(goodsBean.getStoreNum()>0){
//想要购买的商品的原来购买版本号
Integer version = orderBean.getGoodsBean().getVersion();
//现在数据库的商品版本号
Integer dbVersion = goodsBean.getVersion();
//判断当前版本号+1是否大于数据库版本好
if(version+1>dbVersion){
//如果大于执行修改方法
int row = goodsMapper.updateGoodsBean(goodsBean);
if(row>0){
orderMapper.saveOrder(orderBean);
return "购买成功!库存减一";
}else {
//如果版本号不同会执行失败
Boolean reBuy = reBuy(orderBean);
if (reBuy){
return "购买成功!库存减一";
}else{
return "商品已售罄!";
}
}
}else{
// 如果当前版本号+1不大于数据库版本号
//如果版本号不同会执行失败
Boolean reBuy = reBuy(orderBean);
if (reBuy){
return "购买成功!库存减一";
}else{
return "商品已售罄!";
}
}
}else{
return "商品已售罄!";
}
}
乐观锁的补偿机制
乐观锁,是通过版本控制的方法,来解决 多个人,同时因为降低隔离级别,而导致数据不一致的问题
但是它在解决问题,往往可能抛出: 你操作手速太慢 ,需要再次操作!!!
所以需要对它进行一定的补偿!!
核心代码:
/**
* 再次购买
* @param orderBean
* @return
*/
public Boolean reBuy(OrderBean orderBean){
//查询想要购买的商品的版本信息和库存量
GoodsBean goodsBean = goodsMapper.getOneById(orderBean.getGoodsBean().getId());
if(goodsBean.getStoreNum()>0){
int row = goodsMapper.updateGoodsBean(goodsBean);
if(row>0){
orderMapper.saveOrder(orderBean);
return true;
}else {
return false;
}
}else{
return false;
}
}
日志切面
系统日志切面
实现步骤:
1、导入依赖
<!--日志桥梁框架-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.30</version>
<scope>test</scope>
</dependency>
2、开启注解aop切面支持
--开启aop切面支持-->
<aop:aspectj-autoproxy/>
3、编写切面类
package com.springmybatis.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
@Aspect
@Slf4j
public class SystemLog {
@Pointcut("execution(* com.springmybatis.service.impl.*Impl.*(..))")
public void pointcut(){}
/**
* 前置通知
* @param joinPoint
*/
@Before("pointcut()")
public void BeforeLogAdvice(JoinPoint joinPoint){
//目标对象
Object target = joinPoint.getTarget();
//目标方法
String name = joinPoint.getSignature().getName();
//目标方法参数
Object[] args = joinPoint.getArgs();
System.out.println("前置通知");
log.info(target + "--" + name + "()---args:{} ", Arrays.toString(args));
}
/**
* 后置返回通知
* @param joinPoint
* @param result
*/
@AfterReturning(value = "pointcut()",returning = "result")
public void AfterReturnAdvice(JoinPoint joinPoint,Object result){
//目标对象
Object target = joinPoint.getTarget();
//目标方法
String name = joinPoint.getSignature().getName();
System.out.println("后置返回通知");
log.info(target + "--" + name + "()---result:{}",result);
}
/**
* 后置异常通知
* @param joinPoint
* @param exception
*/
@AfterThrowing(value = "pointcut()",throwing = "exception")
public void AfterThrowingAdvice(JoinPoint joinPoint,Exception exception){
//目标对象
Object target = joinPoint.getTarget();
//目标方法
String name = joinPoint.getSignature().getName();
System.out.println("后置异常通知");
log.error(target + "--" + name + "()---exception:{}",exception);
}
}
操作日志切面
操作日志: 记录某个人,在某个时间,在某个模块,执行了个什么样的事情,它影响了什么数据
所有对数据库会造成影响的地方,都需要记录 增删改
实现步骤:
1、建立存储操作日志的表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FwDIYt9n-1615995719858)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201118171831963.png)]
2、写JavaBean
3、写实现插入数据库的业务层以及持久层代码
4、自定义表示切入点的注解,以及相关的枚举类
package com.springmybatis.anno;
import com.springmybatis.enums.OptTypeEnum;
import java.lang.annotation.*;
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
/**
* 模块名称
* @return
*/
String menuName();
/**
* 方法类型
* @return
*/
OptTypeEnum optType();
}
package com.springmybatis.enums;
import lombok.Getter;
/**
* 定义枚举类
* 表示方法类型
*/
@Getter
public enum OptTypeEnum {
SAVE(0),UPDATE(1),DELETE(2);
private int type;
private OptTypeEnum(int type){
this.type=type;
}
}
5、写切面类,用于执行将操作日志写入数据库的业务
package com.springmybatis.aspect;
import com.springmybatis.anno.MyLog;
import com.springmybatis.bean.OptLogBean;
import com.springmybatis.service.IOptLogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Date;
@Component
@Aspect
public class OptLogAspect {
@Resource
private IOptLogService optLogService;
/**
* 后置返回通知
* 记录:操作日志
* @param joinPoint
* @param result
* @param myLog 凡是使用@ MyLog 的地方,都是该切面的切入点
*/
@AfterReturning(value = "@annotation(myLog)",returning = "result")
public void afterReturningAdvice(JoinPoint joinPoint, Object result, MyLog myLog){
OptLogBean optLogBean=new OptLogBean();
optLogBean.setData(Arrays.toString(joinPoint.getArgs()));
optLogBean.setOptTime(new Date());
optLogBean.setUserName("张三");
optLogBean.setMenuName(myLog.menuName());
//枚举类型获取里面的属性
optLogBean.setOptType(myLog.optType().getType());
optLogService.saveOptLog(optLogBean);
}
}
额外切面点定义方法
日志切面中,给大家扩展一种新的定义切入点的方法,使用注解来定义
@annotation 表示使用注解,必须要定义在方法的上方
@within 表示使用注解,必须要定义在类的上方
StringMVC
MVC
MVC:模型、视图、控制器
它是一种表现层的一种架构模式
优点:1、松散表现层的耦合关系 采用3个模块分离关注的方式,俩松散表现层的关系
Model:只关注数据的传输和业务实现
View:只关注页面的展示
Controller:只关注页面的请求的接收,以及相应的返回
2、成本低,部署快,只需要关注Java代码,至于页面将交由前端人员,各部署各的,相互不干扰
3、维护性高,因为MVC是3个模块,相互独立,所以可以分别进行维护
SpringMVC
spring框架有七大功能,其中一个是:webmvc
webmvc就是这里springMVC框架,它主要是一种采用MVC架构模式下的表现层框架
它同样和Servlet一样,遵循:请求-响应模式
同样使用Http通讯协议,完成前后端的数据交互
请求-响应模式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GSw1NJdp-1615995719859)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201119110022617.png)]
Controller 控制器,以前二阶段就是你们的Servlet 来承担这个角色
Servlet存在缺陷
1、太过于依赖Servlet API 强制要求必须继承HTTPServlet
2、太过于依赖tomcat容器了,这就导致开发或测试非常的麻烦
3、太过于依赖具体的页面技术,例如:jsp
但是现在的这个框架中,就完美的解决了上述问题
SpringMVC的控制器
1、核心(前端)控制器
该控制器位于整个框架的最前端,它实际上就是一个Servlet,它的作用:
1)接收页面的请求
2)调度请求到不同的控制器上去
2、应用控制器
处理映射器,视图解析器,文件上传解析器,处理适配器……
这些应用控制器都是SpringMVC提供好了,不需要程序员操作
它们每一种都有它们的自己的责任:
处理映射器: 完成页面请求 与 页面控制器之间的映射关系,相当于@WebServlet所干的事情
视图解析器: 完成后端的返回 视图名称 到 具体视图资源的解析任务
文件上传解析器: 完成文件上传的任务
处理适配器: 完成页面请求 到 页面控制器之间的适配关系
SpringMVC框架,除了采用MVC架构以外,还采用一种叫做:服务到工作者模式
请求到达该框架的每一步,都有一个Controller在完成对应的任务!
3、页面控制器
请求都是来自于页面,页面控制器就是专门用来处理 页面请求的 控制器 (需要程序员自己写)
SpringMVC的详细架构图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HgcHVB2b-1615995719861)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201119110121977.png)]
springmvc具体步骤(面试):
1、前端发起请求到前端控制器中,
2、核心控制器问处理映射器中的映射关系,映射处理器如果有就会返回给前端控制器,
3、前端控制器会到处理器适配器,根据映射关系做适配
4、适配完后会找到控制器,然后执行业务方法,返回ModalAndView
5、前端控制器拿到ModalAndView会把其中的viewName拿给视图解析器进行解析,视图解析器会解析为一个物理路径给前端控制器,
6、前端控制器会根据ModalAndView中的数据向页面进行填充
7、前端控制器会将视图响应给前端页面
SpringMVC框架入门(配置版 - 了解)
详细见课堂笔记
SpringMVC框架 (注解版 - 掌握)
案例:
实现步骤
1、配置web.xml
配置前端控制器、加载spring-mvc.xml文件
<web-app xmlns="http://xmlns.jcp/xml/ns/javaee"
xmlns:xsi="http://www.w3/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp/xml/ns/javaee
http://xmlns.jcp/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!--配置编码过滤器-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!--使用utf-8编码集-->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<!--强制请求和响应都使用utf-8编码集-->
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--定位spring容器的配置文件-->
<!--配置前端控制器-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--向WebApplicationContext 中,定位Spring容器的配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<!--/代表接收所有请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
2、配置spring-mvc.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework/schema/beans"
xmlns:xsi="http://www.w3/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework/schema/mvc"
xmlns:context="http://www.springframework/schema/context"
xsi:schemaLocation="http://www.springframework/schema/beans
https://www.springframework/schema/beans/spring-beans.xsd http://www.springframework/schema/mvc https://www.springframework/schema/mvc/spring-mvc.xsd http://www.springframework/schema/context https://www.springframework/schema/context/spring-context.xsd">
<!--开启SpringMVC框架的注解支持-->
<mvc:annotation-driven/>
<!--开启Spring容器的自动扫描-->
<context:component-scan base-package="com.springmvc"/>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--配置前缀-->
<property name="prefix" value="/"/>
<!--配置后缀-->
<property name="suffix" value=".jsp"/>
<!--解析JSTL标记的解析器-->
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>
</bean>
</beans>
配置开启mvc的注解,配置开启扫描
3、写前端请求页面
4、写控制器具体代码
5、写前端响应页面
6、配置中文乱码过滤器
<!--配置编码过滤器-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!--使用utf-8编码集-->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<!--强制请求和响应都使用utf-8编码集-->
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
@Controller:声明该类是一个控制器
@RestController
前两个注解,都是用来声明组件需要被Spring容器进行管理,区别在于@Controller表示要跳页面,@RestController表示不需要跳页面
@RequestMapping
定义页面请求路径,与执行方法该路径请求的方法之间的映射关系
@RequestParam
该注解的作用: 完成页面表单提交项 到方法参数之间的映射关系
@RequestBody
@ResponseBody
上面两个注解,主要是完成Java对象与json对象的序列化与反序列化
RequestMapping
属性:
value 用来定义映射关系
value= 用来完成路径映射,可以省略
Eg:
value={“/sys/login”} 精准匹配
value={“sys/lo*”} 模糊匹配 能匹配:sys/lo1 , sys/loadsfsadf,但是无法匹配sys/lo/asdf
value={“sys/login/*”} 能匹配sys/login/asdf
value={“sys/**”} 能匹配sys/login/asdf
value={“/sys/login01”,“/sys/login02”}
value={“/user/{userName}/{birthday}”} 路径参数写法,适用于Rest风格中
method 用来规范前端提交的方法
method 用来规范前端提交方法
@RequestMapping(value={"/login"},method = {RequestMethod.POST})
如果前端使用HTTP的方法,不符合,将抛出:HTTP Status 405 – Method Not Allowed
Spring4.3之后,才提供的:
衍生的注解:
@PostMapping == @RequestMapping(method={RequestMethod.POST})
@GetMapping
@PutMapping
@DeleteMapping
params 参数过滤器
params = {} 主要用于过滤 表单提交的参数
举例:
Params={“userName”} 表示表单提交项中,一定要有userName项
Params={“!userName”} 表示表单提交项中,一定不能有userName项
Params={“userName=张三”} 表示表单提交项中,一定要有userName项,而且它的值还必须是”张三”
Params={“userName!=张三”}表示表单提交项中,一定要有userName项,而且它的值一定不能是张三
Params={“userName”,“loginName”} 表示表单提交项中,一定要有userName,loginName项
如果不满足,页面将抛出:HTTP Status 400 – Bad Request
SpringMVC框架,方法的参数类型
1、作用域对象(了解)
HttpServletRequest,HttpServletResponse,HttpSession
这3个东西,都可以作为这里的方法参数,特点:参数的个数和顺序没有验证
就是:1个都可以不写,也可以把3个都写上
/**
* springmvc案例一
* @param request
* @param response
* @return
*/
@RequestMapping(value = "/login",method = RequestMethod.POST)
public ModelAndView login(HttpServletRequest request, HttpServletResponse response){
ModelAndView mv=new ModelAndView();
String loginName = request.getParameter("loginName");
String password = request.getParameter("password");
System.out.println(loginName);
System.out.println(password);
mv.setViewName("result");
mv.addObject("user","张三");
return mv;
}
2、页面表单提交项
/**
* 参数接收表单项
*/
@RequestMapping("/login")
public ModelAndView login(String loginName,@RequestParam(value = "password",defaultValue = "123456") String pwd){
ModelAndView mv=new ModelAndView();
System.out.println(loginName);
System.out.println(password);
mv.setViewName("result");
mv.addObject("user","张三");
return mv;
}
3、表单提交项的封装对象(常用)
/**
* 对象接收表单项
*/
@RequestMapping("/login")
public ModelAndView login(UserBean userBean){
ModelAndView mv=new ModelAndView();
System.out.println(userBean.getLoginName());
System.out.println(userBean.getPassword());
mv.setViewName("result");
mv.addObject("user","张三");
return mv;
}
4、返回的是String
/**
* 当String作为返回类型时,Model作为参数用户写数据
* String作为跳转的页面
* @param model
* @param userBean
* @return 跳转的页面
*/
@RequestMapping("/login")
public String login(Model model, UserBean userBean){
System.out.println(userBean.getLoginName());
System.out.println(userBean.getPassword());
model.addAttribute("user","张三");
return "result";
}
5、接收Cookie的信息(了解)
/**
* 获取请求头里面的信息
* @param model
* @param userBean
* @param sessionId
* @param userAgent
* @return
*/
@RequestMapping("/login")
public String login(Model model, UserBean userBean,
@CookieValue("JSESSIONID") String sessionId,
@RequestHeader("User-Agent") String userAgent){
System.out.println(userBean.getLoginName());
System.out.println(userBean.getPassword());
System.out.println(sessionId);
System.out.println(userAgent);
model.addAttribute("user","张三");a
return "result";
}
SpringMVC框架,方法的返回类型
ModelAndView
ModelAndView 是SpringMVC框架中默认的返回类型,Model代表是模型数据,View代表的是需要跳转的页面(但是只是一个逻辑视图的名字)
public ModelAndView login(HttpServletRequest request, HttpServletResponse response){
ModelAndView mv=new ModelAndView();
String loginName = request.getParameter("loginName");
String password = request.getParameter("password");
System.out.println(loginName);
System.out.println(password);
mv.setViewName("result");
mv.addObject("user","张三");
return mv;
}
返回类型为String
String 返回的字符串,就是需要跳转的页面
如果返回值是:null SpringMVC框架将会自动将 请求路径 作为即将要跳转的页面,所以尽量不要返回 null
/**
* 当String作为返回类型时,Model作为参数用户写数据
* String作为跳转的页面
* @param model
* @param userBean
* @return 跳转的页面
*/
@RequestMapping("/login")
public String login(Model model, UserBean userBean){
System.out.println(userBean.getLoginName());
System.out.println(userBean.getPassword());
model.addAttribute("user","张三");
return "result";
}
String 做请求转发
forward 后一定要是完整路径
return “forward:/sys/login02”;
/**
* 获取请求头里面的信息
* @param model
* @param userBean
* @param sessionId
* @param userAgent
* @return
*/
@RequestMapping("/login")
public String login(Model model, UserBean userBean,
@CookieValue("JSESSIONID") String sessionId,
@RequestHeader("User-Agent") String userAgent){
System.out.println(userBean.getLoginName());
System.out.println(userBean.getPassword());
System.out.println(sessionId);
System.out.println(userAgent);
model.addAttribute("user","张三");
return "forward:/sys/login02";
}
@RequestMapping("/login02")
public String login02(Model model, UserBean userBean,
@CookieValue("JSESSIONID") String sessionId,
@RequestHeader("User-Agent") String userAgent){
System.out.println("login02");
return "result";
}
String 做重定向
return “redirect:/sys/login02”;
@RequestMapping("/login")
public String login(Model model, UserBean userBean,
@CookieValue("JSESSIONID") String sessionId,
@RequestHeader("User-Agent") String userAgent){
System.out.println(userBean.getLoginName());
System.out.println(userBean.getPassword());
System.out.println(sessionId);
System.out.println(userAgent);
model.addAttribute("user","张三");
return "redirect:http://www.baidu";
}
参数绑定
前端页面上的数据,凡是从表单上输入到后端的,都可以认为是String字符串
后端:基本数据类型,字符串,对象
SpringMVC框架针对以下内容已经做了自动参数绑定:
String -> 基本数据类型
String -> String
String -> String[]
String -> 基本数据类型的包装类型
String -> 对象
可查看spring-core.jar中的converter包中的代码
Stirng可以基本数据类型和他的包装类还有数组进行转换
/**
* Stirng可以基本数据类型和他的包装类还有数组进行转换
* @param userName
* @param loginName
* @param password
* @param age
* @param marray
* @param income
* @param hobbys
* @return
*/
@RequestMapping("/register")
public ModelAndView login(String userName,String loginName,String password,Integer age, Boolean marray,Double income, String[] hobbys){
ModelAndView mv=new ModelAndView();
System.out.println("loginName=>"+loginName);
System.out.println("password=>"+password);
System.out.println("age=>"+age);
System.out.println("marray=>"+marray);
System.out.println("income=>"+income);
System.out.println("hobbys=>"+ Arrays.toString(hobbys));
System.out.println("userName=>"+ userName);
mv.addObject("suMsg",userName);
mv.setViewName("result");
return mv;
}
String可以向对象进行转化
/**
* String可以向对象进行转化
* @param
* @return
*/
@RequestMapping("/register")
public ModelAndView login(UserBean userBean){
ModelAndView mv=new ModelAndView();
System.out.println(userBean);
mv.addObject("suMsg",userBean.getUserName());
mv.setViewName("result");
return mv;
}
实现视图跳转视图的方法
<!--页面跳转控制器的用法-->
<mvc:view-controller path="/relogin" view-name="relogin"/>
<mvc:view-controller path="/register" view-name="register"/>
path:写的是在页面中要跳转的名字:
view-name:真实的视图
类型转换器
当有的时候,用户可能在浏览器输入一些 具有某些特殊格式的字符串,在后端转换时,会抛出[org.springframework.web.method.annotation.MethodArgumentTypeMismatchException
此时,我们就需要自定义属于我们自己的转换规则
以Date类型为例
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V9T9cGEc-1615995719862)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201120202000739.png)]
解决方案
先编写自己的转换服务
定义一个类,让这个类实现Converter 转换接口即可
实现步骤:
1、编写自己的类型转换器及日期格式转换的工具类
package com.springmvc.converter;
import com.springmvc.utils.StringToDateUtil;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* 自定义类型转换器
*/
@Component
public class StringToDateConverter implements Converter<String, Date> {
@Override
public Date convert(String s) {
Date date=null;
try {
if (StringToDateUtil.hasLength(s)){
int length=s.length();
if(length==10){
date=StringToDateUtil.stringToDate(s,"yyyy-MM-dd");
}else{
date=StringToDateUtil.stringToDate(s,"yyyy-MM-dd hh:mm:ss");
}
}
}catch (Exception e){
e.printStackTrace();
}
return date;
}
}
package com.springmvc.utils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 日期转换工具类
*/
public class StringToDateUtil {
public static Date stringToDate(String s,String pattern) throws ParseException {
SimpleDateFormat simpleDateFormat=new SimpleDateFormat(pattern);
return simpleDateFormat.parse(s);
}
public static boolean hasLength(String s){
if (s.length()>=10){
return true;
}else{
return false;
}
}
}
2、向Spring框架的转换工厂注册转换服务,以及让MVC框架支持该转换工厂
修改spring-mvc.xml配置文件:
<!--定义类型转换工厂-->
<bean id="conversionServiceFactoryBean" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="stringToDateConverter"></ref>
</set>
</property>
</bean>
<!--开启SpringMVC框架的注解支持-->
<mvc:annotation-driven conversion-service="conversionServiceFactoryBean"/>
文件上传
实现步骤:
1、导入依赖
<!-- 导入文件上传的依赖包 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
上述的依赖,自动向工程中导入commons-fileupload,commons-io 的Jar包
2、编写上传文件的jsp页面
注意:1、上传文件必须是post请求
2、表单需要有属性:enctype=”multipart/form-data”
Enctype 表示给后端说明,前端提交过来的数据的数据类型
application/x-www-form-urlencoded
编码方式,告诉后端,前端提交过来的数据是 普通文本数据,二进制数据 (但是这种方式性能较低)
multipart/form-data
告诉后端,前端提交过来的数据是 二进制数据 eg:图片,音频,视频
text/plain
告诉后端,前端提交过来的数据是 纯文本数据
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<% String path = request.getContextPath() + "/";%>
<html>
<head>
<base href="<%=path %>">
<title>Title</title>
</head>
<body>
<div align="center">
<p>欢迎${suMsg}登陆系统</p>
<form action="files/upload" method="post" enctype="multipart/form-data">
请选择文件:<input type="file" name="pic">
<br>文件描述:<input type="text" name="picMessage">
<br><input type="submit" value="上传">
</form>
</div>
</body>
</html>
3、编写Controller代码
@PostMapping("/upload")
public String uploadFile(MultipartFile pic,String picMessage){
String filename = pic.getOriginalFilename();
//输出文件名称
System.out.println(filename);
System.out.println(picMessage);
//如果有文件服务器,则将该文件上传至FTP文件服务器上去
//如果没有文件服务器,就将该文件持久化到本地磁盘中去
String path="D://woniucode//files";
//判断文件夹是否存在如果没有就创建文件夹
File dir=new File(path);
if (!dir.exists()){
dir.mkdir();
}
//拼接文件路径
String filePath=path+File.separator+filename;
try {
//将文件存储在硬盘中
pic.transferTo(new File(filePath));
} catch (IOException e) {
e.printStackTrace();
}
return "success";
}
4、配置文件上传解析器
<!--文件上传解析器,ID请固定为:multipartResolver-->
<bean id="multipartResolver" class="org.springframework.web.multipartmons.CommonsMultipartResolver">
<!--配置字符编码集-->
<property name="defaultEncoding" value="UTF-8"/>
<!--配置最大上传文件大小-->
<property name="maxUploadSize" value="104857600"/>
<!--单次上传时,每个独立的文件最大容量:10M-->
<property name="maxUploadSizePerFile" value="10485760"/>
<!--每次读取文件时,以1KB的方式来读取-->
<property name="maxInMemorySize" value="1024"/>
</bean>
文件下载
实现步骤:
1、编写jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
成功
<img src="download?fileName=1234.png" alt="阿甘">
<br>
点击,进行下载
<a href="download?fileName=1234.png">点击,即可下载</a>
</body>
</html>
编写Controller代码
/**
* 下载
* @param fileName 文件的名称
* @param userAgent 浏览器的种类
* @return 设置响应的实体对象 由于我们现在是返回的图片,所以我们以字节数组的方式来返回
*/
@RequestMapping("/download")
public ResponseEntity<byte[]> download(String fileName, @RequestHeader("User-Agent") String userAgent){
String filePath = "D://woniucode//files" + File.separator + fileName;
File file = new File(filePath);
if(file.exists()){
try{
//构建一个响应状态码 200
ResponseEntity.BodyBuilder builder = ResponseEntity.ok();
//设置文件的长度
builder.contentLength(file.length());
//告诉前端,后端现在给你的数据类型是:二进制流
builder.contentType(MediaType.APPLICATION_OCTET_STREAM);
fileName = URLEncoder.encode(fileName, "UTF-8");
//判断浏览器是否是IE浏览器
if(userAgent.indexOf("MSIE") > 0) {
builder.header("Content-Disposition", "attachment; filename=" + fileName);
}else {
builder.header("Content-Disposition", "attacher; filename*=UTF-8''" + fileName);
}
//通过FileUtils工具类,读取文件到 响应体中
return builder.body(FileUtils.readFileToByteArray(file));
}catch (Exception e){
e.printStackTrace();
}
}
return null;
}
拦截器
过滤器可以过滤所有请求(动态请求,静态请求),但是拦截器只能拦截 动态请求
拦截器跟AOP一样,底层都是动态代理模式
运用场景
记录用户登陆,退出的日志信息
格式:哪个人,在哪个IP地址,何时登录|退出,登录方式(手机 | PC端)
实现方式
1、实现HandlerInterceptor
2、继承HandlerInterceptorAdapter
案例:
拦截器作用于登陆日志信息
package com.springmvc.interceptor;
import com.springmvc.bean.LoginLogBean;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
public class LoginLogInterceptor implements HandlerInterceptor{
/**
* 在Controller方法执行之前执行
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("在Controller方法执行之前执行");
return true;
}
/**
* 在Controller方法执行之后执行
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("拦截器中的preHandle方法,在Controller方法执行之后执行");
LoginLogBean loginLogBean = new LoginLogBean();
loginLogBean.setId(1L);
loginLogBean.setIp(getIpAddress(request));
loginLogBean.setLoginName(request.getParameter("loginName"));
loginLogBean.setUserName("user");
loginLogBean.setLoginTime(new Date());
}
/**
* 执行在所有的拦截器 的preHandle() 和postHandle() 执行完毕之后的收尾的方法
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("在Controller方法执行最后执行");
}
/**
* 获得IP地址
* @param request
* @return
*/
public String getIpAddress(HttpServletRequest request){
String ip = null;
//X-Forwarded-For:Squid 服务代理
String ipAddresses = request.getHeader("X-Forwarded-For");
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
//Proxy-Client-IP:apache 服务代理
ipAddresses = request.getHeader("Proxy-Client-IP");
}
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
//WL-Proxy-Client-IP:weblogic 服务代理
ipAddresses = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
//HTTP_CLIENT_IP:有些代理服务器
ipAddresses = request.getHeader("HTTP_CLIENT_IP");
}
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
//X-Real-IP:nginx服务代理
ipAddresses = request.getHeader("X-Real-IP");
}
//有些网络通过多层代理,那么获取到的ip就会有多个,一般都是通过逗号(,)分割开来,并且第一个ip为客户端的真实IP
if (ipAddresses != null && ipAddresses.length() != 0) {
ip = ipAddresses.split(",")[0];
}
//还是不能获取到,最后再通过request.getRemoteAddr();获取
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
ip = request.getRemoteAddr();
}
return ip;
}
}
spring-mvc:配置拦截器
<!--配置拦截器-->
<mvc:interceptors>
<!--配置单个拦截器,说:拦截登录请求-->
<mvc:interceptor>
<mvc:mapping path="/sys/login"/>
<ref bean="loginLogInterceptor"></ref>
</mvc:interceptor>
</mvc:interceptors>
注意:拦截器可以配置多个
拦截器链的执行顺序
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y1n4iYKF-1615995719863)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201120205027799.png)]
先后顺序是由xml配置中的先后顺序决定的,前一个的preHanlder先执行,在执行后一个的preHanlder,再执行后一个的postHanlder再执行前一个的postHanlder,再执行后一个的afterCompletion,再执行前一个的afterCompletion
过滤器与拦截器的区别(面试题)
1、拦截器只能拦截动态请求,过滤器可以过滤任何请求
2、拦截器由springmvc提供,过滤器由tomcat提供
3、拦截器底层是动态代理,过滤器是回调机制
4、拦截器运行在spring容器中,过滤器运行在tomcat容器中
SpringMVC的验证
验证:格式验证,内容验证
格式验证:判断输入框是否有值,某一个输入是否是电话号码等
内容验证:判断某一个账号是否存在(js+后端服务器)
SpringMVC的验证(格式验证)
SpringMVC自身并没有任何的验证框架,它采用的是Hibernate的验证框架
Hibernate 框架是一个纯粹的面向对象的框架,它所有的操作都是针对Bean对象来完成,所以这里的验证,也可以被称为:Bean Validation
实现步骤
1、导入依赖
<!-- 导入hibernate 验证框架 --><dependency>
<groupId>org.hibernate</groupId><artifactId>hibernate-validator</artifactId>
<version>5.4.1.Final</version>
</dependency>
2、配置spring-mvc.xml,让SpringMVC框架支持Hibernate 的验证服务
<!--开启SpringMVC框架的注解支持-->
<mvc:annotation-driven conversion-service="conversionServiceFactoryBean" validator="localValidatorFactoryBean"/>
<!--配置Spring框架的验证工厂-->
<bean id="localValidatorFactoryBean" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<!--配置验证服务:由hibernate框架提供-->
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"></property>
</bean>
3、配置JavaBean中的验证
Bean Validation 1.0 (JSR 303)
Bean Validation 1.0 (JSR 303) 这个规范是SUN公司,给企业应用开发针对JavaBean所定义出来的验证规范
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7tMwZUCt-1615995719864)(C:\Users\18146\Pictures\Saved Pictures\图片2.jpg)]
当然,Hibernate在上述的基础上,还有一些补充规范,详情请参见:
https://docs.jboss/hibernate/validator/5.4/reference/en-US/html_single/#section-builtin-constraints
package com.springmvc.bean;
import lombok.Data;
import org.hibernate.validator.constraints.NotEmpty;
import javax.validation.constraints.*;
import java.io.Serializable;
import java.util.Date;
@Data
public class UserBean implements Serializable {
@NotNull(message = "用户名不能为null!")
@NotEmpty(message = "用户名必须有值")
@Size(min = 2,max = 8,message = "用户名[2,8]位!")
private String userName;
@NotEmpty(message = "登录名必须要有值!")
@Size(min = 6,max = 8,message = "登录名的长度:[6,8]位!")
private String loginName;
@NotEmpty(message = "密码必须要有值!")
@Size(min = 6,max = 12,message = "密码的长度:[6,12]位!")
private String password;
@Max(value = 60,message = "年龄过大,不能注册!")
@Min(value = 18,message = "未成年人,不能注册!")
private Integer age;
/**
* 是否婚配
*/
@AssertFalse(message = "你大爷的,你都结婚,还来?")
private boolean marray;
/**
* 个人收入
*/
@NotNull(message = "个人收入不能为空")
@Digits(integer = 10,fraction = 2,message = "你钱太多了,别吹NB了!")
@Min(value = 50000,message = "多找爸妈要点钱再来,或来蜗牛培训一段时间再说!")
private double income;
/**
* 个人爱好
*/
private String[] hobbys;
/**
* 出生日期
*/
@Past(message = "还没出生的小屁孩,滚蛋!")
private Date birthday;
}
4、写controller方法
@Validated 表示JavaBean需要使用Hibernate的验证框架,进行属性格式验证
BindingResult 表示验证的结果
Model 主要用来做数据传输
/**
*@Validated 表示需要对某一个JavaBean做格式校验
* @param userBean
* @param result 验证结构
* @param model
* @return
*/
@RequestMapping("/validator")
public String validator(@Validated UserBean userBean, BindingResult result, Model model){
//判断是否有错误
if (result.hasErrors()){
List<ObjectError> errors = result.getAllErrors();
//遍历所有错误
errors.forEach((e)->{
System.out.println(e.getObjectName());
System.out.println(e.getDefaultMessage());
System.out.println(e.getCode());
});
//如果有错误就回到注册页面
model.addAttribute("errors",errors);
return "validator";
}
//如果没有错误就去到登陆页面
System.out.println(userBean);
return "index";
}
5、jsp中展示验证结果
<c:if test="${errors!=null&&errors.size()>0}">
<c:forEach items="${errors}" var="err">
${err.defaultMessage}
<br>
</c:forEach>
</c:if>
前后端分离时,如何处理json数据
前后端交互:采用的数据格式,常见xml,json
现成的json框架,Jackson(默认), FastJson(阿里巴巴)
实现步骤:
1、导入jackson依赖
<!-- 引入Json的工具类Jar包 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.1</version>
</dependency>
2、编写前端的Ajax请求
<script>
$(function () {
$("#btn").click(function () {
// alert("你好");
$.ajax({
url:"stu/add",
data:{
stuName:"张三",
age:20,
stuNo:"123456"
},
type:"post",
dataType:"json",
success:function (resp) {
alert(resp.code+"..."+resp.msg);
}
})
})
})
</script>
3、spring-mvc中配置释放静态资源
<!--释放静态资源-->
<mvc:resources mapping="/static/**" location="/static/"></mvc:resources>
4、编写后端接收数据的JavaBean
@Data
public class StudentBean implements Serializable {
private Integer id;
private String stuName;
private Integer stuNo;
private Integer age;
}
5、编写返回给前端的JavaBean
@Data
public class ResultMsgBean<T> implements Serializable {
/**
* 状态码
*/
private String code;
/**
* 提示信息
*/
private String msg;
/**
* 返回给用户数据
*/
private T data;
}
6、编写控制器 的方法
注意要添加注解 @ResponseBody
/**
* @ResponseBody: 是通过MappingJackson2HttpMessageConverter 完成:后端Java对象到JSON对象之间的序列化操作。并序列化的结果通过 响应体 完成数据传输
* @param studentBean
* @return
*/
@RequestMapping("/add")
@ResponseBody
public ResultMsgBean add(StudentBean studentBean){
System.out.println(studentBean);
ResultMsgBean<Object> objectResultMsgBean = new ResultMsgBean<>();
objectResultMsgBean.setCode("200");
objectResultMsgBean.setMsg("接收到了");
return objectResultMsgBean;
}
如果前端发送的是json格式的数据
注意:请求方式不能为get
实现步骤:
1、Ajax请求(导入转json的js文件)
注意:需要有contentType:“application/json;charset=utf-8”
告诉后端,传递过来的数据是JSON
$("#btn01").click(function () {
var user={
stuName:"张三",
age:20,
stuNo:"123456"
};
var json = $.toJSON(user);
// alert("你好");
$.ajax({
url:"stu/update",
data:json,
type:"post",
contentType:"application/json;charset=utf-8",//告诉后端,传递过来的数据是JSON
dataType:"json",
success:function (resp) {
alert(resp.code+"..."+resp.msg);
}
})
})
2、页面控制器方法
注意RestController:表示ReponseBody+Controller
RequestBody:用于接收json格式的数据,转给Java对象
/**
* RequestBody:用于接收json格式的数据,转给Java对象
* @param studentBean
* @return
*/
@RequestMapping("/update")
@ResponseBody
public ResultMsgBean update(@RequestBody StudentBean studentBean){
System.out.println(studentBean);
ResultMsgBean<Object> objectResultMsgBean = new ResultMsgBean<>();
objectResultMsgBean.setCode("200");
objectResultMsgBean.setMsg("接收到了json");
return objectResultMsgBean;
}
PostMan发送请求工具
是一个测试工具,可以模拟页面完成http请求的发送
RestFul风格
Rest ful (Representational State Transfer)描述性状态转移风格
此风格 不是一种技术,也不是一种框架,它只是一种提交方式的改变
之前,只能采用GET/POST提交,针对资源操作:users/add users/update users/delete
HTTP协议:在协议中提供了N多种方式,用来对资源进行CRUD
URI(Universal Resource Identifier) : 统一资源标识符 作用:使用名称 将资源做了统一并唯一的标识
如何对资源做操作
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XzyubqXu-1615995719865)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201123190504433.png)]
HTTP的4种标准方法:
GET: 对资源的查询 有就是有 无就是无
POST: 对资源的新增 从无到有
PUT: 对资源的修改 有就是有
DELETE: 对资源的删除 一直都是无
幂等性: 就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的。
GET PUT DELETE
POST 非幂等性的
描述性状态转移: 转移的是资源的状态
使用RestFul的原因
1、便于前后端分离的项目 (不管前端是什么类型,后端只提供统一的访问入口)
2、同一资源,只有一个接口
3、便于服务器集群 (tomcat 做横向扩展之后,在tomcat 中取得数据,都是一样)
实现案例
实现步骤
1、定义Controller接口
package com.springmvc.controller;
import com.springmvc.bean.ResultMsgBean;
import com.springmvc.bean.StudentBean;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
@RequestMapping("/students")
@RestController
public class RestFulController {
/**
* 新增
* @param studentBean
* @return
*/
@PostMapping("/more")
public ResultMsgBean saveStudent(StudentBean studentBean){
ResultMsgBean<Object> resultMsgBean = new ResultMsgBean<>();
resultMsgBean.setMsg("新增成功");
resultMsgBean.setCode("200");
System.out.println(studentBean);
return resultMsgBean;
}
/**
* 删除
* @param id
* @param
* @return
*/
@DeleteMapping("/{id}")
public ResultMsgBean deleteStudent(@PathVariable("id") Long id){
ResultMsgBean<Object> resultMsgBean = new ResultMsgBean<>();
resultMsgBean.setMsg("删除成功");
resultMsgBean.setCode("200");
System.out.println(id);
return resultMsgBean;
}
/**
* 修改方法
* @param studentBean
* @return
*/
@PutMapping("/{id}")
public ResultMsgBean updateStudent(StudentBean studentBean,@PathVariable("id") Long id){
ResultMsgBean<Object> resultMsgBean = new ResultMsgBean<>();
resultMsgBean.setMsg("新增成功");
resultMsgBean.setCode("200");
System.out.println(studentBean);
return resultMsgBean;
}
/**
* 根据id查询
* @param id
* @return
*/
@GetMapping("/{id}")
public ResultMsgBean findByIdStudent(@PathVariable("id") Long id){
ResultMsgBean<Object> resultMsgBean = new ResultMsgBean<>();
resultMsgBean.setMsg("新增成功");
resultMsgBean.setCode("200");
StudentBean studentBean = new StudentBean();
studentBean.setStuName("小二");
resultMsgBean.setData(studentBean);
System.out.println(id);
return resultMsgBean;
}
/**
* 查询所有的学生
* @param stuName
* @param age
* @param stuNo
* @return
*/
@GetMapping("/all/{stuName}/{age}/{stuNo}")
public ResultMsgBean findAllStudent(@PathVariable("stuName") String stuName,@PathVariable("age") Integer age,@PathVariable("stuNo") Integer stuNo){
ResultMsgBean<Object> resultMsgBean = new ResultMsgBean<>();
resultMsgBean.setMsg("新增成功");
resultMsgBean.setCode("200");
StudentBean studentBean = new StudentBean();
studentBean.setStuName("小二");
StudentBean studentBean1 = new StudentBean();
studentBean.setStuName("小二");
List<StudentBean> list=new ArrayList<>();
list.add(studentBean);
list.add(studentBean1);
resultMsgBean.setData(list);
System.out.println(stuName);
System.out.println(age);
System.out.println(stuNo);
return resultMsgBean;
}
}
2、在web.xml中配置支持PUT,DELETE请求过滤器(支持使用表单方式传输数据)
<!--配置运行put和delete请求-->
<filter>
<filter-name>formContentFilter</filter-name>
<filter-class>org.springframework.web.filter.FormContentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>formContentFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
3、编写jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<script type="text/javascript" src="static/js/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="static/js/jquery.json.js"></script>
<title>Title</title>
<script>
/*页面加载完成之后需要执行的代码*/
$(function () {
//根据名称查询学生
$('#btn05').click(function () {
/*发送Ajax请求*/
$.ajax({
type: "GET",
url: "students/all/张三/18/4578",
success: function(msg){
console.info(msg);
}
});
});
//根据ID查询数据
$('#btn04').click(function () {
/*发送Ajax请求*/
$.ajax({
type: "GET",
url: "students/1",
success: function(msg){
console.info(msg);
}
});
});
//删除
$('#btn03').click(function () {
/*发送Ajax请求*/
$.ajax({
type: "DELETE",
url: "students/1",
success: function(msg){
console.info(msg);
}
});
});
//修改
$('#btn02').click(function () {
/*发送Ajax请求*/
$.ajax({
type: "PUT",
url: "students/1",
data: {
stuName:"一护",
stuNo:"4578",
age:"50"
},
success: function(msg){
console.info(msg);
}
});
});
//新增
$('#btn01').click(function () {
/*发送Ajax请求*/
$.ajax({
type: "POST",
url: "students/more",
data: {
stuName:"一护",
stuNo:"4578",
age:"50"
},
success: function(msg){
console.info(msg);
}
});
});
});
</script>
</head>
<body>
<button id="btn01">btn01发起Ajax请求</button>
<button id="btn02">btn02发送json数据</button>
<button id="btn03">btn03发起Ajax请求</button>
<button id="btn04">btn04发送json数据</button>
<button id="btn05">btn05发送json数据</button>
</body>
</html>
异常的处理
处理异常的方式
所有的异常:底层依次向上层去抛,一直抛到表现层为止,采用aop切面的方式同一处理异常,(将异常不是直接抛给页面,而是将异常转换为:友好的提示信息 例如:当前系统繁忙,请稍后重试!)
异常:系统在开发,运行中抛出的问题
异常:运行时异常,编译器异常,错误
异常,在WEB系统中一般如何解决
各层 都有肯抛出异常,底层不要去抓捕异常,相反是不断的向上抛,抛到表现层为止
注意:异常不能抛到一面上
tomcat容器处理系统异常
系统异常:就是凡是抛出不是由程序员自定义的异常,或者Java异常而导致的问题
405:方法不被允许
404:页面找不到
400:参数错误
403:没有授权
处理方法:
1、编写出现错误要显示的页面页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
当前页面不存在请联系管理员!
</body>
</html>
2、配置web.xml中的error-page标签
<!--配置异常处理-->
<error-page>
<error-code>404</error-code>
<location>/errors/404.jsp</location>
</error-page>
SpringMvc框架处理异常
使用xml配置异常解析器(了解)
<!--配置异常解析器-->
<bean id="simpleMappingExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!--默认异常页面-->
<property name="defaultErrorView" value="errors/exception"></property>
<!--异常页面上,可以使用ex 来获得数据-->
<property name="exceptionAttribute" value="ex"></property>
<property name="exceptionMappings">
<props>
<!--异常,对应 页面-->
<prop key="java.lang.NullPointerException">errors/npException</prop>
<!--有其他的就继续-->
</props>
</property>
</bean>
使用AOP切面编程思想去解决(掌握)
第1套方案,在异常页面中显示异常信息
第2套方案,不在异常页面中显示异常信息
2种方案,2选其一,如果同时定义:第1套方案将覆盖第2套方案
第一套方案(适用于跳页面)
/**
* SpringMvc处理异常的方案二
*/
@ControllerAdvice
public class ExceptionHanlder2 {
@ExceptionHandler
public ModelAndView hanlder(Exception e){
ResultMsgBean<Object> msg = new ResultMsgBean<>();
if(e instanceof NullPointerException){
msg.setCode("10000");
msg.setMsg("系统繁忙请稍后重试");
}
ModelAndView mv=new ModelAndView();
mv.addObject("msg",msg);
mv.setViewName("errors/error");
return mv;
}
}
第二套方案(适用于不跳页面的前后端分离的)
@RestControllerAdvice
public class ExceptionHanlder {
@ExceptionHandler
public ResultMsgBean hanlder(Exception e){
ResultMsgBean<Object> msg = new ResultMsgBean<>();
if(e instanceof NullPointerException){
msg.setCode("10000");
msg.setMsg("系统繁忙请稍后重试");
}
return msg;
}
}
SpringMVC框架的优点(面试题)
1、springmvc框架采用mvc架构模式,以及服务到工作者的架构模式,将表现层通过多种内置的组件,定义得更加清晰,让页面控制器显示得更加精炼
2、由于SpringMVC是Spring框架的七大功能中的mvc模块功能,所以它天生就支持Spring框架
3、SpringMVC框架的配置虽然多,但是都比较简单独立(需要什么就可以自己选择配置什么)
4、代码的可重用性很高(通常SpringMVC的配置文件,几乎可以支持任何系统的配置)
5、可扩展性高(SpringMVC依旧是免费开源的产品,如果功能不够,你可以自己修改它的源码)
SSM框架的整合
整合步骤
1、技术选择
2、创建maven工程
3、导入依赖
4、创建项目的包结构
5、编写Spring的配置文件
6、在Spring的配置文件中,完成各种框架的整合
7、配置web.xml
技术选择
Spring、SpringMVC、Mybatis、Log4j+SLF4J 、Lombok、Druid
创建Maven项目
创建项目包结构
编写Spring容器的配置文件applicationContext.xml
步骤:
1、开启容器的自动扫描
<!--开启容器的自动扫描-->
<context:component-scan base-package="com.ssm">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
2、配置数据源连接池
2.1:配置加载jdbc.properties文件
<!--配置 Spring容器加载jdbc.properties -->
<context:property-placeholder location="jdbc.properties"/>
2.2:配置数据源连接池
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!--属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有:
监控统计用的filter:stat
日志用的filter:log4j
防御sql注入的filter:wall-->
<property name="filters" value="stat,log4j,wall" />
<!--最大连接池数量-->
<property name="maxActive" value="20" />
<!--初始化时建立物理连接的个数-->
<property name="initialSize" value="1" />
<!--获取连接时最大等待时间,单位毫秒-->
<property name="maxWait" value="60000" />
<!--最小连接池数量-->
<property name="minIdle" value="1" />
<!--间隔1分钟,监测一次空闲连接-->
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<!--空闲连接在连接池中的最大存活时间,默认为:5分钟-->
<property name="minEvictableIdleTimeMillis" value="300000" />
<!--设置连接空闲时,借出时,归还时是否需要进行连接有效校验-->
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<!--校验语句,用来判断连接是否有效-->
<property name="validationQuery" value="select now() from dual"></property>
<!--设置支持预编译,并设定预编译对象默认为20个-->
<property name="poolPreparedStatements" value="true" />
<property name="maxOpenPreparedStatements" value="20" />
<!-- 配置初始化连接时,采用异步策略 -->
<property name="asyncInit" value="true" />
</bean>
3、配置SessionFactory
<!--配置SqlSessionFactory-->
<bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<!--给JavaBean 定义别名:放置的是 JavaBean 所在的目录-->
<property name="typeAliasesPackage" value="com.woniuxy.ssm.bean"></property>
<!--额外的配置-->
<property name="configuration">
<bean class="org.apache.ibatis.session.Configuration">
<!--开启延迟加载-->
<property name="lazyLoadingEnabled" value="true"></property>
<property name="aggressiveLazyLoading" value="false"></property>
<!--开启Log4j的日志记录-->
<property name="logImpl" value="org.apache.ibatis.logging.log4j.Log4jImpl"></property>
</bean>
</property>
</bean>
4、配置MapperScanner
<!--配置MapperScanner扫描器-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--放置Mapper接口以及配置文件所在路径,如果有多个路径,可以使用 , 或 ; 进行分割-->
<property name="basePackage" value="com.ssm.mapper"></property>
<!-- 将映射文件 和 SessionFactory 进行连接,通过jdk代理为Mapper接口,动态产生代理类 -->
<property name="sqlSessionFactoryBeanName" value="sessionFactory"></property>
</bean>
5、配置事务管理器
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
6、开启事务支持
<!--开启注解驱动支持-->
<tx:annotation-driven transaction-manager="transactionManager"/>
7、开启切面的代理支持
<!--开启切面的代理支持-->
<aop:aspectj-autoproxy/>
SpringMVC框架 和 Spring框架之间的上下文关系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A7Mam1lo-1615995719867)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201124152410850.png)]
注意:由于spring-mvc可以直接使用spring的东西,所以我们不必扫描两次,需要排除springmvc不需要扫描的
编写SpringMVC框架的配置文件spring-mvc.xml
步骤:
1、开启容器的自动扫描
<!--开启容器的自动扫描-->
<context:component-scan base-package="com.ssm">
<!--annotation 注解类型过滤器-->
<!--aspectj 切面类型过滤器-->
<!--assignable 类型相关过滤器-->
<!--custom 自定义类型过滤器-->
<!--regex 正则类型过滤器-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
2、释放静态资源
<!--释放静态资源-->
<mvc:resources mapping="/static/**" location="/static/"></mvc:resources>
3、开启注解支持
<!--开启注解支持-->
<mvc:annotation-driven/>
4、配置类型转换器
4.1:在utlis包下创建DateUtil类
package com.ssm.utils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateUtil {
/**
* 将字符串解析为时间
* @param str 输入的字符串
* @param pattern 字符串的格式
* @return
* @throws ParseException
*/
public static Date str2Date(String str, String pattern) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
return sdf.parse(str);
}
}
4.2:在handler包下创建类StringToDateConverter类
package com.ssm.handler;
import com.ssm.utils.DateUtil;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.Date;
/**
* 自定义转换器
*/
@Component
public class StringToDateConverter implements Converter<String, Date> {
/**
*
* @param s 2020-09-12 | 2020-11-12 11:30:23
*
* 解决方案:1、可以使用正则 2、判断字符串的长度
*
* @return
*/
@Override
public Date convert(String s) {
Date date = null;
try{
if(StringUtils.hasLength(s)){
//获得长度
int lenth = s.length();
if(lenth == 10){
date = DateUtil.str2Date(s,"yyyy-MM-dd");
}else{
date = DateUtil.str2Date(s,"yyyy-MM-dd hh:mm:ss");
}
}
}catch (Exception e){
e.printStackTrace();
}
return date;
}
}
4.3:在spring-mvc中配置
4.3.1:定义转换工厂
<!--定义类型转换工厂-->
<bean id="conversionServiceFactoryBean" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<!--将自定义的转换器,向转换服务工厂进行注册-->
<ref bean="stringToDateConverter"></ref>
</set>
</property>
</bean>
4.3.2:让mvc框架支持转换工厂
<!--开启注解支持-->
<mvc:annotation-driven conversion-service="conversionServiceFactoryBean"/>
3、配置视图解析器
!--配置视图解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
<!--解析JSTL标记的解析器-->
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
</bean>
配置web.xml(启动两大框架)
步骤:
1、开启SpringMVC框架支持
<!--开启SpringMVC框架支持-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
2、开启Spring框架支持
<!--开启Spring框架支持-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
3、配置过滤器
<!--配置过滤器-->
<!--配置乱码过滤器-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<!--配置请求和响应时统一采用UTF-8-->
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--配置支持REST风格过滤器-->
<filter>
<filter-name>formContentFilter</filter-name>
<filter-class>org.springframework.web.filter.FormContentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>formContentFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
配置Log4J
Log4J是一套常用的日志框架
使用的时候需要编写
# DEBUG 指从DEBUG 开启记录日志 console,D,I,W,E 都是输出位置
log4j.rootLogger=DEBUG,console,D,I,W,E
# 定义console 的位置为:控制台
log4j.appender.console=org.apache.log4j.ConsoleAppender
# 定义控制台的输出字体为:黑色
log4j.appender.console.Target=System.out
# 定义控制台的显示编码集为:UTF-8
log4j.appender.console.encoding=UTF-8
# 定义控制台在显示时,需要采用:模板布局
log4j.appender.console.layout=org.apache.log4j.PatternLayout
# 具体的布局方案 %d 代表的时间 %p 日志级别 %c 发生在哪个类 %l 发生在哪一行 %m 输出的消息 %n 换行
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd hh:mm:ss} %p %c %l %m%n
# 定义Debug 的信息,输出位置为:文件
log4j.appender.D=org.apache.log4j.RollingFileAppender
# 定义文件的具体名称
log4j.appender.D.File=d://woniucode/logs/ssm-debug.log
# 定义文件的大小
log4j.appender.D.MaxFileSize=50MB
# 定义文件的保存份数
log4j.appender.D.MaxBackupIndex=5
# 定义文件从DEBUG开始记录日志
log4j.appender.D.Threshold=DEBUG
# 文件采用追加方式记录内容
log4j.appender.D.Appender=true
# 文件的编码集为:UTF-8
log4j.appender.D.encoding=UTF-8
log4j.appender.D.layout=org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern=%d{yyyy-MM-dd hh:mm:ss} %p %c %l %m%n
# 定义文件中最低记录:DEBUG基本的日志
log4j.appender.D.filter.infoFilter.levelMin=DEBUG
# 定义文件中最高记录:DEBUG基本的日志
log4j.appender.D.filter.infoFilter.levelMax=DEBUG
log4j.appender.I=org.apache.log4j.RollingFileAppender
log4j.appender.I.File=d://woniucode/logs/ssm-info.log
log4j.appender.I.MaxFileSize=50MB
log4j.appender.I.MaxBackupIndex=5
log4j.appender.I.Threshold=INFO
log4j.appender.I.Appender=true
log4j.appender.I.encoding=UTF-8
log4j.appender.I.layout=org.apache.log4j.PatternLayout
log4j.appender.I.layout.ConversionPattern=%d{yyyy-MM-dd hh:mm:ss} %p %c %l %m%n
log4j.appender.I.filter.infoFilter.levelMin=INFO
log4j.appender.I.filter.infoFilter.levelMax=INFO
log4j.appender.W=org.apache.log4j.RollingFileAppender
log4j.appender.W.File=d://woniucode/logs/ssm-warn.log
log4j.appender.W.MaxFileSize=50MB
log4j.appender.W.MaxBackupIndex=5
log4j.appender.W.Threshold=WARN
log4j.appender.W.Appender=true
log4j.appender.W.encoding=UTF-8
log4j.appender.W.layout=org.apache.log4j.PatternLayout
log4j.appender.W.layout.ConversionPattern=%d{yyyy-MM-dd hh:mm:ss} %p %c %l %m%n
log4j.appender.W.filter.infoFilter.levelMin=WARN
log4j.appender.W.filter.infoFilter.levelMax=WARN
log4j.appender.E=org.apache.log4j.RollingFileAppender
log4j.appender.E.File=d://woniucode/logs/ssm-error.log
log4j.appender.E.MaxFileSize=50MB
log4j.appender.E.MaxBackupIndex=5
log4j.appender.E.Threshold=ERROR
log4j.appender.E.Appender=true
log4j.appender.E.encoding=UTF-8
log4j.appender.E.layout=org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern=%d{yyyy-MM-dd hh:mm:ss} %p %c %l %m%n
log4j.appender.E.filter.infoFilter.levelMin=ERROR
log4j.appender.E.filter.infoFilter.levelMax=ERROR
开发步骤
1、根据原型图分析业务逻辑
根据原型图分析出:
1、登陆功能
2、新增管理员
3、修改管理员
4、删除管理员
5、分页查询
2、编写业务层接口
3、根据业务层接口,分析持久层接口
4、编写持久层接口
5、分别实现业务层,持久层的代码
6、使用junit单元测试框架,针对所有的业务逻辑进行测试
7、根据原型图和设计图,编写页面
8、编写表现层代码,完成页面和后端的交互
Mybatis的注解
@Insert //新增
@Update //修改
@Delete //删除
@Select //查询
@Results == ResultMap
@ResultType == ResultType
使用规则:简单的SQL语句就用注解,凡是需要使用动态SQL拼接的,统统使用配置!
PageHelper分页插件
PageHelper 是一个针对Mybatis 框架提供的一个分页插件
以前分页,我们是通过2个方法:count(),findList()
原来的分页查询
<select id="findAll" resultType="DeptBean">
select id,dept_name as deptName,dept_desc as deptDesc
<include refid="commonsql"></include>
limit ${p.index},${p.rows}
</select>
存在问题:分页与RDBMS 耦合读太高
如果需要切换数据库,那么SQL语句就必须要做更改
PageHelper 针对不同的RDBMS,产生不同的分页关键语句
pageHelper支持12种数据库,eg: oracle,mysql,sqlserver,sqllite,pgSQL……
具体可查看官网:https://pagehelper.github.io/docs/howtouse/
Spring如何使用PagerHelper的步骤
1、导入依赖关系
<!-- 引入PageHelper分页Jar包 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.4</version>
</dependency>
2、修改applicationContext.xml中SessionFactory,完成插件配置
<!--配置SessionFactory-->
<bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<!--给JavaBean 定义别名:放置的是 JavaBean 所在的目录-->
<property name="typeAliasesPackage" value="com.woniuxy.ssm.bean"></property>
<!--额外的配置-->
<property name="configuration">
<bean class="org.apache.ibatis.session.Configuration">
<!--开启延迟加载-->
<property name="lazyLoadingEnabled" value="true"></property>
<property name="aggressiveLazyLoading" value="false"></property>
<!--开启Log4j的日志记录-->
<property name="logImpl" value="org.apache.ibatis.logging.log4j.Log4jImpl"></property>
</bean>
</property>
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor">
<property name="properties">
<props>
<!-- helperDialect 方言,实际上就是告诉PageHelper插件,我们使用的是什么数据库
oracle,mysql,mariadb,sqlite,hsqldb,postgresql,db2,sqlserver,informix,h2,sqlserver2012,derby -->
<prop key="helperDialect">mysql</prop>
<!--使用 RowBounds 分页会进行 count 查询。-->
<prop key="rowBoundsWithCount">true</prop>
<!--分页合理化参数:当pageNum 为负数时,默认查询第1页数据-->
<prop key="reasonable">true</prop>
<!--更多的参数,请参见官网:https://pagehelper.github.io/docs/howtouse/-->
</props>
</property>
</bean>
</array>
</property>
</bean>
3、在mapper层添加分页方法
/**
* 分页查询
* @param adminBean
* @return
*/
List<AdminBean> findAllByParams(@Param("a") AdminBean adminBean);
4、针对mapper写配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis//DTD Mapper 3.0//EN"
"http://mybatis/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ssm.mapper.AdminMapper">
<resultMap id="pageMap" type="adminBean">
<id property="id" column="id" javaType="long"/>
<result property="adminName" column="admin_name" javaType="string"/>
<result property="loginName" column="login_name" javaType="string"/>
<result property="telphone" column="telphone" javaType="string"/>
<result property="createTime" column="create_time" javaType="java.util.Date"/>
<result property="status" column="status" javaType="int"/>
<association property="deptBean" javaType="deptBean">
<id property="id" column="dId" javaType="int"></id>
<result property="deptName" column="dept_name" javaType="string"></result>
</association>
<association property="roleBean" javaType="roleBean">
<id property="id" column="rId" javaType="int"></id>
<result property="roleName" column="role_name" javaType="string"></result>
</association>
</resultMap>
<select id="findAllByParams" resultMap="pageMap">
select a.id ,a.admin_name,a.login_name,a.telphone,a.create_time,a.status,d.id as dId,r.id as rId ,d.dept_name ,r.role_name
from admin_info as a,dept_info as d,role_info as r
<where>
a.fk_dept_id=d.id and a.fk_role_id=r.id
<if test="adminName!=null">
and admin_name like concat(#{adminName},'%')
</if>
<if test="loginName!=null">
and login_name like concat(#{loginName},'%')
</if>
<if test="telphone!=null">
and telphone like concat(#{telphone},'%')
</if>
</where>
</select>
</mapper>
5、在业务层添加分页方法及其方法的实现
@Override
public PageInfo<AdminBean> findAllByParams(int pageNum, int pageSize, AdminBean adminBean) {
return PageHelper.startPage(pageNum,pageSize).doSelectPageInfo(()->adminMapper.findAllByParams(adminBean));
}
6、在pom中添加资源的路径配置
<!--指定配置文件的位置-->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
也可以在applicationContext.xml中进行配置
在sessionFactory中配置
<!--配置mybatis的mapper映射文件的位置-->
<property name="mapperLocations" value="classpath*:mapper/*.xml"></property>
版权声明:本文标题:Java常用框架笔记(1) 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/jishu/1726362554h944981.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论