学习总结
2024-01-09 14:36:48 6 举报
AI智能生成
部分学习笔记,仅供参考,互相学习加油
作者其他创作
大纲/内容
Java学习
Linux是基于Unix的Linux是一种自由和开放源码的操作系统,存在着许多不同的Linux版本,但它们都使用了Linux内核。Linux可安装在各种计算机硬件设备中,比如手机、平板电脑、路由器、台式计算机
概述
主流版本
VMware Linux系统
安装
目录结构
Linux基础
切换目录命令cd
列出文件列表:ls ll
创建目录和移除目录:mkdir rmdir(-p级联创建和删除的符号)
由第一行开始显示文件内
cat
从最后一行开始显示
tac
显示的时候,顺道输出行号
nl
展示一个页面,如果过多,通过空格展示下一个页面
more
和more差不多,但是可以通过 上下键进行查看
less
tail命令是在实际使用过程中使用非常多的一个命令,它的功能是:用于显示文件后几行的内容。用法:tail -10 /etc/passwd 查看后10行数据tail -f catalina.log 动态查看日志(*****)
tail
浏览文件
rm -f a.txt 不询问,直接删除rm 删除目录rm -r a 递归删除不询问递归删除(慎用)rm -rf a 不询问递归删除rm -rf * 删除所有文件rm -rf /* 自杀
rm 删除文件用法:rm [选项]... 文件...
cp(copy)命令可以将文件从一处复制到另一处。一般在使用cp命令时将一个文件复制成另一个文件或复制到某目录时,需要指定源文件名与目标文件名或目录。cp a.txt b.txt 将a.txt复制为b.txt文件cp a.txt ../ 将a.txt文件复制到上一层目录中mv 移动或者重命名mv a.txt ../ 将a.txt文件移动到上一层目录中mv a.txt b.txt 将a.txt文件重命名为b.txt
cp、mv
-c:创建一个新tar文件-v:显示运行过程的信息-f:指定文件名-z:调用gzip压缩命令进行压缩-t:查看压缩文件的内容-x:解开tar文件打包:tar –cvf xxx.tar ./*打包并且压缩:tar –zcvf xxx.tar.gz ./* 解压 tar –xvf xxx.tartar -zxvf xxx.tar.gz -C /usr/aaa
tar
find指令用于查找符合条件的文件示例:find / -name “ins*” 查找文件名称是以ins开头的文件find / -name “ins*” –ls find / –user itcast –ls 查找用户itcast的文件find / –user itcast –type d –ls 查找用户itcast的目录find /-perm -777 –type d-ls 查找权限是777的文件
find
查找文件里符合条件的字符串。用法: grep [选项]... PATTERN [FILE]...示例:grep lang anaconda-ks.cfg 在文件中查找langgrep lang anaconda-ks.cfg –color 高亮显示
grep
文件操作
显示当前所在目录
pwd
创建一个空文件* touch a.txt
touch
clear crtl+L
其它常用命令
快捷键:dd – 快速删除一行yy - 复制当前行nyy - 从当前行向后复制几行p - 粘贴R – 替换x 删除当前光标所在处的字符
在Linux下一般使用vi编辑器来编辑文件。vi既可以查看文件也可以编辑文件。三种模式:命令行、插入、底行模式。切换到命令行模式:按Esc键;切换到插入模式:按 i 、o、a键; i 在当前位置前插入 I 在当前行首插入 a 在当前位置后插入 A 在当前行尾插入 o 在当前行之后插入一行 O 在当前行之前插入一行
Vi和Vim编辑器
cat /etc/passwd > a.txt 将输出定向到a.txt中cat /etc/passwd >> a.txt 输出并且追加ifconfig > ifconfig.txt
重定向输出>和>>
ps 正在运行的某个进程的状态ps –ef 查看所有进程ps –ef | grep ssh 查找某一进程kill 2868 杀掉2868编号的进程kill -9 2868 强制杀死进程
系统管理命令
管道是Linux命令中重要的一个概念,其作用是将一个命令的输出用作另一个命令的输入。示例ls --help | more 分页查询帮助信息ps –ef | grep java 查询名称中包含java的进程ifconfig | morecat index.html | moreps –ef | grep aio
管道
权限命令
普通文件: 包括文本文件、数据文件、可执行的二进制程序文件等。 目录文件: Linux系统把目录看成是一种特殊的文件,利用它构成文件系统的树型结构。 设备文件: Linux系统把每一个设备都看成是一个文件
Linux的三种文件形式
普通文件(-)目录(d)符号链接(l)* 进入etc可以查看,相当于快捷方式字符设备文件(c)块设备文件(s)套接字(s)命名管道(p)
文件类型标识
文件权限变更
-c comment 指定一段注释性描述。-d 目录 指定用户主目录,如果此目录不存在,则同时使用-m选项,可以创建主目录。-g 用户组 指定用户所属的用户组。-G 用户组,用户组 指定用户所属的附加组。-m 使用者目录如不存在则自动建立。-s Shell文件 指定用户的登录Shell。-u 用户号 指定用户的用户号,如果同时有-o选项,则可以重复使用其他用户的标识号
增加账户——useradd
常用的选项是 -r,它的作用是把用户的主目录一起删除。
userdel 选项 用户名
删除账户
usermod 选项 用户名
修改帐号
-l 锁定口令,即禁用账号。-u 口令解锁。-d 使账号无口令。-f 强迫用户下次登录时修改口令
指定和修改用户口令的Shell命令是 passwd 。超级用户可以为自己和其他用户指定口令,普通用户只能用它修改自己的口令。
用户口令的管理
账号操作
df命令参数功能:检查文件系统的磁盘空间占用情况。可以利用该命令来获取硬盘被占用了多少空间,目前还剩下多少空间等信息。
# 将系统内所有的文件系统列出来!# 在 Linux 底下如果 df 没有加任何选项# 那么默认会将系统内所有的 (不含特殊内存内的文件系统与 swap) 都以 1 Kbytes 的容量来列出来![root@fzl /]# dfFilesystem 1K-blocks Used Available Use% Mounted ondevtmpfs 889100 0 889100 0% /devtmpfs 899460 704 898756 1% /dev/shmtmpfs 899460 496 898964 1% /runtmpfs 899460 0 899460 0% /sys/fs/cgroup/dev/vda1 41152812 6586736 32662368 17% /tmpfs 179896 0 179896 0% /run/user/0
# 将容量结果以易读的容量格式显示出来[root@fzl/]# df -hFilesystem Size Used Avail Use% Mounted ondevtmpfs 869M 0 869M 0% /devtmpfs 879M 708K 878M 1% /dev/shmtmpfs 879M 496K 878M 1% /runtmpfs 879M 0 879M 0% /sys/fs/cgroup/dev/vda1 40G 6.3G 32G 17% /tmpfs 176M 0 176M 0% /run/user/0
# 将系统内的所有特殊文件格式及名称都列出来[root@fzl/]# df -aTFilesystem Type 1K-blocks Used Available Use% Mounted onsysfs sysfs 0 0 0 - /sysproc proc 0 0 0 - /procdevtmpfs devtmpfs 889100 0 889100 0% /devsecurityfs securityfs 0 0 0 -/sys/kernel/securitytmpfs tmpfs 899460 708 898752 1% /dev/shmdevpts devpts 0 0 0 - /dev/ptstmpfs tmpfs 899460 496 898964 1% /runtmpfs tmpfs 899460 0 899460 0% /sys/fs/cgroup
# 将 /etc 底下的可用的磁盘容量以易读的容量格式显示[root@kuangshen /]# df -h /etcFilesystem Size Used Avail Use% Mounted on/dev/vda1 40G 6.3G 32G 17% /
测试
df :列出文件系统的整体磁盘使用量
-a :列出所有的文件与目录容量,因为默认仅统计目录底下的文件量而已。-h :以人们较易读的容量格式 (G/M) 显示;-s :列出总量而已,而不列出每个各别的目录占用容量;-S :不包括子目录下的总计,与 -s 有点差别。-k :以 KBytes 列出容量显示;-m :以 MBytes 列出容量显示;
du [-ahskm] 文件或目录名称
Linux du命令也是查看使用空间的,但是与df命令不同的是Linux du命令是对文件和目录磁盘使用的空间的查看,还是和df命令有一些区别的,这里介绍Linux du命令
# 只列出当前目录下的所有文件夹容量(包括隐藏文件夹):# 直接输入 du 没有加任何选项时,则 du 会分析当前所在目录的文件与目录所占用的硬盘空间。[root@kuangshen home]# du16 ./redis8 ./www/.oracle_jre_usage # 包括隐藏文件的目录24 ./www48 . # 这个目录(.)所占用的总量
# 将文件的容量也列出来[root@fzl home]# du -a4 ./redis/.bash_profile4 ./redis/.bash_logout ....中间省略....4 ./kuangstudy.txt # 有文件的列表了48 .
# 检查根目录底下每个目录所占用的容量[root@fzl home]# du -sm /*0 /bin146 /boot.....中间省略....0 /proc.....中间省略....1 /tmp3026 /usr # 系统初期最大就是他了啦!513 /var2666 /www
du:检查磁盘空间使用量
Linux 的磁盘挂载使用 mount 命令,卸载使用 umount 命令
mount [-t 文件系统] [-L Label名] [-o 额外选项] [-n] 装置文件名 挂载点
磁盘挂载语法
# 将 /dev/hdc6 挂载到 /mnt/hdc6 上面![root@www ~]# mkdir /mnt/hdc6[root@www ~]# mount /dev/hdc6 /mnt/hdc6[root@www ~]# dfFilesystem 1K-blocks Used Available Use% Mounted on/dev/hdc6 1976312 42072 1833836 3% /mnt/hdc6
磁盘挂载与删除
Linux磁盘管理常用三个命令为 df、du 和 fdisk
磁盘管理
# 查看防火墙规则firewall-cmd --list-all # 查看全部信息firewall-cmd --list-ports # 只看端口信息
命令含义:--zone #作用域--add-port=80/tcp #添加端口,格式为:端口/通讯协议--permanent #永久生效,没有此参数重启后失效
# 开启端口开端口命令:firewall-cmd --zone=public --add-port=80/tcp --permanent重启防火墙:systemctl restart firewalld.service
1、安装好了Java环境后我们可以测试下Tomcat!准备好Tomcat的安装包!2、将文件移动到/usr/tomcat/下,并解压3、运行Tomcat,进入bin目录,和我们以前在Windows下看的都是一样的4、确保Linux的防火墙端口是开启的,如果是阿里云,需要保证阿里云的安全组策略是开放的
Tomcat安装使用
配置主机名
配置IP
重启网络
上网操作
① vi /etc/profile② 在末尾行添加 #set java environment JAVA_HOME=/usr/local/jdk/jdk1.7.0_71 CLASSPATH=.:$JAVA_HOME/lib.tools.jar PATH=$JAVA_HOME/bin:$PATH export JAVA_HOME CLASSPATH PATH保存退出
* 通常将软件安装到/usr/local* 直接解压就可以 tar –xvf jdk.tar.gz -C 目标路径
* 上传JDK* 卸载open-JDK # 查看jdk版本java –version# 查看安装的jdk信息rpm -qa | grep java# 卸载jdkrpm -e --nodeps java-1.6.0-openjdk-1.6.0.35-1.13.7.1.el6_6.i686rpm -e --nodeps java-1.7.0-openjdk-1.7.0.79-2.5.5.4.el6.i686
在Linux上安装JDK
一款很强大的http命令行工具。它支持文件的上传和下载,是综合传输工具学习地址:https://www.cnblogs.com/duhuo/p/5695256.html
curl
Linux命令
Linux
版本控制(Revision control)是一种在开发的过程中用于管理我们对文件、目录或工程等内容的修改历史,方便查看更改历史记录,备份以便恢复以前的版本的软件工程技术
本地版本控制
分布式版本控制
集中版本控制
#查看系统configgit config --system --list#查看当前用户(global)配置git config --global --list
查看配置git config -l
Git相关的配置文件:1)、Git\\mingw64\\etc\\gitconfig : Git 安装目录下的 gitconfig --system 系统级2)、C:\\Users\\Administrator\\ .gitconfig 只适用于当前登录用户的配置 --global 全局
git config --global user.name \"kuangshen\" #名称git config --global user.email 24736743@qq.com #邮箱
设置用户名和邮箱
Git相关配置文件
Git启动
Git的环境配置
Git配置
# 在当前目录新建一个Git代码库$ git init
本地仓库搭建
# 克隆一个项目和它的整个代码历史(版本信息)$ git clone [url]
克隆远程仓库
#查看指定文件状态git status [filename]#查看所有文件状态git status
查看文件状态
# 列出所有本地分支git branch# 列出所有远程分支git branch -r# 新建一个分支,但依然停留在当前分支git branch [branch-name]# 新建一个分支,并切换到该分支git checkout -b [branch]# 合并指定分支到当前分支$ git merge [branch]# 删除分支$ git branch -d [branch-name]# 删除远程分支$ git push origin --delete [branch-name]$ git branch -dr [remote/branch]
git常用命令
Git项目搭建
$ git init$ touch README.md$ git add README.md$ git commit -m 'first_commit'$ git remote add origin https://github.com/jerryhanjj/baike_spider.git$ git push origin master
上传文件
上传项目跟踪项目文件夹中的所有文件和文件夹$ git add . 输入本次的提交说明,准备提交暂存区中的更改的已跟踪文件,单引号内为说明内容$ git commit -m 'first_commit'关联远程仓库,添加后,远程库的名字就是 origin,这是 Git 默认的叫法,也可以改成别的,但是 origin 这个名字一看就知道是远程库。$ git remote add origin https://github.com/jerryhanjj/baike_spider.git如果关联出现错误 fatal: remote origin already exists,则执行下列语句再进行关联git remote rm origin把本地库的所有内容推送到远程库上$ git push -u origin master如果在推送时出现错误 error:failed to push som refs to.......,则执行下列语句git pull origin master将远程仓库 Github 上的文件拉下来合并之后重新推送上去,如果在推送时失败,提示fatal: refusing to merge unrelated histories,则执行下列语句git pull origin master --allow-unrelated-histories
上传项目
案例
使用详解
Git
项目对象模型 (POM:Project Object Model)
https://maven.apache.org/download.cgi
选择合适版本
将下载好的安装包解压到无中文的路径下
bin:存放了 maven 的命令,比如我们前面用到的 mvn tomcat:runboot:存放了一些 maven 本身的引导程序,如类加载器等conf:存放了 maven 的一些配置文件,如 setting.xml 文件lib:存放了 maven 本身运行所需的一些 jar 包
目录结构
构建Maven的环境变量
环境变量测试
mvn -v
Maven的目录结构
下载
compile 是 maven 工程的编译命令,作用是将 src/main/java 下的文件编译为 class 文件输出到 target目录下。cmd 进入命令状态,执行 mvn compile
test 是 maven 工程的测试命令 mvn test,会执行 src/test/java 下的单元测试类。cmd 执行 mvn test 执行 src/test/java 下单元测试类
clean 是 maven 工程的清理命令,执行 clean 会删除 target 目录及内容
package 是 maven 工程的打包命令,对于 java 工程执行 package 打成 jar 包,对于 web 工程打成 war包
install 是 maven 工程的安装命令,执行 install 将 maven 打成 jar 包或 war 包发布到本地仓库
常用命令
Maven 包含了一个项目对象模型 (Project Object Model),一组标准集合,一个项目生命周期(Project Lifecycle),一个依赖管理系统(Dependency Management System),和用来运行定义在生命周期阶段(phase)中插件(plugin)目标(goal)的逻辑
概念模型
概念
分模块开发与设计
<packaging>pom</packaging>
创建一个空模块,打包类型定义为pom
<modules> <module>../ssm_controller</module> <module>../ssm_service</module> <module>../ssm_dao</module> <module>../ssm_pojo</module></modules>
定义当前模块进行构建操作时定义其它模块
用于快速构建maven项目,一次构建多个项目和模块
聚合
=
<!-- 申明此处进行依赖管理--> <dependencyManagement><!--具体的依赖 --> <dependencies><!--spring环境 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.9.RELEASE</version> </dependency> <dependencies> <dependencyManagement>
继承
版本管理
资源管理
多环境开发配置
跳过测试
私服
Maven
版本控制工具
Junit单元测试
反射
注解
基础加强
Java DataBase Connectivity Java 数据库连接, Java语言操作数据库
* 步骤: 1. 导入驱动jar包 mysql-connector-java-5.1.37-bin.jar 1.复制mysql-connector-java-5.1.37-bin.jar到项目的libs目录下 2.右键-->Add As Library 2. 注册驱动 3. 获取数据库连接对象 Connection 4. 定义sql 5. 获取执行sql语句的对象 Statement 6. 执行sql,接受返回结果 7. 处理结果 8. 释放资源
连接步骤
代码实现: //1. 导入驱动jar包 //2.注册驱动 Class.forName(\"com.mysql.jdbc.Driver\"); //3.获取数据库连接对象 Connection conn = DriverManager.getConnection(\"jdbc:mysql://localhost:3306/db3\
代码实现
1. DriverManager:驱动管理对象 * 功能: 1. 注册驱动:告诉程序该使用哪一个数据库驱动jar static void registerDriver(Driver driver) :注册与给定的驱动程序 DriverManager 。 写代码使用: Class.forName(\"com.mysql.jdbc.Driver\"); 通过查看源码发现:在com.mysql.jdbc.Driver类中存在静态代码块 static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException(\"Can't register driver!\
2. Connection:数据库连接对象 1. 功能: 1. 获取执行sql 的对象 * Statement createStatement() * PreparedStatement prepareStatement(String sql) 2. 管理事务: * 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务 * 提交事务:commit() * 回滚事务:rollback()
3. Statement:执行sql的对象 1. 执行sql 1. boolean execute(String sql) :可以执行任意的sql 了解 2. int executeUpdate(String sql) :执行DML(insert、update、delete)语句、DDL(create,alter、drop)语句 * 返回值:影响的行数,可以通过这个影响的行数判断DML语句是否执行成功 返回值>0的则执行成功,反之,则失败。 3. ResultSet executeQuery(String sql) :执行DQL(select)语句 2. 练习: 1. account表 添加一条记录 2. account表 修改记录 3. account表 删除一条记录 代码: Statement stmt = null; Connection conn = null; try { //1. 注册驱动 Class.forName(\"com.mysql.jdbc.Driver\"); //2. 定义sql String sql = \
* 定义一个方法,查询emp表的数据将其封装为对象,然后装载集合,返回。 1. 定义Emp类 2. 定义方法 public List<Emp> findAll(){} 3. 实现方法 select * from emp;
练习
配置对象的详解
* 目的:简化书写 * 分析: 1. 注册驱动也抽取 2. 抽取一个方法获取连接对象 * 需求:不想传递参数(麻烦),还得保证工具类的通用性。 * 解决:配置文件 jdbc.properties url= user= password= 3. 抽取一个方法释放资源 * 代码实现: public class JDBCUtils { private static String url; private static String user; private static String password; private static String driver; /** * 文件的读取,只需要读取一次即可拿到这些值。使用静态代码块 */ static{ //读取资源文件,获取值。 try { //1. 创建Properties集合类。 Properties pro = new Properties(); //获取src路径下的文件的方式--->ClassLoader 类加载器 ClassLoader classLoader = JDBCUtils.class.getClassLoader(); URL res = classLoader.getResource(\"jdbc.properties\"); String path = res.getPath(); System.out.println(path);///D:/IdeaProjects/itcast/out/production/day04_jdbc/jdbc.properties //2. 加载文件 // pro.load(new FileReader(\"D:\\\\IdeaProjects\\\\itcast\\\\day04_jdbc\\\\src\\\\jdbc.properties\")); pro.load(new FileReader(path)); //3. 获取数据,赋值 url = pro.getProperty(\"url\"); user = pro.getProperty(\"user\"); password = pro.getProperty(\"password\"); driver = pro.getProperty(\"driver\
JDBC UTILS
* 练习: * 需求: 1. 通过键盘录入用户名和密码 2. 判断用户是否登录成功 * select * from user where username = \"\" and password = \"\
事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
事务
操作: 1. 开启事务 2. 提交事务 3. 回滚事务
流程
* 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务 * 在执行sql之前开启事务 * 提交事务:commit() * 当所有sql都执行完提交事务 * 回滚事务:rollback() * 在catch中回滚事务
使用Connection对象来管理事务
代码: public class JDBCDemo10 { public static void main(String[] args) { Connection conn = null; PreparedStatement pstmt1 = null; PreparedStatement pstmt2 = null; try { //1.获取连接 conn = JDBCUtils.getConnection(); //开启事务 conn.setAutoCommit(false); //2.定义sql //2.1 张三 - 500 String sql1 = \"update account set balance = balance - ? where id = ?\"; //2.2 李四 + 500 String sql2 = \"update account set balance = balance + ? where id = ?\
代码
JDBC控制事务
JDBC
概念:其实就是一个容器(集合),存放数据库连接的容器。
1. 节约资源 2. 用户访问高效
好处
1. 标准接口:DataSource javax.sql包下的 1. 方法: * 获取连接:getConnection() * 归还连接:Connection.close()。如果连接对象Connection是从连接池中获取的, 那么调用Connection.close()方法,则不会再关闭连接了。而是归还连接 2. 一般我们不去实现它,有数据库厂商来实现 1. C3P0:数据库连接池技术 2. Druid:数据库连接池实现技术,由阿里巴巴提供的
实现
* 步骤: 1. 导入jar包 (两个) c3p0-0.9.5.2.jar mchange-commons-java-0.2.12.jar , * 不要忘记导入数据库驱动jar包 2. 定义配置文件: * 名称: c3p0.properties 或者 c3p0-config.xml * 路径:直接将文件放在src目录下即可。 3. 创建核心对象 数据库连接池对象 ComboPooledDataSource 4. 获取连接: getConnection * 代码: //1.创建数据库连接池对象 DataSource ds = new ComboPooledDataSource(); //2. 获取连接对象 Connection conn = ds.getConnection();
C3P0
1. 步骤: 1. 导入jar包 druid-1.0.9.jar 2. 定义配置文件: * 是properties形式的 * 可以叫任意名称,可以放在任意目录下 3. 加载配置文件。Properties 4. 获取数据库连接池对象:通过工厂来来获取 DruidDataSourceFactory 5. 获取连接:getConnection * 代码: //3.加载配置文件 Properties pro = new Properties(); InputStream is = DruidDemo.class.getClassLoader().getResourceAsStream(\"druid.properties\"); pro.load(is); //4.获取连接池对象 DataSource ds = DruidDataSourceFactory.createDataSource(pro); //5.获取连接 Connection conn = ds.getConnection(); 2. 定义工具类 1. 定义一个类 JDBCUtils 2. 提供静态代码块加载配置文件,初始化连接池对象 3. 提供方法 1. 获取连接方法:通过数据库连接池获取连接 2. 释放资源 3. 获取连接池的方法 * 代码: public class JDBCUtils { //1.定义成员变量 DataSource private static DataSource ds ; static{ try { //1.加载配置文件 Properties pro = new Properties(); pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream(\"druid.properties\
Druid
* Spring框架对JDBC的简单封装。提供了一个JDBCTemplate对象简化JDBC的开发 * 步骤: 1. 导入jar包 2. 创建JdbcTemplate对象。依赖于数据源DataSource * JdbcTemplate template = new JdbcTemplate(ds); 3. 调用JdbcTemplate的方法来完成CRUD的操作 * update():执行DML语句。增、删、改语句 * queryForMap():查询结果将结果集封装为map集合,将列名作为key,将值作为value 将这条记录封装为一个map集合 * 注意:这个方法查询的结果集长度只能是1 * queryForList():查询结果将结果集封装为list集合 * 注意:将每一条记录封装为一个Map集合,再将Map集合装载到List集合中 * query():查询结果,将结果封装为JavaBean对象 * query的参数:RowMapper * 一般我们使用BeanPropertyRowMapper实现类。可以完成数据到JavaBean的自动封装 * new BeanPropertyRowMapper<类型>(类型.class) * queryForObject:查询结果,将结果封装为对象 * 一般用于聚合函数的查询
Spring JDBC
* 需求: 1. 修改1号数据的 salary 为 10000 2. 添加一条记录 3. 删除刚才添加的记录 4. 查询id为1的记录,将其封装为Map集合 5. 查询所有记录,将其封装为List 6. 查询所有记录,将其封装为Emp对象的List集合 7. 查询总记录数 * 代码: import cn.itcast.domain.Emp; import cn.itcast.utils.JDBCUtils; import org.junit.Test; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import java.sql.Date; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import java.util.Map; public class JdbcTemplateDemo2 { //Junit单元测试,可以让方法独立执行 //1. 获取JDBCTemplate对象 private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource()); /** * 1. 修改1号数据的 salary 为 10000 */ @Test public void test1(){ //2. 定义sql String sql = \"update emp set salary = 10000 where id = 1001\"; //3. 执行sql int count = template.update(sql); System.out.println(count); } /** * 2. 添加一条记录 */ @Test public void test2(){ String sql = \
JDBC连接池
JDBC连接池和JDBCtemplate
. 文件标签:构成html最基本的标签 * html:html文档的根标签 * head:头标签。用于指定html文档的一些属性。引入外部的资源 * title:标题标签。 * body:体标签 * <!DOCTYPE html>:html5中定义该文档是html文档
<!DOCTYPE html> <html lang=\"ch\"> <head> <meta charset=\"UTF-8\"> <title>黑马程序员简介</title> </head> <body> <h1> 公司简介 </h1> <hr color=\"#ffd700\"> <p> <font color=\"#FF0000\">\"中关村黑马程序员训练营\"</font>是由<b><i>传智播客</i></b>联合中关村软件园、CSDN, 并委托传智播客进行教学实施的软件开发高端培训机构,致力于服务各大软件企业,解决当前软件开发技术飞速发展, 而企业招不到优秀人才的困扰。 </p> <p> 目前,“中关村黑马程序员训练营”已成长为行业“学员质量好、课程内容深、企业满意”的移动开发高端训练基地, 并被评为中关村软件园重点扶持人才企业。 </p> <p> 黑马程序员的学员多为大学毕业后,有理想、有梦想,想从事IT行业,而没有环境和机遇改变自己命运的年轻人。 黑马程序员的学员筛选制度,远比现在90%以上的企业招聘流程更为严格。任何一名学员想成功入学“黑马程序员”, 必须经历长达2个月的面试流程,这些流程中不仅包括严格的技术测试、自学能力测试,还包括性格测试、压力测试、 品德测试等等测试。毫不夸张地说,黑马程序员训练营所有学员都是精挑细选出来的。百里挑一的残酷筛选制度确 保学员质量,并降低企业的用人风险。 中关村黑马程序员训练营不仅着重培养学员的基础理论知识,更注重培养项目实施管理能力,并密切关注技术革新, 不断引入先进的技术,研发更新技术课程,确保学员进入企业后不仅能独立从事开发工作,更能给企业带来新的技术体系和理念。 </p> <p> 一直以来,黑马程序员以技术视角关注IT产业发展,以深度分享推进产业技术成长,致力于弘扬技术创新,倡导分享、 开放和协作,努力打造高质量的IT人才服务平台。 </p> <hr color=\"#ffd700\"> <font color=\"gray\" size=\"2\
公司介绍
图片标签: * img:展示图片 * 属性: * src:指定图片的位置 * 代码: <!--展示一张图片 img--> <img src=\"image/jingxuan_2.jpg\" align=\"right\" alt=\"古镇\" width=\"500\" height=\"500\"/> <!-- 相对路径 * 以.开头的路径 * ./:代表当前目录 ./image/1.jpg * ../:代表上一级目录 --> <img src=\"./image/jiangwai_1.jpg\"> <img src=\"../image/jiangwai_1.jpg\">
列表标签: * 有序列表: * ol: * li: * 无序列表: * ul: * li:
链接标签: * a:定义一个超链接 * 属性: * href:指定访问资源的URL(统一资源定位符) * target:指定打开资源的方式 * _self:默认值,在当前页面打开 * _blank:在空白页面打开 * 代码: <!--超链接 a--> <a href=\"http://www.itcast.cn\">点我</a> <br> <a href=\"http://www.itcast.cn\" target=\"_self\">点我</a> <br> <a href=\"http://www.itcast.cn\" target=\"_blank\">点我</a> <br> <a href=\"./5_列表标签.html\">列表标签</a><br> <a href=\"mailto:itcast@itcast.cn\">联系我们</a> <br> <a href=\"http://www.itcast.cn\"><img src=\"image/jiangwai_1.jpg\"></a>
div和span: * div:每一个div占满一整行。块级标签 * span:文本信息在一行展示,行内标签 内联标签
语义化标签:html5中为了提高程序的可读性,提供了一些标签。 1. <header>:页眉 2. <footer>:页脚
## 案例:旅游网站首页 1. 确定使用table来完成布局 2. 如果某一行只有一个单元格,则使用<tr><td></td></tr> 3. 如果某一行有多个单元格,则使用 <tr> <td> <table></table> </td> </tr> 4. 代码实现 <!DOCTYPE html> <html lang=\"en\"> <head> <meta charset=\"UTF-8\"> <title>黑马旅游网</title> </head> <body> <!--采用table来完成布局--> <!--最外层的table,用于整个页面的布局--> <table width=\"100%\" align=\"center\"> <!-- 第1行 --> <tr> <td> <img src=\"image/top_banner.jpg\" width=\"100%\" alt=\"\"> </td> </tr> <!-- 第2行 --> <tr> <td> <table width=\"100%\" align=\"center\"> <tr> <td> <img src=\"image/logo.jpg\" alt=\"\"> </td> <td> <img src=\"image/search.png\" alt=\"\"> </td> <td> <img src=\"image/hotel_tel.png\" alt=\"\"> </td> </tr> </table> </td> </tr> <!-- 第3行 --> <tr> <td> <table width=\"100%\" align=\"center\"> <tr bgcolor=\"#ffd700\" align=\"center\" height=\"45\" > <td> <a href=\"\">首页</a> </td> <td> 门票 </td> <td> 门票 </td> <td> 门票 </td> <td> 门票 </td> <td> 门票 </td> <td> 门票 </td> <td> 门票 </td> <td> 门票 </td> <td> 门票 </td> </tr> </table> </td> </tr> <!-- 第4行 轮播图 --> <tr> <td> <img src=\"image/banner_3.jpg\" alt=\"\" width=\"100%\"> </td> </tr> <!-- 第5行 黑马精选--> <tr> <td> <img src=\"image/icon_5.jpg\" alt=\"\"> 黑马精选 <hr color=\"#ffd700\" > </td> </tr> <!-- 第6行 --> <tr> <td> <table align=\"center\" width=\"95%\"> <tr> <td> <img src=\"image/jiangxuan_1.jpg\" alt=\"\"> <p>上海飞三亚五天4晚自由行(春节销售+亲子+蜜月+自由行)</p> <font color=\"red\">¥ 899</font> </td> <td> <img src=\"image/jiangxuan_1.jpg\" alt=\"\"> <p>上海飞三亚五天4晚自由行(春节销售+亲子+蜜月+自由行)</p> <font color=\"red\">¥ 899</font> </td> <td> <img src=\"image/jiangxuan_1.jpg\" alt=\"\"> <p>上海飞三亚五天4晚自由行(春节销售+亲子+蜜月+自由行)</p> <font color=\"red\">¥ 899</font> </td> <td> <img src=\"image/jiangxuan_1.jpg\" alt=\"\"> <p>上海飞三亚五天4晚自由行(春节销售+亲子+蜜月+自由行)</p> <font color=\"red\">¥ 899</font> </td> </tr> </table> </td> </tr> <!-- 第7行 国内游 --> <tr> <td> <img src=\"image/icon_6.jpg\" alt=\"\"> 国内游 <hr color=\"#ffd700\" > </td> </tr> <!-- 第8行 --> <tr> <td> <table align=\"center\" width=\"95%\"> <tr> <td rowspan=\"2\"> <img src=\"image/guonei_1.jpg\" alt=\"\"> </td> <td> <img src=\"image/jiangxuan_2.jpg\" alt=\"\" height=\"100%\"> <p>上海飞三亚五天4晚自由行(春节销售+亲子+蜜月+自由行)</p> <font color=\"red\">¥ 699</font> </td> <td> <img src=\"image/jiangxuan_2.jpg\" alt=\"\"> <p>上海飞三亚五天4晚自由行(春节销售+亲子+蜜月+自由行)</p> <font color=\"red\">¥ 699</font> </td> <td> <img src=\"image/jiangxuan_2.jpg\" alt=\"\"> <p>上海飞三亚五天4晚自由行(春节销售+亲子+蜜月+自由行)</p> <font color=\"red\">¥ 699</font> </td> </tr> <tr> <td> <img src=\"image/jiangxuan_2.jpg\" alt=\"\"> <p>上海飞三亚五天4晚自由行(春节销售+亲子+蜜月+自由行)</p> <font color=\"red\">¥ 699</font> </td> <td> <img src=\"image/jiangxuan_2.jpg\" alt=\"\"> <p>上海飞三亚五天4晚自由行(春节销售+亲子+蜜月+自由行)</p> <font color=\"red\">¥ 699</font> </td> <td> <img src=\"image/jiangxuan_2.jpg\" alt=\"\"> <p>上海飞三亚五天4晚自由行(春节销售+亲子+蜜月+自由行)</p> <font color=\"red\">¥ 699</font> </td> </tr> </table> </td> </tr> <!-- 第9行 境外游 --> <tr> <td> <img src=\"image/icon_7.jpg\" alt=\"\"> 境外游 <hr color=\"#ffd700\" > </td> </tr> <!-- 第10行 --> <tr> <td> <table align=\"center\" width=\"95%\"> <tr> <td rowspan=\"2\"> <img src=\"image/jiangwai_1.jpg\" alt=\"\"> </td> <td> <img src=\"image/jiangxuan_3.jpg\" alt=\"\" height=\"100%\"> <p>上海飞三亚五天4晚自由行(春节销售+亲子+蜜月+自由行)</p> <font color=\"red\">¥ 699</font> </td> <td> <img src=\"image/jiangxuan_3.jpg\" alt=\"\"> <p>上海飞三亚五天4晚自由行(春节销售+亲子+蜜月+自由行)</p> <font color=\"red\">¥ 699</font> </td> <td> <img src=\"image/jiangxuan_3.jpg\" alt=\"\"> <p>上海飞三亚五天4晚自由行(春节销售+亲子+蜜月+自由行)</p> <font color=\"red\">¥ 699</font> </td> </tr> <tr> <td> <img src=\"image/jiangxuan_3.jpg\" alt=\"\"> <p>上海飞三亚五天4晚自由行(春节销售+亲子+蜜月+自由行)</p> <font color=\"red\">¥ 699</font> </td> <td> <img src=\"image/jiangxuan_3.jpg\" alt=\"\"> <p>上海飞三亚五天4晚自由行(春节销售+亲子+蜜月+自由行)</p> <font color=\"red\">¥ 699</font> </td> <td> <img src=\"image/jiangxuan_3.jpg\" alt=\"\"> <p>上海飞三亚五天4晚自由行(春节销售+亲子+蜜月+自由行)</p> <font color=\"red\">¥ 699</font> </td> </tr> </table> </td> </tr> <!-- 第11行 --> <tr> <td> <img src=\"image/footer_service.png\" alt=\"\" width=\"100%\"> </td> </tr> <!-- 第12行 --> <tr> <td align=\"center\" bgcolor=\"#ffd700\" height=\"40\"> <font color=\"gray\" size=\"2\
案例:旅游网页
表格标签: * table:定义表格 * width:宽度 * border:边框 * cellpadding:定义内容和单元格的距离 * cellspacing:定义单元格之间的距离。如果指定为0,则单元格的线会合为一条、 * bgcolor:背景色 * align:对齐方式 * tr:定义行 * bgcolor:背景色 * align:对齐方式 * td:定义单元格 * colspan:合并列 * rowspan:合并行 * th:定义表头单元格 * <caption>:表格标题 * <thead>:表示表格的头部分 * <tbody>:表示表格的体部分 * <tfoot>:表示表格的脚部分
* 表单项标签: * input:可以通过type属性值,改变元素展示的样式 * type属性: * text:文本输入框,默认值 * placeholder:指定输入框的提示信息,当输入框的内容发生变化,会自动清空提示信息 * password:密码输入框 * radio:单选框 * 注意: 1. 要想让多个单选框实现单选的效果,则多个单选框的name属性值必须一样。 2. 一般会给每一个单选框提供value属性,指定其被选中后提交的值 3. checked属性,可以指定默认值 * checkbox:复选框 * 注意: 1. 一般会给每一个单选框提供value属性,指定其被选中后提交的值 2. checked属性,可以指定默认值 * file:文件选择框 * hidden:隐藏域,用于提交一些信息。 * 按钮: * submit:提交按钮。可以提交表单 * button:普通按钮 * image:图片提交按钮 * src属性指定图片的路径 * label:指定输入项的文字描述信息 * 注意: * label的for属性一般会和 input 的 id属性值 对应。如果对应了,则点击label区域,会让input输入框获取焦点。 * select: 下拉列表 * 子元素:option,指定列表项 * textarea:文本域 * cols:指定列数,每一行有多少个字符 * rows:默认多少行。
* 表单: * 概念:用于采集用户输入的数据的。用于和服务器进行交互。 * form:用于定义表单的。可以定义一个范围,范围代表采集用户数据的范围 * 属性: * action:指定提交数据的URL * method:指定提交方式 * 分类:一共7种,2种比较常用 * get: 1. 请求参数会在地址栏中显示。会封装到请求行中(HTTP协议后讲解)。 2. 请求参数大小是有限制的。 3. 不太安全。 * post: 2. 请求参数不会再地址栏中显示。会封装在请求体中(HTTP协议后讲解) 2. 请求参数的大小没有限制。 3. 较为安全。 * 表单项中的数据要想被提交:必须指定其name属性
标签复习
HTML
Cascading Style Sheets 层叠样式表 * 层叠:多个样式可以作用在同一个html的元素上,同时生效
1. 功能强大 2. 将内容展示和样式控制分离 * 降低耦合度。解耦 * 让分工协作更容易 * 提高开发效率
1. 内联样式 * 在标签内使用style属性指定css代码 * 如:<div style=\"color:red;\">hello css</div>
2. 内部样式 * 在head标签内,定义style标签,style标签的标签体内容就是css代码 * 如: <style> div{ color:blue; } </style> <div>hello css</div>
3. 外部样式 1. 定义css资源文件。 2. 在head标签内,定义link标签,引入外部的资源文件 * 如: * a.css文件: div{ color:green; } <link rel=\"stylesheet\" href=\"css/a.css\"> <div>hello css</div> <div>hello css</div>
CSS的使用:CSS与html结合方式
使用
* 格式: 选择器 { 属性名1:属性值1; 属性名2:属性值2; ... } * 选择器:筛选具有相似特征的元素 * 注意: * 每一对属性需要使用;隔开,最后一对属性可以不加;
语法
1. 基础选择器 1. id选择器:选择具体的id属性值的元素.建议在一个html页面中id值唯一 * 语法:#id属性值{}
2. 元素选择器:选择具有相同标签名称的元素 * 语法: 标签名称{} * 注意:id选择器优先级高于元素选择器
3. 类选择器:选择具有相同的class属性值的元素。 * 语法:.class属性值{} * 注意:类选择器选择器优先级高于元素选择器
基础选择器
扩展选择器
分类
选择器
1. 字体、文本 * font-size:字体大小 * color:文本颜色 * text-align:对其方式 * line-height:行高 2. 背景 * background: 3. 边框 * border:设置边框,符合属性 4. 尺寸 * width:宽度 * height:高度 5. 盒子模型:控制布局 * margin:外边距 * padding:内边距 * 默认情况下内边距会影响整个盒子的大小 * box-sizing: border-box; 设置盒子的属性,让width和height就是最终盒子的大小 * float:浮动 * left * right
属性
<!DOCTYPE html> <html lang=\"en\"> <head> <meta charset=\"UTF-8\"> <title>注册页面</title> <style> *{ margin: 0px; padding: 0px; box-sizing: border-box; } body{ background: url(\"img/register_bg.png\
CSS案例
CSS
练习:99乘法表 <!DOCTYPE html> <html lang=\"en\"> <head> <meta charset=\"UTF-8\"> <title>99乘法表</title> <style> td{ border: 1px solid; } </style> <script> document.write(\"<table align='center'>\"); //1.完成基本的for循环嵌套,展示乘法表 for (var i = 1; i <= 9 ; i++) { document.write(\"<tr>\"); for (var j = 1; j <=i ; j++) { document.write(\"<td>\"); //输出 1 * 1 = 1 document.write(i + \" * \" + j + \" = \" + ( i*j) +\" \"); document.write(\"</td>\"); } /*//输出换行 document.write(\"<br>\");*/ document.write(\"</tr>\"); } //2.完成表格嵌套 document.write(\"</table>\"); </script> </head> <body> </body> </html>
练习99乘法表
<!DOCTYPE html> <html lang=\"en\"> <head> <meta charset=\"UTF-8\"> <title>电灯开关</title> </head> <body> <img id=\"light\" src=\"img/off.gif\
灯泡控制
<!DOCTYPE html><html lang=\"en\"><head> <meta charset=\"UTF-8\"> <title>轮播图</title></head><body> <img id=\"img\" src=\"img/banner_1.jpg\" width=\"100%\"> <script> /* 分析: 1.在页面上使用img标签展示图片 2.定义一个方法,修改图片对象的src属性 3.定义一个定时器,每隔3秒调用方法一次。 */ //修改图片src属性 var number = 1; function fun(){ number ++ ; //判断number是否大于3 if(number > 3){ number = 1; } //获取img对象 var img = document.getElementById(\"img\"); img.src = \"img/banner_\"+number+\".jpg\
轮播图
<!DOCTYPE html><html lang=\"en\"><head> <meta charset=\"UTF-8\"> <title>自动跳转</title> <style> p{ text-align: center; } span{ color:red; } </style></head><body> <p> <span id=\"time\">5</span>秒之后,自动跳转到首页... </p> <script> /* 分析: 1.显示页面效果 <p> 2.倒计时读秒效果实现 2.1 定义一个方法,获取span标签,修改span标签体内容,时间-- 2.2 定义一个定时器,1秒执行一次该方法 3.在方法中判断时间如果<= 0 ,则跳转到首页 */ // 2.倒计时读秒效果实现 var second = 5; var time = document.getElementById(\"time\"); //定义一个方法,获取span标签,修改span标签体内容,时间-- function showTime(){ second -- ; //判断时间如果<= 0 ,则跳转到首页 if(second <= 0){ //跳转到首页 location.href = \"https://www.baidu.com\"; } time.innerHTML = second +\"\
自动跳转首页
<!DOCTYPE html><html lang=\"en\"><head> <meta charset=\"UTF-8\
动态表格
表格全选
<!DOCTYPE html><html lang=\"en\"><head> <meta charset=\"UTF-8\"> <title>注册页面</title><style> *{ margin: 0px; padding: 0px; box-sizing: border-box; } body{ background: url(\"img/register_bg.png\
表格验证
常见的事件: 1. 点击事件: 1. onclick:单击事件 2. ondblclick:双击事件 2. 焦点事件 1. onblur:失去焦点 2. onfocus:元素获得焦点。 3. 加载事件: 1. onload:一张页面或一幅图像完成加载。 4. 鼠标事件: 1. onmousedown 鼠标按钮被按下。 2. onmouseup 鼠标按键被松开。 3. onmousemove 鼠标被移动。 4. onmouseover 鼠标移到某元素之上。 5. onmouseout 鼠标从某元素移开。 5. 键盘事件: 1. onkeydown 某个键盘按键被按下。 2. onkeyup 某个键盘按键被松开。 3. onkeypress 某个键盘按键被按下并松开。 6. 选择和改变 1. onchange 域的内容被改变。 2. onselect 文本被选中。 7. 表单事件: 1. onsubmit 确认按钮被点击。 2. onreset 重置按钮被点击。
常见的事件
JS
一个前端开发的框架,Bootstrap,来自 Twitter,是目前很受欢迎的前端框架。Bootstrap 是基于 HTML、CSS、JavaScript 的,它简洁灵活,使得 Web 开发更加快捷。
* 框架:一个半成品软件,开发人员可以在框架基础上,在进行开发,简化编码。 * 好处: 1. 定义了很多的css样式和js插件。我们开发人员直接可以使用这些样式和插件得到丰富的页面效果。 2. 响应式布局。 * 同一套页面可以兼容不同分辨率的设备。
1. 下载Bootstrap
2. 在项目中将这三个文件夹复制
3. 创建html页面,引入必要的资源文件
步骤
<!DOCTYPE html> <html lang=\"zh-CN\"> <head> <meta charset=\"utf-8\"> <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"> <meta name=\"viewport\" content=\
示例
快速入门
实现:依赖于栅格系统:将一行平均分成12个格子,可以指定元素占几个格子
1. 定义容器。相当于之前的table、 * 容器分类: 1. container:两边留白 2. container-fluid:每一种设备都是100%宽度
2. 定义行。相当于之前的tr 样式:row
3. 定义元素。指定该元素在不同的设备上,所占的格子数目。样式:col-设备代号-格子数目 * 设备代号: 1. xs:超小屏幕 手机 (<768px):col-xs-12 2. sm:小屏幕 平板 (≥768px) 3. md:中等屏幕 桌面显示器 (≥992px) 4. lg:大屏幕 大桌面显示器 (≥1200px)
注意: 1. 一行中如果格子数目超过12,则超出部分自动换行。 2. 栅格类属性可以向上兼容。栅格类适用于与屏幕宽度大于或等于分界点大小的设备。 3. 如果真实设备宽度小于了设置栅格类属性的设备代码的最小值,会一个元素沾满一整行。
响应式布局
* 按钮:class=\"btn btn-default\" * 图片: * class=\"img-responsive\":图片在任意尺寸都占100% * 图片形状 * <img src=\"...\" alt=\"...\" class=\"img-rounded\">:方形 * <img src=\"...\" alt=\"...\" class=\"img-circle\"> : 圆形 * <img src=\"...\" alt=\"...\" class=\"img-thumbnail\"> :相框 * 表格 * table * table-bordered * table-hover * 表单 * 给表单项添加:class=\"form-control\"
全局CSS样式
* 导航条* 分页条
组件
* 轮播图
插件
CSS样式和JS插件
<!DOCTYPE html> <html lang=\"zh-CN\"> <head> <meta charset=\"utf-8\"> <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"> <meta name=\"viewport\" content=\
黑马旅游网
BootStrap
Extensible Markup Language 可扩展标记语言
* 功能 * 存储数据 1. 配置文件 2. 在网络中传输
功能
* xml与html的区别 1. xml标签都是自定义的,html标签是预定义。 2. xml的语法严格,html语法松散 3. xml是存储数据的,html是展示数据
区别
* w3c:万维网联盟
* 可扩展:标签都是自定义的。 <user> <student>
1. xml文档的后缀名 .xml2. xml第一行必须定义为文档声明3. xml文档中有且仅有一个根标签4. 属性值必须使用引号(单双都可)引起来5. 标签必须正确关闭6. xml标签名称区分大小写
基本语法
<?xml version='1.0' ?> <users> <user id='1'> <name>zhangsan</name> <age>23</age> <gender>male</gender> <br/> </user> <user id='2'> <name>lisi</name> <age>24</age> <gender>female</gender> </user> </users>
1. 文档声明 1. 格式:<?xml 属性列表 ?>
2. 属性列表: * version:版本号,必须的属性 * encoding:编码方式。告知解析引擎当前文档使用的字符集,默认值:ISO-8859-1 * standalone:是否独立 * 取值: * yes:不依赖其他文件 * no:依赖其他文件
2. 指令(了解):结合css的 * <?xml-stylesheet type=\"text/css\" href=\"a.css\" ?>
3. 标签:标签名称自定义的 * 规则: * 名称可以包含字母、数字以及其他的字符 * 名称不能以数字或者标点符号开始 * 名称不能以字母 xml(或者 XML、Xml 等等)开始 * 名称不能包含空格
4. 属性: id属性值唯一
5. 文本: * CDATA区:在该区域中的数据会被原样展示 * 格式: <![CDATA[ 数据 ]]>
组成部分
* 作为框架的使用者(程序员): 1. 能够在xml中引入约束文档 2. 能够简单的读懂约束文档
* DTD: * 引入dtd文档到xml文档中 * 内部dtd:将约束规则定义在xml文档中 * 外部dtd:将约束的规则定义在外部的dtd文件中 * 本地:<!DOCTYPE 根标签名 SYSTEM \"dtd文件的位置\"> * 网络:<!DOCTYPE 根标签名 PUBLIC \"dtd文件名字\" \"dtd文件的位置URL\">
原理
dtd
* Schema: * 引入: 1.填写xml文档的根元素 2.引入xsi前缀. xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" 3.引入xsd文件命名空间. xsi:schemaLocation=\"http://www.itcast.cn/xml student.xsd\
Schema
* 分类: 1. DTD:一种简单的约束技术 2. Schema:一种复杂的约束技术
约束
1. 解析(读取):将文档中的数据读取到内存中
2. 写入:将内存中的数据保存到xml文档中。持久化的存储
操作xml文档
1. DOM:将标记语言文档一次性加载进内存,在内存中形成一颗dom树 * 优点:操作方便,可以对文档进行CRUD的所有操作 * 缺点:占内存
2. SAX:逐行读取,基于事件驱动的。 * 优点:不占内存。 * 缺点:只能读取,不能增删改
解析xml方式
1. JAXP:sun公司提供的解析器,支持dom和sax两种思想
2. DOM4J:一款非常优秀的解析器
3. Jsoup:jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。
4. PULL:Android操作系统内置的解析器,sax方式的。
xml的常见解析器
jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。 * 快速入门: * 步骤: 1. 导入jar包 2. 获取Document对象 3. 获取对应的标签Element对象 4. 获取数据
* 代码: //2.1获取student.xml的path String path = JsoupDemo1.class.getClassLoader().getResource(\"student.xml\
Jsoup
3. Elements:元素Element对象的集合。可以当做 ArrayList<Element>来使用
2. 获取属性值 * String attr(String key):根据属性名称获取属性值
3. 获取文本内容 * String text():获取文本内容 * String html():获取标签体的所有内容(包括字标签的字符串内容)
4. Element:元素对象
5. Node:节点对象 * 是Document和Element的父类
对象的使用
1. selector:选择器 * 使用的方法:Elements select(String cssQuery) * 语法:参考Selector类中定义的语法
2. XPath:XPath即为XML路径语言,它是一种用来确定XML(标准通用标记语言的子集)文档中某部分位置的语言 * 使用Jsoup的Xpath需要额外导入jar包。 * 查询w3cshool参考手册,使用xpath的语法完成查询 * 代码: //1.获取student.xml的path String path = JsoupDemo6.class.getClassLoader().getResource(\"student.xml\
快捷查询方式
解析
XML
1. C/S:客户端/服务器端
2. B/S:浏览器/服务器端
软件架构
资源分类
1. IP:电子设备(计算机)在网络中的唯一标识。
2. 端口:应用程序在计算机中的唯一标识。 0~65536
3. 传输协议:规定了数据传输的规则 1. 基础协议: 1. tcp:安全协议,三次握手。 速度稍慢 2. udp:不安全协议。 速度快
网络传输的三要素
* 服务器:安装了服务器软件的计算机 * 服务器软件:接收用户的请求,处理请求,做出响应 * web服务器软件:接收用户的请求,处理请求,做出响应。 * 在web服务器软件中,可以部署web项目,让用户通过浏览器来访问这些项目 * web容器
web服务器软件
* webLogic:oracle公司,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。 * webSphere:IBM公司,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。* JBOSS:JBOSS公司的,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。* Tomcat:Apache基金组织,中小型的JavaEE服务器,仅仅支持少量的JavaEE规范servlet/jsp。开源的,免费的。
常见Java服务器相关的web服务器软件
web相关概念
http://tomcat.apache.org/
安装:解压压缩包即可。 * 注意:安装目录建议不要有中文和空格
删除目录就行了
卸载
启动
1. 正常关闭: * bin/shutdown.bat * ctrl+c 2. 强制关闭: * 点击启动窗口的×
关闭
* 部署项目的方式: 1. 直接将项目放到webapps目录下即可。 * /hello:项目的访问路径-->虚拟目录 * 简化部署:将项目打成一个war包,再将war包放置到webapps目录下。 * war包会自动解压缩 2. 配置conf/server.xml文件 在<Host>标签体中配置 <Context docBase=\"D:\\hello\" path=\"/hehe\" /> * docBase:项目存放的路径 * path:虚拟目录 3. 在conf\\Catalina\\localhost创建任意名称的xml文件。在文件中编写 <Context docBase=\"D:\\hello\" /> * 虚拟目录:xml文件的名称 * 静态项目和动态项目: * 目录结构 * java动态项目的目录结构: -- 项目的根目录 -- WEB-INF目录: -- web.xml:web项目的核心配置文件 -- classes目录:放置字节码文件的目录 -- lib目录:放置依赖的jar包 * 将Tomcat集成到IDEA中,并且创建JavaEE的项目,部署项目。
配置
Tomcat
* Servlet就是一个接口,定义了Java类被浏览器访问到(tomcat识别)的规则。 * 将来我们自定义一个类,实现Servlet接口,复写方法。
概念:运行在服务器端的小程序
1. 创建JavaEE项目
2. 定义一个类,实现Servlet接口 * public class ServletDemo1 implements Servlet
3. 实现接口中的抽象方法
4. 配置Servlet 在web.xml中配置: <!--配置Servlet --> <servlet> <servlet-name>demo1</servlet-name> <servlet-class>cn.itcast.web.servlet.ServletDemo1</servlet-class> </servlet> <servlet-mapping> <servlet-name>demo1</servlet-name> <url-pattern>/demo1</url-pattern> </servlet-mapping>
* 执行原理: 1. 当服务器接受到客户端浏览器的请求后,会解析请求URL路径,获取访问的Servlet的资源路径 2. 查找web.xml文件,是否有对应的<url-pattern>标签体内容。 3. 如果有,则在找到对应的<servlet-class>全类名 4. tomcat会将字节码文件加载进内存,并且创建其对象 5. 调用其方法
执行原理
1. 被创建:执行init方法,只执行一次 * Servlet什么时候被创建? * 默认情况下,第一次被访问时,Servlet被创建 * 可以配置执行Servlet的创建时机。 * 在<servlet>标签下配置 1. 第一次被访问时,创建 * <load-on-startup>的值为负数 2. 在服务器启动时,创建 * <load-on-startup>的值为0或正整数 * Servlet的init方法,只执行一次,说明一个Servlet在内存中只存在一个对象,Servlet是单例的 * 多个用户同时访问时,可能存在线程安全问题。 * 解决:尽量不要在Servlet中定义成员变量。即使定义了成员变量,也不要对修改值
2. 提供服务:执行service方法,执行多次 * 每次访问Servlet时,Service方法都会被调用一次。
3. 被销毁:执行destroy方法,只执行一次 * Servlet被销毁时执行。服务器关闭时,Servlet被销毁 * 只有服务器正常关闭时,才会执行destroy方法。 * destroy方法在Servlet被销毁之前执行,一般用于释放资源
servlet中的生命周期方法
支持注解配置。可以不需要web.xml了
1. 创建JavaEE项目,选择Servlet的版本3.0以上,可以不创建web.xml 2. 定义一个类,实现Servlet接口 3. 复写方法 4. 在类上使用@WebServlet注解,进行配置 * @WebServlet(\"资源路径\") @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface WebServlet { String name() default \"\";//相当于<Servlet-name> String[] value() default {};//代表urlPatterns()属性配置 String[] urlPatterns() default {};//相当于<url-pattern> int loadOnStartup() default -1;//相当于<load-on-startup> WebInitParam[] initParams() default {}; boolean asyncSupported() default false; String smallIcon() default \"\"; String largeIcon() default \"\"; String description() default \"\"; String displayName() default \"\"; }
Servlet3.0
1. IDEA会为每一个tomcat部署的项目单独建立一份配置文件 * 查看控制台的log:Using CATALINA_BASE: \"C:\\Users\\fqy\\.IntelliJIdea2018.1\\system\\tomcat\\_itcast\"
2. 工作空间项目 和 tomcat部署的web项目 * tomcat真正访问的是“tomcat部署的web项目”,\"tomcat部署的web项目\"对应着\"工作空间项目\" 的web目录下的所有资源 * WEB-INF目录下的资源不能被浏览器直接访问。
3. 断点调试:使用\"小虫子\"启动 dubug 启动
idea与Tomcat的相关配置
1. urlpartten:Servlet访问路径 1. 一个Servlet可以定义多个访问路径 : @WebServlet({\"/d4\
servlet相关配置
servlet
Tomcat&servlet
Hyper Text Transfer Protocol 超文本传输协议
* 传输协议:定义了,客户端和服务器端通信时,发送数据的格式 * 特点: 1. 基于TCP/IP的高级协议 2. 默认端口号:80 3. 基于请求/响应模型的:一次请求对应一次响应 4. 无状态的:每次请求之间相互独立,不能交互数
传输协议
* 历史版本: * 1.0:每一次请求响应都会建立新的连接 * 1.1:复用连接
历史版本
2. 请求头:客户端浏览器告诉服务器一些信息 请求头名称: 请求头值 * 常见的请求头: 1. User-Agent:浏览器告诉服务器,我访问你使用的浏览器版本信息 * 可以在服务器端获取该头的信息,解决浏览器的兼容性问题 2. Referer:http://localhost/login.html * 告诉服务器,我(当前请求)从哪里来? * 作用: 1. 防盗链: 2. 统计工作:
3. 请求空行 空行,就是用于分割POST请求的请求头,和请求体的。
请求消息数据格式 1. 请求行 请求方式 请求url 请求协议/版本 GET /login.html HTTP/1.1 * 请求方式: * HTTP协议有7中请求方式,常用的有2种 * GET: 1. 请求参数在请求行中,在url后。 2. 请求的url长度有限制的 3. 不太安全 * POST: 1. 请求参数在请求体中 2. 请求的url长度没有限制的 3. 相对安全
HTTP
1. request和response对象是由服务器创建的。我们来使用它们2. request对象是来获取请求消息,response对象是来设置响应消息
request对象和response对象的原理
ServletRequest -- 接口 | 继承 HttpServletRequest -- 接口 | 实现 org.apache.catalina.connector.RequestFacade 类(tomcat)
request对象的继承体系架构
1. 获取请求消息数据 1. 获取请求行数据 * GET /day14/demo1?name=zhangsan HTTP/1.1 * 方法: 1. 获取请求方式 :GET * String getMethod() 2. (*)获取虚拟目录:/day14 * String getContextPath() 3. 获取Servlet路径: /demo1 * String getServletPath() 4. 获取get方式请求参数:name=zhangsan * String getQueryString() 5. (*)获取请求URI:/day14/demo1 * String getRequestURI(): /day14/demo1 * StringBuffer getRequestURL() :http://localhost/day14/demo1 * URL:统一资源定位符 : http://localhost/day14/demo1 中华人民共和国 * URI:统一资源标识符 : /day14/demo1 共和国 6. 获取协议及版本:HTTP/1.1 * String getProtocol() 7. 获取客户机的IP地址: * String getRemoteAddr()
2. 获取请求头数据 * 方法: * (*)String getHeader(String name):通过请求头的名称获取请求头的值 * Enumeration<String> getHeaderNames():获取所有的请求头名称
3. 获取请求体数据: * 请求体:只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数 * 步骤: 1. 获取流对象 * BufferedReader getReader():获取字符输入流,只能操作字符数据 * ServletInputStream getInputStream():获取字节输入流,可以操作所有类型数据 * 在文件上传知识点后讲解 2. 再从流对象中拿数据
request功能
其它功能
BeanUtils
Request
HTTP&request
服务器端发送给客户端的数据
响应消息
1. 响应行 1. 组成:协议/版本 响应状态码 状态码描述 2. 响应状态码:服务器告诉客户端浏览器本次请求和响应的一个状态。 1. 状态码都是3位数字 2. 分类: 1. 1xx:服务器就收客户端消息,但没有接受完成,等待一段时间后,发送1xx多状态码 2. 2xx:成功。代表:200 3. 3xx:重定向。代表:302(重定向),304(访问缓存) 4. 4xx:客户端错误。 * 代表: * 404(请求路径没有对应的资源) * 405:请求方式没有对应的doXxx方法 5. 5xx:服务器端错误。代表:500(服务器内部出现异常)
数据格式
响应字符串格式
* 案例: 1. 完成重定向 * 重定向:资源跳转的方式 * 代码实现: //1. 设置状态码为302 response.setStatus(302); //2.设置响应头location response.setHeader(\"location\
2. 服务器输出字符数据到浏览器 * 步骤: 1. 获取字符输出流 2. 输出数据 * 注意: * 乱码问题: 1. PrintWriter pw = response.getWriter();获取的流的默认编码是ISO-8859-1 2. 设置该流的默认编码 3. 告诉浏览器响应体使用的编码 //简单的形式,设置编码,是在获取流之前设置 response.setContentType(\"text/html;charset=utf-8\"); 3. 服务器输出字节数据到浏览器 * 步骤: 1. 获取字节输出流 2. 输出数据 4. 验证码 1. 本质:图片 2. 目的:防止恶意表单注册
## 案例: * 文件下载需求: 1. 页面显示超链接 2. 点击超链接后弹出下载提示框 3. 完成图片文件下载 * 分析: 1. 超链接指向的资源如果能够被浏览器解析,则在浏览器中展示,如果不能解析,则弹出下载提示框。不满足需求 2. 任何资源都必须弹出下载提示框 3. 使用响应头设置资源的打开方式: * content-disposition:attachment;filename=xxx * 步骤: 1. 定义页面,编辑超链接href属性,指向Servlet,传递资源名称filename 2. 定义Servlet 1. 获取文件名称 2. 使用字节输入流加载文件进内存 3. 指定response的响应头: content-disposition:attachment;filename=xxx 4. 将数据写出到response输出流 * 问题: * 中文文件问题 * 解决思路: 1. 获取客户端使用的浏览器版本信息 2. 根据不同的版本信息,设置filename的编码方式不同
Response对象
response
1. 会话:一次会话中包含多次请求和响应。 * 一次会话:浏览器第一次给服务器资源发送请求,会话建立,直到有一方断开为止 2. 功能:在一次会话的范围内的多次请求间,共享数据 3. 方式: 1. 客户端会话技术:Cookie 2. 服务器端会话技术:Session
客户端会话技术,将数据保存到客户端
基于响应头set-cookie和请求头cookie实现
实现原理
1. 一次可不可以发送多个cookie? * 可以 * 可以创建多个Cookie对象,使用response调用多次addCookie方法发送cookie即可。
2. cookie在浏览器中保存多长时间? 1. 默认情况下,当浏览器关闭后,Cookie数据被销毁 2. 持久化存储: * setMaxAge(int seconds) 1. 正数:将Cookie数据写到硬盘的文件中。持久化存储。并指定cookie存活时间,时间到后,cookie文件自动失效 2. 负数:默认值 3. 零:删除cookie信息
3. cookie能不能存中文? * 在tomcat 8 之前 cookie中不能直接存储中文数据。 * 需要将中文数据转码---一般采用URL编码(%E3) * 在tomcat 8 之后,cookie支持中文数据。特殊字符还是不支持,建议使用URL编码存储,URL解码解析
4. cookie共享问题? 1. 假设在一个tomcat服务器中,部署了多个web项目,那么在这些web项目中cookie能不能共享? * 默认情况下cookie不能共享 * setPath(String path):设置cookie的获取范围。默认情况下,设置当前的虚拟目录 * 如果要共享,则可以将path设置为\"/\" 2. 不同的tomcat服务器间cookie共享问题? * setDomain(String path):如果设置一级域名相同,那么多个服务器之间cookie可以共享 * setDomain(\".baidu.com\
cookie细节
1. cookie存储数据在客户端浏览器 2. 浏览器对于单个cookie 的大小有限制(4kb) 以及 对同一个域名下的总cookie数量也有限制(20个) * 作用: 1. cookie一般用于存出少量的不太敏感的数据 2. 在不登录的情况下,完成服务器对客户端的身份识别
Cookie的特点和作用
记住上一次访问时间 1. 需求: 1. 访问一个Servlet,如果是第一次访问,则提示:您好,欢迎您首次访问。 2. 如果不是第一次访问,则提示:欢迎回来,您上次访问时间为:显示时间字符串 2. 分析: 1. 可以采用Cookie来完成 2. 在服务器中的Servlet判断是否有一个名为lastTime的cookie 1. 有:不是第一次访问 1. 响应数据:欢迎回来,您上次访问时间为:2018年6月10日11:50:20 2. 写回Cookie:lastTime=2018年6月10日11:50:01 2. 没有:是第一次访问 1. 响应数据:您好,欢迎您首次访问 2. 写回Cookie:lastTime=2018年6月10日11:50:01 3. 代码实现: package cn.itcast.cookie; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.net.URLDecoder; import java.net.URLEncoder; import java.text.SimpleDateFormat; import java.util.Date; @WebServlet(\"/cookieTest\
cookie
服务器端会话技术,在一次会话的多次请求间共享数据,将数据保存在服务器端的对象中。HttpSession
Session的实现是依赖于Cookie的
细节
1. session用于存储一次会话的多次请求的数据,存在服务器端 2. session可以存储任意类型,任意大小的数据 * session与Cookie的区别: 1. session存储数据在服务器端,Cookie在客户端 2. session没有数据大小限制,Cookie有 3. session数据安全,Cookie相对于不安全
特点
session
会话技术
用于配置JSP页面,导入资源文件
作用
<%@ 指令名称 属性名1=属性值1 属性名2=属性值2 ... %>
格式
1. page : 配置JSP页面的 * contentType:等同于response.setContentType() 1. 设置响应体的mime类型以及字符集 2. 设置当前jsp页面的编码(只能是高级的IDE才能生效,如果使用低级工具,则需要设置pageEncoding属性设置当前页面的字符集) * import:导包 * errorPage:当前页面发生异常后,会自动跳转到指定的错误页面 * isErrorPage:标识当前也是是否是错误页面。 * true:是,可以使用内置对象exception * false:否。默认值。不可以使用内置对象exception 2. include : 页面包含的。导入页面的资源文件 * <%@include file=\"top.jsp\"%> 3. taglib : 导入资源 * <%@ taglib prefix=\"c\" uri=\"http://java.sun.com/jsp/jstl/core\" %> * prefix:前缀,自定义的
指令
1. html注释: <!-- -->:只能注释html代码片段 2. jsp注释:推荐使用 <%-- --%>:可以注释所有
注释
* 在jsp页面中不需要创建,直接使用的对象 * 一共有9个: 变量名 真实类型 作用 * pageContext PageContext 当前页面共享数据,还可以获取其他八个内置对象 * request HttpServletRequest 一次请求访问的多个资源(转发) * session HttpSession 一次会话的多个请求间 * application ServletContext 所有用户间共享数据 * response HttpServletResponse 响应对象 * page Object 当前页面(Servlet)的对象 this * out JspWriter 输出对象,数据输出到页面上 * config ServletConfig Servlet的配置对象 * exception Throwable 异常对象
内置对象
JSP
Expression Language 表达式语言
替换和简化jsp页面中java代码的编写
* jsp默认支持el表达式的。如果要忽略el表达式 1. 设置jsp中page指令中:isELIgnored=\"true\" 忽略当前jsp页面中所有的el表达式 2. \\${表达式} :忽略当前这个el表达式
注意
${表达式}
1. 运算: * 运算符: 1. 算数运算符: + - * /(div) %(mod) 2. 比较运算符: > < >= <= == != 3. 逻辑运算符: &&(and) ||(or) !(not) 4. 空运算符: empty * 功能:用于判断字符串、集合、数组对象是否为null或者长度是否为0 * ${empty list}:判断字符串、集合、数组对象是否为null或者长度为0 * ${not empty str}:表示判断字符串、集合、数组对象是否不为null 并且 长度>0 2. 获取值 1. el表达式只能从域对象中获取值 2. 语法: 1. ${域名称.键名}:从指定域中获取指定键的值 * 域名称: 1. pageScope --> pageContext 2. requestScope --> request 3. sessionScope --> session 4. applicationScope --> application(ServletContext) * 举例:在request域中存储了name=张三 * 获取:${requestScope.name} 2. ${键名}:表示依次从最小的域中查找是否有该键对应的值,直到找到为止。 3. 获取对象、List集合、Map集合的值 1. 对象:${域名称.键名.属性名} * 本质上会去调用对象的getter方法 2. List集合:${域名称.键名[索引]} 3. Map集合: * ${域名称.键名.key名称} * ${域名称.键名[\"key名称\"]} 3. 隐式对象: * el表达式中有11个隐式对象 * pageContext: * 获取jsp其他八个内置对象 * ${pageContext.request.contextPath}:动态获取虚拟目录
EL
Java Server Pages Tag Library JSP标准标签库 * 是由Apache组织提供的开源的免费的jsp标签 <标签>
作用:用于简化和替换jsp页面上的java代码
1. 导入jstl相关jar包 2. 引入标签库:taglib指令: <%@ taglib %> 3. 使用标签
使用步骤
1. if:相当于java代码的if语句 1. 属性: * test 必须属性,接受boolean表达式 * 如果表达式为true,则显示if标签体内容,如果为false,则不显示标签体内容 * 一般情况下,test属性值会结合el表达式一起使用 2. 注意: * c:if标签没有else情况,想要else情况,则可以在定义一个c:if标签 2. choose:相当于java代码的switch语句 1. 使用choose标签声明 相当于switch声明 2. 使用when标签做判断 相当于case 3. 使用otherwise标签做其他情况的声明 相当于default 3. foreach:相当于java代码的for语句
常用JSTL标签
* 需求:在request域中有一个存有User对象的List集合。需要使用jstl+el将list集合数据展示到jsp页面的表格table中
JSTL
1. M:Model,模型。JavaBean * 完成具体的业务操作,如:查询数据库,封装对象 2. V:View,视图。JSP * 展示数据 3. C:Controller,控制器。Servlet * 获取用户的输入 * 调用模型 * 将数据交给视图进行展示 * 优缺点: 1. 优点: 1. 耦合性低,方便维护,可以利于分工协作 2. 重用性高 2. 缺点: 1. 使得项目架构变得复杂,对开发人员要求高
MVC:开发模式
JSP/EL/JSTL
1. 步骤: 1. 定义一个类,实现接口Filter 2. 复写方法 3. 配置拦截路径 1. web.xml 2. 注解
2. 代码: @WebFilter(\"/*\
1. web.xml配置 <filter> <filter-name>demo1</filter-name> <filter-class>cn.itcast.web.filter.FilterDemo1</filter-class> </filter> <filter-mapping> <filter-name>demo1</filter-name> <!-- 拦截路径 --> <url-pattern>/*</url-pattern> </filter-mapping> 2. 过滤器执行流程 1. 执行过滤器 2. 执行放行后的资源 3. 回来执行过滤器放行代码下边的代码 3. 过滤器生命周期方法 1. init:在服务器启动后,会创建Filter对象,然后调用init方法。只执行一次。用于加载资源 2. doFilter:每一次请求被拦截资源时,会执行。执行多次 3. destroy:在服务器关闭后,Filter对象被销毁。如果服务器是正常关闭,则会执行destroy方法。只执行一次。用于释放资源 4. 过滤器配置详解 * 拦截路径配置: 1. 具体资源路径: /index.jsp 只有访问index.jsp资源时,过滤器才会被执行 2. 拦截目录: /user/* 访问/user下的所有资源时,过滤器都会被执行 3. 后缀名拦截: *.jsp 访问所有后缀名为jsp资源时,过滤器都会被执行 4. 拦截所有资源:/* 访问所有资源时,过滤器都会被执行 * 拦截方式配置:资源被访问的方式 * 注解配置: * 设置dispatcherTypes属性 1. REQUEST:默认值。浏览器直接请求资源 2. FORWARD:转发访问资源 3. INCLUDE:包含访问资源 4. ERROR:错误跳转资源 5. ASYNC:异步访问资源 * web.xml配置 * 设置<dispatcher></dispatcher>标签即可 5. 过滤器链(配置多个过滤器) * 执行顺序:如果有两个过滤器:过滤器1和过滤器2 1. 过滤器1 2. 过滤器2 3. 资源执行 4. 过滤器2 5. 过滤器1 * 过滤器先后顺序问题: 1. 注解配置:按照类名的字符串比较规则比较,值小的先执行 * 如: AFilter 和 BFilter,AFilter就先执行了。 2. web.xml配置: <filter-mapping>谁定义在上边,谁先执行
过滤器细节
1. 案例1_登录验证 * 需求: 1. 访问day17_case案例的资源。验证其是否登录 2. 如果登录了,则直接放行。 3. 如果没有登录,则跳转到登录页面,提示\"您尚未登录,请先登录\"。 2. 案例2_敏感词汇过滤 * 需求: 1. 对day17_case案例录入的数据进行敏感词汇过滤 2. 敏感词汇参考《敏感词汇.txt》 3. 如果是敏感词汇,替换为 *** * 分析: 1. 对request对象进行增强。增强获取参数相关方法 2. 放行。传递代理对象 * 增强对象的功能: * 设计模式:一些通用的解决固定问题的方式 1. 装饰模式 2. 代理模式 * 概念: 1. 真实对象:被代理的对象 2. 代理对象: 3. 代理模式:代理对象代理真实对象,达到增强真实对象功能的目的 * 实现方式: 1. 静态代理:有一个类文件描述代理模式 2. 动态代理:在内存中形成代理类 * 实现步骤: 1. 代理对象和真实对象实现相同的接口 2. 代理对象 = Proxy.newProxyInstance(); 3. 使用代理对象调用方法。 4. 增强方法 * 增强方式: 1. 增强参数列表 2. 增强返回值类型 3. 增强方法体执行逻辑
Filter
* 概念:web的三大组件之一。 * 事件监听机制 * 事件 :一件事情 * 事件源 :事件发生的地方 * 监听器 :一个对象 * 注册监听:将事件、事件源、监听器绑定在一起。 当事件源上发生某个事件后,执行监听器代码 * ServletContextListener:监听ServletContext对象的创建和销毁 * 方法: * void contextDestroyed(ServletContextEvent sce) :ServletContext对象被销毁之前会调用该方法 * void contextInitialized(ServletContextEvent sce) :ServletContext对象创建后会调用该方法 * 步骤: 1. 定义一个类,实现ServletContextListener接口 2. 复写方法 3. 配置 1. web.xml <listener> <listener-class>cn.itcast.web.listener.ContextLoaderListener</listener-class> </listener> * 指定初始化参数<context-param> 2. 注解: * @WebListener
Listener
Filter&Listener
一个JavaScript框架。简化JS开发 * jQuery是一个快速、简洁的JavaScript框架,是继Prototype之后又一个优秀的JavaScript代码库(或JavaScript框架)。jQuery设计的宗旨 是“write Less,Do More”,即倡导写更少的代码,做更多的事情。它封装JavaScript常用的功能代码,提供一种简便的JavaScript设计模式,优 化HTML文档操作、事件处理、动画设计和Ajax交互。
1. JQuery对象在操作时,更加方便。 2. JQuery对象和js对象方法不通用的. 3. 两者相互转换 * jq -- > js : jq对象[索引] 或者 jq对象.get(索引) * js -- > jq : $(js对象)
JQuery和js对象区别与转换
1. 基本操作学习: 1. 事件绑定 //1.获取b1按钮 $(\"#b1\").click(function(){ alert(\"abc\
2. 分类 1. 基本选择器 1. 标签选择器(元素选择器) * 语法: $(\"html标签名\") 获得所有匹配标签名称的元素 2. id选择器 * 语法: $(\"#id的属性值\") 获得与指定id属性值匹配的元素 3. 类选择器 * 语法: $(\".class的属性值\") 获得与指定的class属性值匹配的元素 4. 并集选择器: * 语法: $(\
1. 内容操作 1. html(): 获取/设置元素的标签体内容 <a><font>内容</font></a> --> <font>内容</font> 2. text(): 获取/设置元素的标签体纯文本内容 <a><font>内容</font></a> --> 内容 3. val(): 获取/设置元素的value属性值 2. 属性操作 1. 通用属性操作 1. attr(): 获取/设置元素的属性 2. removeAttr():删除属性 3. prop():获取/设置元素的属性 4. removeProp():删除属性 * attr和prop区别? 1. 如果操作的是元素的固有属性,则建议使用prop 2. 如果操作的是元素自定义的属性,则建议使用attr 2. 对class属性操作 1. addClass():添加class属性值 2. removeClass():删除class属性值 3. toggleClass():切换class属性 * toggleClass(\"one\"): * 判断如果元素对象上存在class=\"one\",则将属性值one删除掉。 如果元素对象上不存在class=\"one\",则添加 4. css(): 3. CRUD操作: 1. append():父元素将子元素追加到末尾 * 对象1.append(对象2): 将对象2添加到对象1元素内部,并且在末尾 2. prepend():父元素将子元素追加到开头 * 对象1.prepend(对象2):将对象2添加到对象1元素内部,并且在开头 3. appendTo(): * 对象1.appendTo(对象2):将对象1添加到对象2内部,并且在末尾 4. prependTo(): * 对象1.prependTo(对象2):将对象1添加到对象2内部,并且在开头 5. after():添加元素到元素后边 * 对象1.after(对象2): 将对象2添加到对象1后边。对象1和对象2是兄弟关系 6. before():添加元素到元素前边 * 对象1.before(对象2): 将对象2添加到对象1前边。对象1和对象2是兄弟关系 7. insertAfter() * 对象1.insertAfter(对象2):将对象2添加到对象1后边。对象1和对象2是兄弟关系 8. insertBefore() * 对象1.insertBefore(对象2): 将对象2添加到对象1前边。对象1和对象2是兄弟关系 9. remove():移除元素 * 对象.remove():将对象删除掉 10. empty():清空元素的所有后代元素。 * 对象.empty():将对象的后代元素全部清空,但是保留当前对象以及其属性节点
Dom操作
基础
动画
遍历
1. jquery标准的绑定方式 * jq对象.事件方法(回调函数); * 注:如果调用事件方法,不传递回调函数,则会触发浏览器默认行为。 * 表单对象.submit();//让表单提交 2. on绑定事件/off解除绑定 * jq对象.on(\"事件名称\
数据绑定
1. 广告显示和隐藏 <!DOCTYPE html> <html> <head> <meta charset=\"UTF-8\"> <title>广告的自动显示与隐藏</title> <style> #content{width:100%;height:500px;background:#999} </style> <!--引入jquery--> <script type=\"text/javascript\" src=\"../js/jquery-3.3.1.min.js\
5. 插件:增强JQuery的功能 1. 实现方式: 1. $.fn.extend(object) * 增强通过Jquery获取的对象的功能 $(\"#id\") 2. $.extend(object) * 增强JQeury对象自身的功能 $/jQuery
高级
Jquery
ASynchronous JavaScript And XML 异步的JavaScript 和 XML
1. 异步和同步:客户端和服务器端相互通信的基础上 * 客户端必须等待服务器端的响应。在等待的期间客户端不能做其他操作。 * 客户端不需要等待服务器端的响应。在服务器处理请求的过程中,客户端可以进行其他的操作。 Ajax 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。 [1] 通过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。 传统的网页(不使用 Ajax)如果需要更新内容,必须重载整个网页页面。 提升用户的体验
2. JQeury实现方式 1. $.ajax() * 语法:$.ajax({键值对}); //使用$.ajax()发送异步请求 $.ajax({ url:\"ajaxServlet1111\
实现方式
Ajax
概念: JavaScript Object Notation JavaScript对象表示法 Person p = new Person(); p.setName(\"张三\"); p.setAge(23); p.setGender(\"男\"); var p = {\"name\":\"张三\
1. 基本规则 * 数据在名称/值对中:json数据是由键值对构成的 * 键用引号(单双都行)引起来,也可以不使用引号 * 值得取值类型: 1. 数字(整数或浮点数) 2. 字符串(在双引号中) 3. 逻辑值(true 或 false) 4. 数组(在方括号中) {\"persons\
* JSON解析器:\t\t\t* 常见的解析器:Jsonlib,Gson,fastjson,jackson\t\t\t\t1. JSON转为Java对象\t\t\t1. 导入jackson的相关jar包\t\t\t2. 创建Jackson核心对象 ObjectMapper\t\t\t3. 调用ObjectMapper的相关方法进行转换span style=\"white-space:pre\
转换
* 校验用户名是否存在 1. 服务器响应的数据,在客户端使用时,要想当做json数据格式使用。有两种解决方案: 1. $.get(type):将最后一个参数type指定为\"json\" 2. 在服务器端设置MIME类型 response.setContentType(\"application/json;charset=utf-8\");
JSON
Ajax&JSON
综合案例
Nginx 是一款高性能的 http 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器。由俄罗斯的程序设计师伊戈尔·西索夫(Igor Sysoev)所开发,官方测试 nginx 能够支支撑 5 万并发链接,并且 cpu、内存等资源消耗却非常低,运行非常稳定。
1、http 服务器。Nginx 是一个 http 服务可以独立提供 http 服务。可以做网页静态服务器。2、虚拟主机。可以实现在一台服务器虚拟出多个网站。例如个人网站使用的虚拟主机。3、反向代理,负载均衡。当网站的访问量达到一定程度后,单台服务器不能满足用户的请求时,需要用多台服务器集群可以使用 nginx 做反向代理。并且多台服务器可以平均分担负载,不会因为某台服务器负载高宕机而某台服务器闲置的情况。
应用场景
(1)需要安装 gcc 的环境【此步省略】yum install gcc-c++(2)第三方的开发包。 PCRE PCRE(Perl Compatible Regular Expressions)是一个 Perl 库,包括 perl 兼容的正则表达式库。nginx 的 http 模块使用 pcre 来解析 正则表达式,所以需要在 linux 上安装 pcre 库。 yum install -y pcre pcre-devel注:pcre-devel 是使用 pcre 开发的一个二次开发库。nginx 也需要此库。zlibzlib 库提供了很多种压缩和解压缩的方式,nginx 使用 zlib 对 http 包的内容进行 gzip,所以需要在 linux 上安装 zlib 库。yum install -y zlib zlib-devel、OpenSSLOpenSSL 是一个强大的安全套接字层密码库,囊括主要的密码算法、常用的密钥和证书封装管理功能及 SSL 协议,并提供丰富的应用程序供测试或其它目的使用。nginx 不仅支持 http 协议,还支持 https(即在 ssl 协议上传输 http),所以需要在 linux安装 openssl 库。yum install -y openssl openssl-devel
环境准备
官方网站下载 nginx:http://nginx.org/
configure参数./configure \\--prefix=/usr \\ 指向安装目录--sbin-path=/usr/sbin/nginx \\ 指向(执行)程序文件(nginx)--conf-path=/etc/nginx/nginx.conf \\ 指向配置文件--error-log-path=/var/log/nginx/error.log \\ 指向log--http-log-path=/var/log/nginx/access.log \\ 指向http-log--pid-path=/var/run/nginx/nginx.pid \\ 指向pid--lock-path=/var/lock/nginx.lock \\ (安装文件锁定,防止安装文件被别人利用,或自己误操作。)--user=nginx \\--group=nginx \\--with-http_ssl_module \\ 启用ngx_http_ssl_module支持(使支持https请求,需已安装openssl)--with-http_flv_module \\ 启用ngx_http_flv_module支持(提供寻求内存使用基于时间的偏移量文件)--with-http_stub_status_module \\ 启用ngx_http_stub_status_module支持(获取nginx自上次启动以来的工作状态)--with-http_gzip_static_module \\ 启用ngx_http_gzip_static_module支持(在线实时压缩输出数据流)--http-client-body-temp-path=/var/tmp/nginx/client/ \\ 设定http客户端请求临时文件路径--http-proxy-temp-path=/var/tmp/nginx/proxy/ \\ 设定http代理临时文件路径--http-fastcgi-temp-path=/var/tmp/nginx/fcgi/ \\ 设定http fastcgi临时文件路径--http-uwsgi-temp-path=/var/tmp/nginx/uwsgi \\ 设定http uwsgi临时文件路径--http-scgi-temp-path=/var/tmp/nginx/scgi \\ 设定http scgi临时文件路径--with-pcre 启用pcre库
第一步:把 nginx 的源码包nginx-1.8.0.tar.gz上传到 linux 系统第二步:解压缩tar zxvf nginx-1.8.0.tar.gz第三步:进入nginx-1.8.0目录 使用 configure 命令创建一 makeFile 文件。./configure \\--prefix=/usr/local/nginx \\--pid-path=/var/run/nginx/nginx.pid \\--lock-path=/var/lock/nginx.lock \\--error-log-path=/var/log/nginx/error.log \\--http-log-path=/var/log/nginx/access.log \\--with-http_gzip_static_module \\--http-client-body-temp-path=/var/temp/nginx/client \\--http-proxy-temp-path=/var/temp/nginx/proxy \\--http-fastcgi-temp-path=/var/temp/nginx/fastcgi \\--http-uwsgi-temp-path=/var/temp/nginx/uwsgi \\--http-scgi-temp-path=/var/temp/nginx/scgi执行后可以看到Makefile文件第四步:编译make第五步:安装make install
安装步骤
进入到Nginx目录下的sbin目录cd /usr/local/ngiux/sbin输入命令启动Nginx./nginx启动后查看进程ps aux|grep nginx
关闭 nginx:./nginx -s stop或者./nginx -s quit重启 nginx:1、先关闭后启动。2、刷新配置文件:./nginx -s reload
启动与访问
将/资料/静态页面/index目录下的所有内容 上传到服务器的/usr/local/nginx/html下即可访问
配置虚拟主机
(1)上传静态网站:将/资料/静态页面/index目录上传至 /usr/local/nginx/index下将/资料/静态页面/regist目录上传至 /usr/local/nginx/regist下(2)修改Nginx 的配置文件:/usr/local/nginx/conf/nginx.confserver { listen 81; # 监听的端口 server_name localhost; # 域名或ip location / { # 访问路径配置 root index;# 根目录 index index.html index.htm; # 默认首页 } error_page 500 502 503 504 /50x.html; # 错误页面 location = /50x.html { root html; } } server { listen 82; # 监听的端口 server_name localhost; # 域名或ip location / { # 访问路径配置 root regist;# 根目录 index regist.html; # 默认首页 } error_page 500 502 503 504 /50x.html; # 错误页面 location = /50x.html { root html; } }(3)访问测试:地址栏输入http://192.168.177.129/:81 可以看到首页面地址栏输入http://192.168.177.129/:82 可以看到注册页面
端口绑定
静态网站部署
域名(Domain Name),是由一串用“点”分隔的字符组成的Internet上某一台计算机或计算机组的名称,用于在数据传输时标识计算机的电子方位(有时也指地理位置,地理上的域名,指代有行政自主权的一个地方区域)。域名是一个IP地址上有“面具” 。域名的目的是便于记忆和沟通的一组服务器的地址(网站,电子邮件,FTP等)。域名作为力所能及难忘的互联网参与者的名称。域名按域名系统(DNS)的规则流程组成。在DNS中注册的任何名称都是域名。域名用于各种网络环境和应用程序特定的命名和寻址目的。通常,域名表示互联网协议(IP)资源,例如用于访问因特网的个人计算机,托管网站的服务器计算机,或网站本身或通过因特网传送的任何其他服务。世界上第一个注册的域名是在1985年1月注册的。
什么是域名
一个域名对应一个 ip 地址,一个 ip 地址可以被多个域名绑定。本地测试可以修改 hosts 文件(C:\\Windows\\System32\\drivers\\etc)可以配置域名和 ip 的映射关系,如果 hosts 文件中配置了域名和 ip 的对应关系,不需要走dns 服务器。192.168.177.129 www.hmtravel.com192.168.177.129 regist.hmtravel.com做好域名指向后,修改nginx配置文件 server { listen 80; server_name www.hmtravel.com; location / { root cart; index cart.html; } } server { listen 80; server_name regist.hmtravel.com; location / { root search; index search.html; } }执行以下命令,刷新配置[root@localhost sbin]# ./nginx -s reload
域名和IP绑定
域名绑定
正向代理图
反向代理图
反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。
(1) 将travel案例部署到tomcat中(ROOT目录),上传到服务器。(2)启动TOMCAT,输入网址http://192.168.177.129:8080 可以看到网站首页
配置反向代理-准备工作
(1)在Nginx主机修改 Nginx配置文件 upstream tomcat-travel{ server 192.168.177.129:8080; } server { listen 80; # 监听的端口 server_name www.hmtravel.com; # 域名或ip location / { # 访问路径配置 # root index;# 根目录 proxy_pass http://tomcat-travel; index index.html index.htm; # 默认首页 }}(2)重新启动Nginx 然后用浏览器测试:http://www.hmtravel.com (此域名须配置域名指向)
反向代理
负载均衡 建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽、增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。负载均衡,英文名称为Load Balance,其意思就是分摊到多个操作单元上进行执行,例如Web服务器、FTP服务器、企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务。
(1)将刚才的存放工程的tomcat复制三份,修改端口分别为8080 ,8081,8082 。(2)分别启动这三个tomcat服务。(3)为了能够区分是访问哪个服务器的网站,可以在首页标题加上标记以便区分。
准备工作
修改 Nginx配置文件: upstream tomcat-travel { server 192.168.177.129:8080; server 192.168.177.129:8081; server 192.168.177.129:8082; } server { listen 80; # 监听的端口 server_name www.hmtravel.com; # 域名或ip location / { # 访问路径配置 # root index;# 根目录 proxy_pass http://tomcat-travel; index index.html index.htm; # 默认首页 } error_page 500 502 503 504 /50x.html; # 错误页面 location = /50x.html { root html; } }地址栏输入http:// www.hmtravel.com / 刷新观察每个网页的标题,看是否不同。经过测试,三台服务器出现的概率各为33.3333333%,交替显示。如果其中一台服务器性能比较好,想让其承担更多的压力,可以设置权重。比如想让NO.1出现次数是其它服务器的2倍,则修改配置如下: upstream tomcat-travel { server 192.168.177.129:8080; server 192.168.177.129:8081 weight=2; server 192.168.177.129:8082; }经过测试,每刷新四次,有两次是8081
配置负载均衡
负载均衡
Nginx反向代理与负载均衡
Nginx
JavaEE
正则表达式是⼀组由字⺟和符号组成的特殊⽂本,它可以⽤来从⽂本中找出满⾜你想要的格式的句⼦。
什么是正则表达式?
字符之间的匹配
基本匹配
. 句号匹配任意单个字符除了换⾏符。[ ] 字符种类。匹配⽅括号内的任意字符。[^ ] 否定的字符种类。匹配除了⽅括号⾥的任意字符* 匹配>=0个重复的在*号之前的字符。
. 是元字符中最简单的例⼦。. 匹配任意单个字符,但不匹配换⾏符。例如,表达式.ar 匹配⼀个任意字符后⾯跟着是a 和r 的字符串。\".ar\" => The car parked in the garage.
点运算
字符集也叫做字符类。⽅括号⽤来指定⼀个字符集。在⽅括号中使⽤连字符来指定字符集的范围。
表达式 匹配 ar. 字符串\"ar[.]\" => A garage is a good place to park a car.
表达式[^c]ar 匹配⼀个后⾯跟着ar 的除了c 的任意字符。\"[^c]ar\" => The car parked in the garage.
否定字符集
后⾯跟着元字符 + , * or ? 的,⽤来指定匹配⼦模式的次数。这些元字符在不同的情况下有着不同的意思。
* 号匹配 在* 之前的字符出现⼤于等于0 次。例如,表达式字符串。匹配0或更多个以a开头的字符。表达式[a-z]* 匹配⼀个⾏中所有以⼩写字⺟开头的\"[a-z]*\" => The car parked in the garage #21.
* 号
+号匹配+ 号之前的字符出现 >=1 次。例如表达式c.+t 匹配以⾸字⺟c 开头以t 结尾,中间跟着⾄少⼀个字符的字符串。
+ 号
在正则表达式中元字符例如,表达式标记在?符号前⾯的字符为可选,即出现 0 或 1 次。
? 号
重复次数
在正则表达式中 {} 是⼀个量词,常⽤来限定⼀个或⼀组字符可以重复出现的次数。例如, 表达式匹配最少 2 位最多 3 位 0~9 的数字。
{}
(......)特征标群
或运算符就表示或,⽤作判断条件。例如 匹配 或 car 。
| 运算符
反斜线 在表达式中⽤于转码紧跟其后的字符。⽤于指定 { } [ ] / \\ + * . $ ^ | ? 这些特殊字符。如果想要匹配这些特殊字符则要在其前⾯加上反斜线 \\ 。
转码特殊字符
锚点
^ ⽤来检查匹配的字符串是否在所匹配字符串的开头。例如,在中使⽤表达式会得到结果 a 。但如果使⽤将匹配不到任何结果。因为在字符串中并不是以 开头。
字符类
元字符
整数或者小数
^[0-9]*$
只能输入数字
^\\d{n}$
只能输入n位的数字
只能输入至少n位的数字
只能输入m~n位的数字
^(0|[1-9][0-9]*)$
只能输入零和非零开头的数字
^[0-9]+(.[0-9]{2})?$
只能输入有两位小数的正实数
只能输入有1~3位小数的正实数
^\\+?[1-9][0-9]*$
只能输入非零的正整数
^\\-[1-9][]0-9*$
只能输入非零的负整数
^.{3}$
只能输入长度为3的字符
^[A-Za-z]+$
只能输入由26个英文字母组成的字符串
^[A-Z]+$
只能输入由26个大写英文字母组成的字符串
^[a-z]+$
只能输入由26个小写英文字母组成的字符串
^[A-Za-z0-9]+$
只能输入由数字和26个英文字母组成的字符串
^\\w+$
只能输入由数字、26个英文字母或者下划线组成的字符串
验证用户密码:
只能输入汉字
^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$
验证Email地址
^[http|https]://([\\w-]+\\.)+[\\w-]+(/[\\w-./?%&=]*)?$
验证Internet URL
验证电话号码
^\\d{15}|\\d{18}$
验证身份证号(15位或18位数字)
^(0?[1-9]|1[0-2])$
验证一年的12个月
^((0?[1-9])|((1|2)[0-9])|30|31)$
验证一个月的31天
[\\u4e00-\\u9fa5]
匹配中文字符的正则表达式
[^\\x00-\\xff]
匹配双字节字符(包括汉字在内)
\[\\s| ]*\
匹配空行的正则表达式
<(.*)>(.*)<\\/(.*)>|<(.*)\\/>
匹配html标签的正则表达式
(^\\s*)|(\\s*$)
匹配首尾空格的正则表达式
\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*
匹配Email地址的正则表达式
<(\\S*?)[^>]*>.*?|<.*? />
匹配HTML标记的正则表达式
^\\s*|\\s*$
匹配首尾空白字符的正则表达式
[a-zA-z]+://[^\\s]*
匹配网址URL的正则表达式
匹配帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线)
\\d{3}-\\d{8}|\\d{4}-\\d{7}
匹配国内电话号码
匹配腾讯QQ号
[1-9]\\d{5}(?!\\d)
匹配中国邮政编码
\\d{15}|\\d{18}
匹配身份证
\\d+\\.\\d+\\.\\d+\\.\\d+
匹配ip地址
^[1-9]\\d*$ //匹配正整数^-[1-9]\\d*$ //匹配负整数^-?[1-9]\\d*$ //匹配整数^[1-9]\\d*|0$ //匹配非负整数(正整数 + 0)^-[1-9]\\d*|0$ //匹配非正整数(负整数 + 0)^[1-9]\\d*\\.\\d*|0\\.\\d*[1-9]\\d*$ //匹配正浮点数^-([1-9]\\d*\\.\\d*|0\\.\\d*[1-9]\\d*)$ //匹配负浮点数^-?([1-9]\\d*\\.\\d*|0\\.\\d*[1-9]\\d*|0?\\.0+|0)$ //匹配浮点数^[1-9]\\d*\\.\\d*|0\\.\\d*[1-9]\\d*|0?\\.0+|0$ //匹配非负浮点数(正浮点数 + 0)^(-([1-9]\\d*\\.\\d*|0\\.\\d*[1-9]\\d*))|0?\\.0+|0$//匹配非正浮点数(负浮点数 + 0)s
匹配特定数字
^[A-Za-z]+$//匹配由26个英文字母组成的字符串^[A-Z]+$//匹配由26个英文字母的大写组成的字符串^[a-z]+$//匹配由26个英文字母的小写组成的字符串^[A-Za-z0-9]+$//匹配由数字和26个英文字母组成的字符串^\\w+$//匹配由数字、26个英文字母或者下划线组成的字符串
匹配特定字符串
校验密码强度包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间。
中文
^\\\\w+$
由数字、26个英文字母或下划线组成的字符串
[\\\\w!#$%&'*+/=?^_`{|}~-]+(?:\\\\.[\\\\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\\\\w](?:[\\\\w-]*[\\\\w])?\\\\.)+[\\\\w](?:[\\\\w-]*[\\\\w])?
校验E-Mail 地址
15位: ^[1-9]\\\\d{7}((0\\\\d)|(1[0-2]))(([0|1|2]\\\\d)|3[0-1])\\\\d{3}$
18位: ^[1-9]\\\\d{5}[1-9]\\\\d{3}((0\\\\d)|(1[0-2]))(([0|1|2]\\\\d)|3[0-1])\\\\d{3}([0-9]|X)$
校验身份证号码
^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$
校验日期“yyyy-mm-dd“ 格式的日期校验,已考虑平闰年。
校验金额精确到2位小数。
^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\\\\d{8}$
校验手机号下面是国内 13、15、18开头的手机号正则表达式。(可根据目前国内收集号扩展前两位开头号码)
^.*MSIE [5-8](?:\\\\.[0-9]+)?(?!.*Trident\\\\/[5-9]\\\\.0).*$
判断IE的版本
\\\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\b
校验IP-v4地址
校验IP-v6地址
if (!s.match(/^[a-zA-Z]+:\\\\/\\\\//)){ s = 'http://' + s;}
检查URL的前缀
^(f|ht){1}(tp|tps):\\\\/\\\\/([\\\\w-]+\\\\.)+[\\\\w-]+(\\\\/[\\\\w- ./?%&=]*)?
提取URL链接
^([a-zA-Z]\\\\:|\\\\\\\\)\\\\\\\\([^\\\\\\\\]+\\\\\\\\)*[^\\\\/:*?\"<>|]+\\\\.txt(l)?$
文件路径及扩展名校验验证windows下文件路径和扩展名(下面的例子中为.txt文件)
^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$
提取网页颜色代码有时需要抽取网页中的颜色代码,可以使用下面的表达式。
\\\\< *[img][^\\\\>]*[src] *= *[\\\\\
提取网页图片
(<a\\\\s*(?!.*\\\\brel=)[^>]*)(href=\
提取页面超链接
^\\\\s*[a-zA-Z\\\\-]+\\\\s*[:]{1}\\\\s[a-zA-Z0-9\\\\s.#]+[;]{1}
查找CSS属性
<!--(.*?)-->
抽取注释
<\\\\/?\\\\w+((\\\\s+\\\\w+(\\\\s*=\\\\s*(?:\".*?\"|'.*?'|[\\\\^'\">\\\\s]+))?)+\\\\s*|\\\\s*)\\\\/?>
匹配HTML标签
校验字符串
简单的日期判断(YYYY/MM/DD)
演化的日期判断(YYYY/MM/DD| YY/MM/DD)
^((((1[6-9]|[2-9]\\d)\\d{2})-(0?[13578]|1[02])-(0?[1-9]|[12]\\d|3[01]))|(((1[6-9]|[2-9]\\d)\\d{2})-(0?[13456789]|1[012])-(0?[1-9]|[12]\\d|30))|(((1[6-9]|[2-9]\\d)\\d{2})-0?2-(0?[1-9]|1\\d|2[0-8]))|(((1[6-9]|[2-9]\\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))-0?2-29-))$
加入闰年的判断的
时间正则案例
正则
https://www.pppet.net/
在线表达式生成地址
Cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,Cron有如下两种语法格式: Seconds Minutes Hours DayofMonth Month DayofWeek Year或Seconds Minutes Hours DayofMonth Month DayofWeek每一个域可出现的字符如下:Seconds:可出现\
表达式详解
表达式范例
Quartz
单机模式
伪分布模式
全分布式
安装与配置
树形文件系统
每一个节点都被称为: ZNode,每个节点上都会保存自己的数据和节点信息。
节点可以拥有子节点,同时也允许少量(1MB)数据存储在该节点之下
PERSISTENT 持久化节点
EPHEMERAL 临时节点 :-e
PERSISTENT_SEQUENTIAL 持久化顺序节点 :-s
EPHEMERAL_SEQUENTIAL 临时顺序节点 :-es
节点可以分为四大类
数据模型
启动 ZooKeeper 服务: ./zkServer.sh start查看 ZooKeeper 服务状态: ./zkServer.sh status停止 ZooKeeper 服务: ./zkServer.sh stop 重启 ZooKeeper 服务: ./zkServer.sh restart
服务端常用命令
./zkCli.sh –server ip:port
连接ZooKeeper服务端
断开连接 quit
查看命令帮助 help
显示指定目录下节点 ls
create /节点path value
创建节点
get /节点path
获取节点值
set /节点path value
设置节点值
delete /节点path
删除单个节点
deleteall /节点path
删除带有子节点的节点
czxid:节点被创建的事务ID ctime: 创建时间 mzxid: 最后一次被更新的事务ID mtime: 修改时间 pzxid:子节点列表最后一次被更新的事务IDcversion:子节点的版本号
dataversion:数据版本号 aclversion:权限版本号 ephemeralOwner:用于临时节点,代表临时节点的事务ID,如果为持久节点则为0 dataLength:节点存储的数据的长度 numChildren:当前节点的子节点个数
客户端常用命令
命令操作
/** * 建立连接 */ @Before public void testConnect() { /* * * @param connectString 连接字符串。zk server 地址和端口 \
建立连接
@Test public void testCreate3() throws Exception { //3. 设置节点的类型 //默认类型:持久化 String path = client.create().withMode(CreateMode.EPHEMERAL).forPath(\"/app3\"); System.out.println(path); } @Test public void testCreate4() throws Exception { //4. 创建多级节点 /app1/p1 //creatingParentsIfNeeded():如果父节点不存在,则创建父节点 String path = client.create().creatingParentsIfNeeded().forPath(\"/app4/p1\"); System.out.println(path); }
@Test public void testCreate() throws Exception { //2. 创建节点 带有数据 //如果创建节点,没有指定数据,则默认将当前客户端的ip作为数据存储 String path = client.create().forPath(\"/app2\
添加节点
/** * 删除节点: delete deleteall * 1. 删除单个节点:delete().forPath(\"/app1\"); * 2. 删除带有子节点的节点:delete().deletingChildrenIfNeeded().forPath(\"/app1\"); * 3. 必须成功的删除:为了防止网络抖动。本质就是重试。 client.delete().guaranteed().forPath(\"/app2\"); * 4. 回调:inBackground * @throws Exception */ @Test public void testDelete() throws Exception { // 1. 删除单个节点 client.delete().forPath(\"/app1\"); } @Test public void testDelete2() throws Exception { //2. 删除带有子节点的节点 client.delete().deletingChildrenIfNeeded().forPath(\"/app4\"); } @Test public void testDelete3() throws Exception { //3. 必须成功的删除 client.delete().guaranteed().forPath(\"/app2\
删除节点
/** * 修改数据 * 1. 基本修改数据:setData().forPath() * 2. 根据版本修改: setData().withVersion().forPath() * * version 是通过查询出来的。目的就是为了让其他客户端或者线程不干扰我。 * * @throws Exception */ @Test public void testSet() throws Exception { client.setData().forPath(\"/app1\
修改节点
// 2. 查询子节点: ls List<String> path = client.getChildren().forPath(\"/\"); System.out.println(path); } @Test public void testGet3() throws Exception { Stat status = new Stat(); System.out.println(status); //3. 查询节点状态信息:ls -s client.getData().storingStatIn(status).forPath(\"/app1\"); System.out.println(status); }
/** * 查询节点: * 1. 查询数据:get: getData().forPath() * 2. 查询子节点: ls: getChildren().forPath() * 3. 查询节点状态信息:ls -s:getData().storingStatIn(状态对象).forPath() */ @Test public void testGet1() throws Exception { //1. 查询数据:get byte[] data = client.getData().forPath(\"/app1\"); System.out.println(new String(data)); } @Test public void testGet2() throws Exception {
查询节点
Watch事件监听
1.客户端获取锁时,在lock节点下创建临时顺序节点。2.然后获取lock下面的所有子节点,客户端获取到所有的子节点之后,如果发现自己创建的子节点序号最小,那么就认为该客户端获取到了锁。使用完锁后,将该节点删除。3.如果发现自己创建的节点并非lock所有子节点中最小的,说明自己还没有获取到锁,此时客户端需要找到比自己小的那个节点,同时对其注册事件监听器,监听删除事件。4.如果发现比自己小的那个节点被删除,则客户端的 Watcher会收到相应通知,此时再次判断自己创建的节点 是否是lock子节点中序号最小的,如果是则获取到了锁, 如果不是则重复以上步骤继续获取到比自己小的一个节点 并注册监听。
核心思想:当客户端要获取锁,则创建节点,使用完锁,则删除该节点。
InterProcessSemaphoreMutex:分布式排它锁(非可重入锁)
InterProcessMutex:分布式可重入排它锁
InterProcessReadWriteLock:分布式读写锁
InterProcessMultiLock:将多个锁作为单个实体管理的容器
InterProcessSemaphoreV2:共享信号量
在Curator中有五种锁方案
分布式锁实现
API操作
Curator
JavaAPI操作
集群搭建
核心理论
zookeeper
Apache POI是用Java编写的免费开源的跨平台的Java API,Apache POI提供API给Java程序对Microsoft Office格式档案读和写的功能,其中使用最多的就是使用POI操作Excel文件。
官方主页: http://poi.apache.org/index.htmlAPI文档: http://poi.apache.org/apidocs/index.html
jxl:专门操作Excel
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.14</version></dependency><dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.14</version></dependency>
Maven座标
工作簿,代表一个excel的整个文档
HSSFWorkbook(); // 创建一个新的工作簿HSSFWorkbook(InputStream inputStream); // 创建一个关联输入流的工作簿,可以将一个excel文件封装成工作簿HSSFSheet createSheet(String sheetname); 创建一个新的SheetHSSFSheet getSheet(String sheetName); 通过名称获取SheetHSSFSheet getSheetAt(int index); // 通过索引获取Sheet,索引从0开始HSSFCellStyle createCellStyle(); 创建单元格样式int getNumberOfSheets(); 获取sheet的个数setActiveSheet(int index); 设置默认选中的工作表write();write(File newFile);write(OutputStream stream);
HSSFWorkbook
工作表
HSSFSheet
行
HSSFCell createCell(int column); 创建新的单元格HSSFCell setCell(shot index);HSSFCell getCell(shot index);HSSFCell getCell(CellReference.convertColStringToIndex(“A”)); 根据列名英文字母获取。setRowStyle(HSSFCellStyle style); 设置行样式short getLastCellNum(); 获取最后的单元格号,如果单元格有第一个开始算,lastCellNum就是列的个数setHeightInPoints(float height); 设置行的高度
HSSFRow
单元格
setCellValue(String value); 设置单元格的值setCellType(); 设置单元格类型,如 字符串、数字、布尔等setCellStyle(); 设置单元格样式String getStringCellValue(); 获取单元格中的字符串值setCellStyle(HSSFCellStyle style); 设置单元格样式,例如字体、加粗、格式化setCellFormula(String formula); 设置计算公式,计算的结果作为单元格的值,也提供了异常常用的函数,如求和\
HSSFCell
单元格样式
setFont(Font font); 为单元格设置字体样式setAlignment(HorizontalAlignment align); // 设置水平对齐方式setVerticalAlignment(VerticalAlignment align); // 设置垂直对齐方式setFillPattern(FillPatternType fp);setFillForegroundColor(short bg); 设置前景色setFillBackgroundColor(short bg); 设置背景颜色
HSSFCellStyle
字体
setColor(short color); // 设置字体颜色setBold(boolean bold); // 设置是否粗体setItalic(boolean italic); 设置倾斜setUnderline(byte underline); 设置下划线
HSSFFont
HSSFName:名称HSSFDataFormat :日期格式化HSSFHeader : Sheet的头部HSSFFooter :Sheet的尾部HSSFDateUtil :日期工具HSSFPrintSetup :打印设置HSSFErrorConstants:错误信息表
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.8</version> </dependency>
引入依赖
public static void createExcel() throws IOException{ // 获取桌面路径 FileSystemView fsv = FileSystemView.getFileSystemView(); String desktop = fsv.getHomeDirectory().getPath(); String filePath = desktop + \"/template.xls\"; File file = new File(filePath); OutputStream outputStream = new FileOutputStream(file); HSSFWorkbook workbook = new HSSFWorkbook(); HSSFSheet sheet = workbook.createSheet(\"Sheet1\"); HSSFRow row = sheet.createRow(0); row.createCell(0).setCellValue(\"id\"); row.createCell(1).setCellValue(\"订单号\"); row.createCell(2).setCellValue(\"下单时间\"); row.createCell(3).setCellValue(\"个数\"); row.createCell(4).setCellValue(\"单价\"); row.createCell(5).setCellValue(\"订单金额\"); row.setHeightInPoints(30); // 设置行的高度 HSSFRow row1 = sheet.createRow(1); row1.createCell(0).setCellValue(\"1\"); row1.createCell(1).setCellValue(\"NO00001\"); // 日期格式化 HSSFCellStyle cellStyle2 = workbook.createCellStyle(); HSSFCreationHelper creationHelper = workbook.getCreationHelper(); cellStyle2.setDataFormat(creationHelper.createDataFormat().getFormat(\"yyyy-MM-dd HH:mm:ss\
在桌面上生成一个Excel文件
public static void readExcel() throws IOException{ FileSystemView fsv = FileSystemView.getFileSystemView(); String desktop = fsv.getHomeDirectory().getPath(); String filePath = desktop + \"/template.xls\"; FileInputStream fileInputStream = new FileInputStream(filePath); BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream); POIFSFileSystem fileSystem = new POIFSFileSystem(bufferedInputStream); HSSFWorkbook workbook = new HSSFWorkbook(fileSystem); HSSFSheet sheet = workbook.getSheet(\"Sheet1\"); int lastRowIndex = sheet.getLastRowNum(); System.out.println(lastRowIndex); for (int i = 0; i <= lastRowIndex; i++) { HSSFRow row = sheet.getRow(i); if (row == null) { break; } short lastCellNum = row.getLastCellNum(); for (int j = 0; j < lastCellNum; j++) { String cellValue = row.getCell(j).getStringCellValue(); System.out.println(cellValue); } } bufferedInputStream.close();}
读取Excel,解析数据
@SuppressWarnings(\"resource\")@RequestMapping(\"/export\
Java Web 中导出和导入Excel
<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3</version></dependency>
使用SpringMVC上传文件,需要用到commons-fileupload
<bean name=\"multipartResolver\" class=\"org.springframework.web.multipart.commons.CommonsMultipartResolver\"> <property name=\"defaultEncoding\" value=\"UTF-8\" /></bean>
需要在spring的配置文件中配置一下multipartResolver
<a href=\"/Spring-Mybatis-Druid/user/export\">导出</a> <br/><form action=\"/Spring-Mybatis-Druid/user/import\" enctype=\"multipart/form-data\" method=\"post\"> <input type=\"file\" name=\"file\"/> <input type=\"submit\" value=\"导入Excel\"></form>
jsp文件
@SuppressWarnings(\"resource\")@RequestMapping(\"/import\")public void importExcel(@RequestParam(\"file\") MultipartFile file) throws Exception{ InputStream inputStream = file.getInputStream(); BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream); POIFSFileSystem fileSystem = new POIFSFileSystem(bufferedInputStream); HSSFWorkbook workbook = new HSSFWorkbook(fileSystem); //HSSFWorkbook workbook = new HSSFWorkbook(file.getInputStream()); HSSFSheet sheet = workbook.getSheetAt(0); int lastRowNum = sheet.getLastRowNum(); for (int i = 2; i <= lastRowNum; i++) { HSSFRow row = sheet.getRow(i); int id = (int) row.getCell(0).getNumericCellValue(); String name = row.getCell(1).getStringCellValue(); int age = (int) row.getCell(2).getNumericCellValue(); System.out.println(id + \"-\" + name + \"-\" + age); }}
解析上传的.xls文件
导入示例
HSSF - 提供读写Microsoft Excel XLS格式档案的功能
XSSF - 提供读写Microsoft Excel OOXML XLSX格式档案的功能
HWPF - 提供读写Microsoft Word DOC格式档案的功能
HSLF - 提供读写Microsoft PowerPoint格式档案的功能
HDGF - 提供读Microsoft Visio格式档案的功能
HPBF - 提供读Microsoft Publisher格式档案的功能
HSMF - 提供读Microsoft Outlook格式档案的功能
POI结构
一个Excel文件对应于一个workbook(HSSFWorkbook),一个workbook可以有多个sheet(HSSFSheet)组成,一个sheet是由多个row(HSSFRow)组成,一个row是由多个cell(HSSFCell)组成
Excel中的工作簿、工作表、行、单元格中的关系
Apache POI
<dependency> <groupId>com.lowagie</groupId> <artifactId>itext</artifactId> <version>2.1.7</version></dependency>
iText是著名的开放源码的站点sourceforge一个项目,是用于生成PDF文档的一个java类库。通过iText不仅可以生成PDF或rtf的文档,而且可以将XML、Html文件转化为PDF文件。 iText的安装非常方便,下载iText.jar文件后,只需要在系统的CLASSPATH中加入iText.jar的路径,在程序中就可以使用iText类库了。
Itext
JasperReports是一个强大、灵活的报表生成工具,能够展示丰富的页面内容,并将之转换成PDF,HTML,或者XML格式。该库完全由Java写成,可以用于在各种Java应用程序,包括J2EE,Web应用程序中生成动态内容。一般情况下,JasperReports会结合Jaspersoft Studio(模板设计器)使用导出PDF报表。
@Testpublic void testJasperReports()throws Exception{ String jrxmlPath = \"D:\\\\ideaProjects\\\\projects111\\\\jasperdemo\\\\src\\\\main\\\esources\\\\demo.jrxml\"; String jasperPath = \"D:\\\\ideaProjects\\\\projects111\\\\jasperdemo\\\\src\\\\main\\\esources\\\\demo.jasper\
<dependency> <groupId>net.sf.jasperreports</groupId> <artifactId>jasperreports</artifactId> <version>6.8.0</version></dependency>
- JRXML:报表填充模板,本质是一个xml文件- Jasper:由JRXML模板编译成的二进制文件,用于代码填充数据- Jrprint:当用数据填充完Jasper后生成的对象,用于输出报表- Exporter:报表输出的管理类,可以指定要输出的报表为何种格式- PDF/HTML/XML:报表形式
JasperReports
PDF生成工具
七牛云存储
若依框架
LayUI
Other technology
MQ全称 Message Queue(消息队列),是在消息的传输过程中保存消息的容器。多用于分布式系统之间进行通信
⚫ MQ,消息队列,存储消息的中间件⚫ 分布式系统通信两种方式:直接远程调用 和 借助第三方 完成间接通信⚫ 发送方称为生产者,接收方称为消费者
总结
⚫ 应用解耦
⚫ 异步提速
⚫ 削峰填谷
优势
系统引入的外部依赖越多,系统稳定性越差。一旦 MQ 宕机,就会对业务造成影响。如何保证MQ的高可用?
⚫ 系统可用性降低
MQ 的加入大大增加了系统的复杂度,以前系统间是同步的远程调用,现在是通过 MQ 进行异步调用。如何保证消息没有被重复消费?怎么处理消息丢失情况?那么保证消息传递的顺序性?
⚫ 系统复杂度提高
A 系统处理完业务,通过 MQ 给B、C、D三个系统发消息数据,如果 B 系统、C 系统处理成功,D 系统处理失败。如何保证消息数据处理的一致性?
⚫ 一致性问题
劣势
小结既然 MQ 有优势也有劣势,那么使用 MQ 需要满足什么条件呢?① 生产者不需要从消费者处获得反馈。引入消息队列之前的直接调用,其接口的返回值应该为空,这才让明明下层的动作还没做,上层却当成动作做完了继续往后走,即所谓异步成为了可能。② 容许短暂的不一致性。③ 确实是用了有效果。即解耦、提速、削峰这些方面的收益,超过加入MQ,管理MQ这些成本
常见的MQ产品
优劣势分析
MQ 的基本概念
AMQP,即 Advanced Message Queuing Protocol(高级消息队列协议),是一个网络协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。2006年,AMQP 规范发布。类比HTTP。
简介
RabbitMQ 基础架构如下图
RabbitMQ 中的相关概念
简单模式、work queues、Publish/Subscribe 发布与订阅模式、Routing路由模式、Topics 主题模式、RPC 远程调用模式
六种工作模式
Java 消息服务(JavaMessage Service)应用程序接口,是一个 Java 平台中关于面向消息中间件的API
JMS
RabbitMQ 的安装和配置
producer
consumer
code
RabbitMQ 快速入门
原型图
与入门程序的简单模式相比,多了一个或一些消费端,多个消费端共同消费同一个队列中的消息
Work Queues
对于任务过重或任务较多情况使用工作队列可以提高任务处理的速度
在一个队列中如果有多个消费者,那么消费者之间对于同一个消息的关系是竞争的关系
Work Queues 对于任务过重或任务较多情况使用工作队列可以提高任务处理的速度。例如:短信服务部署多个,只需要有一个节点成功发送即可
代码编写
工作队列模式
在订阅模型中,多了一个 Exchange 角色,而且过程略有变化
⚫ P:生产者,也就是要发送消息的程序,但是不再发送到队列中,而是发给X(交换机)⚫ C:消费者,消息的接收者,会一直等待消息到来⚫ Queue:消息队列,接收消息、缓存消息⚫ Exchange:交换机(X)。一方面,接收生产者发送的消息。另一方面,知道如何处理消息,例如递交给某个特别队列、递交给所有队列、或是将消息丢弃。到底如何操作,取决于Exchange的类型。Exchange有常见以下3种类型:➢ Fanout:广播,将消息交给所有绑定到交换机的队列➢ Direct:定向,把消息交给符合指定routing key 的队列➢ Topic:通配符,把消息交给符合routing pattern(路由模式) 的队列Exchange(交换机)只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与 Exchange 绑定,或者没有符合路由规则的队列,那么消息会丢失!
模式说明
1. 交换机需要与队列进行绑定,绑定之后;一个消息可以被多个消费者都收到。2. 发布订阅模式与工作队列模式的区别:⚫ 工作队列模式不用定义交换机,而发布/订阅模式需要定义交换机⚫ 发布/订阅模式的生产方是面向交换机发送消息,工作队列模式的生产方是面向队列发送消息(底层使用默认交换机) ⚫ 发布/订阅模式需要设置队列和交换机的绑定,工作队列模式不需要设置,实际上工作队列模式会将队列绑定到默认的交换机
小结
Pub/Sub 订阅模式
⚫ 队列与交换机的绑定,不能是任意绑定了,而是要指定一个 RoutingKey(路由key) ⚫ 消息的发送方在向 Exchange 发送消息时,也必须指定消息的 RoutingKey⚫ Exchange 不再把消息交给每一个绑定的队列,而是根据消息的 Routing Key 进行判断,只有队列的Routingkey 与消息的 Routing key 完全一致,才会接收到消息
图
Routing 路由模式
⚫ Topic 类型与 Direct 相比,都是可以根据 RoutingKey 把消息路由到不同的队列。只不过 Topic 类型Exchange 可以让队列在绑定 Routing key 的时候使用通配符! ⚫ Routingkey 一般都是有一个或多个单词组成,多个单词之间以”.”分割,例如: item.insert ⚫ 通配符规则:# 匹配一个或多个词,* 匹配不多不少恰好1个词,例如:item.# 能够匹配 item.insert.abc 或者 item.insert,item.* 只能匹配 item.insert
图解
Topics 通配符模式
1. 简单模式 HelloWorld一个生产者、一个消费者,不需要设置交换机(使用默认的交换机)。2. 工作队列模式 Work Queue一个生产者、多个消费者(竞争关系),不需要设置交换机(使用默认的交换机)。3. 发布订阅模式 Publish/subscribe需要设置类型为 fanout 的交换机,并且交换机和队列进行绑定,当发送消息到交换机后,交换机会将消息发送到绑定的队列。4. 路由模式 Routing需要设置类型为 direct 的交换机,交换机和队列进行绑定,并且指定 routing key,当发送消息到交换机后,交换机会根据 routing key 将消息发送到对应的队列。5. 通配符模式 Topic需要设置类型为 topic 的交换机,交换机和队列进行绑定,并且指定通配符方式的 routing key,当发送消息到交换机后,交换机会根据 routing key 将消息发送到对应的队列。
工作模式总结
RabbitMQ 的工作模式
步骤图片
<?xml version=\"1.0\" encoding=\"UTF-8\"?><project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"> <modelVersion>4.0.0</modelVersion> <groupId>com.itheima</groupId> <artifactId>spring-rabbitmq-consumers</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.amqp</groupId> <artifactId>spring-rabbit</artifactId> <version>2.1.8.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.1.7.RELEASE</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build></project>
1.pom依赖
rabbitmq.host=192.168.89.66rabbitmq.port=5672rabbitmq.username=fzlrabbitmq.password=fzlrabbitmq.virtual-host=/fzl
rabbitmq配置
<?xml version=\"1.0\" encoding=\"UTF-8\"?><beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:context=\"http://www.springframework.org/schema/context\" xmlns:rabbit=\"http://www.springframework.org/schema/rabbit\" xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://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/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit.xsd\"> <!--加载配置文件--> <context:property-placeholder location=\"classpath:rabbitmq.properties\"/> <!-- 定义rabbitmq connectionFactory --> <rabbit:connection-factory id=\"connectionFactory\" host=\"${rabbitmq.host}\" port=\"${rabbitmq.port}\" username=\"${rabbitmq.username}\" password=\"${rabbitmq.password}\" virtual-host=\"${rabbitmq.virtual-host}\"/> <bean id=\"springQueueListener\" class=\"com.itheima.rabbitmq.listener.SpringQueueListener\"/> <!--<bean id=\"fanoutListener1\" class=\"com.itheima.rabbitmq.listener.FanoutListener1\"/> <bean id=\"fanoutListener2\" class=\"com.itheima.rabbitmq.listener.FanoutListener2\"/> <bean id=\"topicListenerStar\" class=\"com.itheima.rabbitmq.listener.TopicListenerStar\"/> <bean id=\"topicListenerWell\" class=\"com.itheima.rabbitmq.listener.TopicListenerWell\"/> <bean id=\"topicListenerWell2\" class=\"com.itheima.rabbitmq.listener.TopicListenerWell2\"/>--> <rabbit:listener-container connection-factory=\"connectionFactory\" auto-declare=\"true\"> <rabbit:listener ref=\"springQueueListener\" queue-names=\"spring_queue\"/> <!-- <rabbit:listener ref=\"fanoutListener1\" queue-names=\"spring_fanout_queue_1\"/> <rabbit:listener ref=\"fanoutListener2\" queue-names=\"spring_fanout_queue_2\"/> <rabbit:listener ref=\"topicListenerStar\" queue-names=\"spring_topic_queue_star\"/> <rabbit:listener ref=\"topicListenerWell\" queue-names=\"spring_topic_queue_well\"/> <rabbit:listener ref=\"topicListenerWell2\" queue-names=\"spring_topic_queue_well2\"/>--> </rabbit:listener-container></beans>
spring对于rabbitmq的配置
package com.itheima.rabbitmq.listener;import org.springframework.amqp.core.Message;import org.springframework.amqp.core.MessageListener;public class SpringQueueListener implements MessageListener { @Override public void onMessage(Message message) { //打印消息 System.out.println(new String(message.getBody())); }}
接收类具体代码
消费者具体代码
<?xml version=\"1.0\" encoding=\"UTF-8\"?><project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"> <modelVersion>4.0.0</modelVersion> <groupId>com.itheima</groupId> <artifactId>spring-rabbitmq-producers</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.amqp</groupId> <artifactId>spring-rabbit</artifactId> <version>2.1.8.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.1.7.RELEASE</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build></project>
<?xml version=\"1.0\" encoding=\"UTF-8\"?><beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:context=\"http://www.springframework.org/schema/context\" xmlns:rabbit=\"http://www.springframework.org/schema/rabbit\" xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://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/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit.xsd\"> <!--加载配置文件--> <context:property-placeholder location=\"classpath:rabbitmq.properties\"/> <!-- 定义rabbitmq connectionFactory --> <rabbit:connection-factory id=\"connectionFactory\" host=\"${rabbitmq.host}\" port=\"${rabbitmq.port}\" username=\"${rabbitmq.username}\" password=\"${rabbitmq.password}\" virtual-host=\"${rabbitmq.virtual-host}\"/> <!--定义管理交换机、队列--> <rabbit:admin connection-factory=\"connectionFactory\"/> <!--定义持久化队列,不存在则自动创建;不绑定到交换机则绑定到默认交换机 默认交换机类型为direct,名字为:\"\",路由键为队列的名称 --> <!-- id:bean的名称 name:queue的名称 auto-declare:自动创建 auto-delete:自动删除。 最后一个消费者和该队列断开连接后,自动删除队列 exclusive:是否独占 durable:是否持久化 --> <rabbit:queue id=\"spring_queue\" name=\"spring_queue\" auto-declare=\"true\"/> <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~广播;所有队列都能收到消息~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --> <!--定义广播交换机中的持久化队列,不存在则自动创建--> <rabbit:queue id=\"spring_fanout_queue_1\" name=\"spring_fanout_queue_1\" auto-declare=\"true\"/> <!--定义广播交换机中的持久化队列,不存在则自动创建--> <rabbit:queue id=\"spring_fanout_queue_2\" name=\"spring_fanout_queue_2\" auto-declare=\"true\"/> <!--定义广播类型交换机;并绑定上述两个队列--> <rabbit:fanout-exchange id=\"spring_fanout_exchange\" name=\"spring_fanout_exchange\" auto-declare=\"true\"> <rabbit:bindings> <rabbit:binding queue=\"spring_fanout_queue_1\" /> <rabbit:binding queue=\"spring_fanout_queue_2\"/> </rabbit:bindings> </rabbit:fanout-exchange> <!--<rabbit:direct-exchange name=\"aa\" > <rabbit:bindings> <!–direct 类型的交换机绑定队列 key :路由key queue:队列名称–> <rabbit:binding queue=\"spring_queue\" key=\"xxx\"></rabbit:binding> </rabbit:bindings> </rabbit:direct-exchange>--> <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~通配符;*匹配一个单词,#匹配多个单词 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --> <!--定义广播交换机中的持久化队列,不存在则自动创建--> <rabbit:queue id=\"spring_topic_queue_star\" name=\"spring_topic_queue_star\" auto-declare=\"true\"/> <!--定义广播交换机中的持久化队列,不存在则自动创建--> <rabbit:queue id=\"spring_topic_queue_well\" name=\"spring_topic_queue_well\" auto-declare=\"true\"/> <!--定义广播交换机中的持久化队列,不存在则自动创建--> <rabbit:queue id=\"spring_topic_queue_well2\" name=\"spring_topic_queue_well2\" auto-declare=\"true\"/> <rabbit:topic-exchange id=\"spring_topic_exchange\" name=\"spring_topic_exchange\" auto-declare=\"true\"> <rabbit:bindings> <rabbit:binding pattern=\"heima.*\" queue=\"spring_topic_queue_star\"/> <rabbit:binding pattern=\"heima.#\" queue=\"spring_topic_queue_well\"/> <rabbit:binding pattern=\"itcast.#\" queue=\"spring_topic_queue_well2\"/> </rabbit:bindings> </rabbit:topic-exchange> <!--定义rabbitTemplate对象操作可以在代码中方便发送消息--> <rabbit:template id=\"rabbitTemplate\" connection-factory=\"connectionFactory\"/></beans>
package com.itheima;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.amqp.rabbit.core.RabbitTemplate;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations = \"classpath:spring-rabbitmq-producer.xml\")public class ProducerTest { //1.注入 RabbitTemplate @Autowired private RabbitTemplate rabbitTemplate; @Test public void testHelloWorld(){ //2.发送消息 rabbitTemplate.convertAndSend(\"spring_queue\
发送消息具体代码
生产者具体代码
Spring 整合 RabbitMQ
在使用 RabbitMQ 的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败场景。RabbitMQ 为我们提供了两种方式用来控制消息的投递可靠性模式。
⚫ confirm 确认模式⚫ return 退回模式
rabbitmq 整个消息投递的路径为:producer--->rabbitmq broker--->exchange--->queue--->consumer⚫ 消息从 producer 到 exchange 则会返回一个 confirmCallback 。 ⚫ 消息从 exchange-->queue 投递失败则会返回一个 returnCallback 。我们将利用这两个 callback 控制消息的可靠性投递
/** * 确认模式: * 步骤: * 1. 确认模式开启:ConnectionFactory中开启publisher-confirms=\"true\
生产者
确认模式
/** * 回退模式: 当消息发送给Exchange后,Exchange路由到Queue失败是 才会执行 ReturnCallBack * 步骤: * 1. 开启回退模式:publisher-returns=\"true\
回退模式
➢ 设置ConnectionFactory的publisher-confirms=\"true\" 开启 确认模式。➢ 使用rabbitTemplate.setConfirmCallback设置回调函数。当消息发送到exchange后回调confirm方法。在方法中判断ack,如果为true,则发送成功,如果为false,则发送失败,需要处理。➢ 设置ConnectionFactory的publisher-returns=\"true\
消息的可靠投递小结
消息可靠性投递
ack指Acknowledge,确认。 表示消费端收到消息后的确认方式。
• 自动确认:acknowledge=\"none\" • 手动确认:acknowledge=\"manual\"• 根据异常情况确认:acknowledge=\"auto\"
其中自动确认是指,当消息一旦被Consumer接收到,则自动确认收到,并将相应 message 从 RabbitMQ 的消息缓存中移除。但是在实际业务处理中,很可能消息接收到,业务处理出现异常,那么该消息就会丢失。如果设置了手动确认方式,则需要在业务处理成功后,调用channel.basicAck(),手动签收,如果出现异常,则调用channel.basicNack()方法,让其自动重新发送消息。
package com.itheima.listener;import com.rabbitmq.client.Channel;import org.springframework.amqp.core.Message;import org.springframework.amqp.core.MessageListener;import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;import org.springframework.stereotype.Component;import java.io.IOException;/** * Consumer ACK机制: * 1. 设置手动签收。acknowledge=\"manual\
<?xml version=\"1.0\" encoding=\"UTF-8\"?><beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:context=\"http://www.springframework.org/schema/context\" xmlns:rabbit=\"http://www.springframework.org/schema/rabbit\" xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://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/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit.xsd\"> <!--加载配置文件--> <context:property-placeholder location=\"classpath:rabbitmq.properties\"/> <!-- 定义rabbitmq connectionFactory --> <rabbit:connection-factory id=\"connectionFactory\" host=\"${rabbitmq.host}\" port=\"${rabbitmq.port}\" username=\"${rabbitmq.username}\" password=\"${rabbitmq.password}\" virtual-host=\"${rabbitmq.virtual-host}\"/> <context:component-scan base-package=\"com.itheima.listener\" /> <!--定义监听器容器--> <rabbit:listener-container connection-factory=\"connectionFactory\" acknowledge=\"manual\" prefetch=\"1\" > <!-- <rabbit:listener ref=\"ackListener\" queue-names=\"test_queue_confirm\"></rabbit:listener>--> <!-- <rabbit:listener ref=\"qosListener\" queue-names=\"test_queue_confirm\"></rabbit:listener>--> <!--定义监听器,监听正常队列--> <!--<rabbit:listener ref=\"dlxListener\" queue-names=\"test_queue_dlx\"></rabbit:listener>--> <!--延迟队列效果实现: 一定要监听的是 死信队列!!!--> <rabbit:listener ref=\"orderListener\" queue-names=\"order_queue_dlx\"></rabbit:listener> </rabbit:listener-container></beans>
spring配置文件
rabbitmq.host=192.168.89.66rabbitmq.port=5672rabbitmq.username=fzlrabbitmq.password=fzlrabbitmq.virtual-host=/
rabbitmq配置文件
Consumer Ack 小结
Consumer ACK
<rabbit:listener-container connection-factory=\"connectionFactory\" acknowledge=\"manual\" prefetch=\"1\" >
消费端代码
代码演示
➢ 在<rabbit:listener-container> 中配置 prefetch属性设置消费端一次拉取多少消息➢ 消费端的确认模式一定为手动确认。acknowledge=\"manual\"
消费端限流
TTL 全称 Time To Live(存活时间/过期时间)
当消息到达存活时间后,还没有被消费,会被自动清除
RabbitMQ可以对消息设置过期时间,也可以对整个队列(Queue)设置过期时间
概念图
/** * TTL:过期时间 * 1. 队列统一过期 * * 2. 消息单独过期 * * * 如果设置了消息的过期时间,也设置了队列的过期时间,它以时间短的为准。 * 队列过期后,会将队列所有消息全部移除。 * 消息过期后,只有消息在队列顶端,才会判断其是否过期(移除掉) * */ @Test public void testTtl() { /* for (int i = 0; i < 10; i++) { // 发送消息 rabbitTemplate.convertAndSend(\"test_exchange_ttl\
<!--ttl--> <rabbit:queue name=\"test_queue_ttl\" id=\"test_queue_ttl\"> <!--设置queue的参数--> <rabbit:queue-arguments> <!--x-message-ttl指队列的过期时间--> <entry key=\"x-message-ttl\" value=\"100000\" value-type=\"java.lang.Integer\"></entry> </rabbit:queue-arguments> </rabbit:queue>
<rabbit:topic-exchange name=\"test_exchange_ttl\" > <rabbit:bindings> <rabbit:binding pattern=\"ttl.#\" queue=\"test_queue_ttl\"></rabbit:binding> </rabbit:bindings> </rabbit:topic-exchange>
➢ 设置队列过期时间使用参数:x-message-ttl,单位:ms(毫秒),会对整个队列消息统一过期。➢ 设置消息过期时间使用参数:expiration。单位:ms(毫秒),当该消息在队列头部时(消费时),会单独判断这一消息是否过期。➢ 如果两者都进行了设置,以时间短的为准。
TTL 小结
TTL
死信队列,英文缩写:DLX 。Dead Letter Exchange(死信交换机),当消息成为Dead message后,可以被重新发送到另一个交换机,这个交换机就是DLX。
消息成为死信的三种情况
给队列设置参数: x-dead-letter-exchange 和 x-dead-letter-routing-key
队列绑定死信交换机
<!-- 死信队列: 1. 声明正常的队列(test_queue_dlx)和交换机(test_exchange_dlx) 2. 声明死信队列(queue_dlx)和死信交换机(exchange_dlx) 3. 正常队列绑定死信交换机 设置两个参数: * x-dead-letter-exchange:死信交换机名称 * x-dead-letter-routing-key:发送给死信交换机的routingkey --> <!-- 1. 声明正常的队列(test_queue_dlx)和交换机(test_exchange_dlx) --> <rabbit:queue name=\"test_queue_dlx\" id=\"test_queue_dlx\"> <!--3. 正常队列绑定死信交换机--> <rabbit:queue-arguments> <!--3.1 x-dead-letter-exchange:死信交换机名称--> <entry key=\"x-dead-letter-exchange\" value=\"exchange_dlx\" /> <!--3.2 x-dead-letter-routing-key:发送给死信交换机的routingkey--> <entry key=\"x-dead-letter-routing-key\" value=\"dlx.hehe\" /> <!--4.1 设置队列的过期时间 ttl--> <entry key=\"x-message-ttl\" value=\"10000\" value-type=\"java.lang.Integer\" /> <!--4.2 设置队列的长度限制 max-length --> <entry key=\"x-max-length\" value=\"10\" value-type=\"java.lang.Integer\" /> </rabbit:queue-arguments> </rabbit:queue> <rabbit:topic-exchange name=\"test_exchange_dlx\"> <rabbit:bindings> <rabbit:binding pattern=\"test.dlx.#\" queue=\"test_queue_dlx\"></rabbit:binding> </rabbit:bindings> </rabbit:topic-exchange> <!-- 2. 声明死信队列(queue_dlx)和死信交换机(exchange_dlx) --> <rabbit:queue name=\"queue_dlx\" id=\"queue_dlx\"></rabbit:queue> <rabbit:topic-exchange name=\"exchange_dlx\"> <rabbit:bindings> <rabbit:binding pattern=\"dlx.#\" queue=\"queue_dlx\"></rabbit:binding> </rabbit:bindings> </rabbit:topic-exchange>
spring配置
/** * 发送测试死信消息: * 1. 过期时间 * 2. 长度限制 * 3. 消息拒收 */ @Test public void testDlx(){ //1. 测试过期时间,死信消息 //rabbitTemplate.convertAndSend(\"test_exchange_dlx\
监听
死信队列
延迟队列,即消息进入队列后不会立即被消费,只有到达指定时间后,才会被消费。
需求:1. 下单后,30分钟未支付,取消订单,回滚库存。2. 新用户注册成功7天后,发送短信问候。
1. 定时器2. 延迟队列
实现方式:
RabbitMQ中并未提供延迟队列功能但可以使用TTL+死信队列 组合实现延迟队列的效果
图像展示
消费者
@Test public void testDelay() throws InterruptedException { //1.发送订单消息。 将来是在订单系统中,下单成功后,发送消息 rabbitTemplate.convertAndSend(\"order_exchange\
<!-- 延迟队列: 1. 定义正常交换机(order_exchange)和队列(order_queue) 2. 定义死信交换机(order_exchange_dlx)和队列(order_queue_dlx) 3. 绑定,设置正常队列过期时间为30分钟 --> <!-- 1. 定义正常交换机(order_exchange)和队列(order_queue)--> <rabbit:queue id=\"order_queue\" name=\"order_queue\"> <!-- 3. 绑定,设置正常队列过期时间为30分钟--> <rabbit:queue-arguments> <entry key=\"x-dead-letter-exchange\" value=\"order_exchange_dlx\" /> <entry key=\"x-dead-letter-routing-key\" value=\"dlx.order.cancel\" /> <entry key=\"x-message-ttl\" value=\"10000\" value-type=\"java.lang.Integer\" /> </rabbit:queue-arguments> </rabbit:queue> <rabbit:topic-exchange name=\"order_exchange\"> <rabbit:bindings> <rabbit:binding pattern=\"order.#\" queue=\"order_queue\"></rabbit:binding> </rabbit:bindings> </rabbit:topic-exchange> <!-- 2. 定义死信交换机(order_exchange_dlx)和队列(order_queue_dlx)--> <rabbit:queue id=\"order_queue_dlx\" name=\"order_queue_dlx\"></rabbit:queue> <rabbit:topic-exchange name=\"order_exchange_dlx\"> <rabbit:bindings> <rabbit:binding pattern=\"dlx.order.#\" queue=\"order_queue_dlx\"></rabbit:binding> </rabbit:bindings> </rabbit:topic-exchange>
1. 延迟队列 指消息进入队列后,可以被延迟一定时间,再进行消费。2. RabbitMQ没有提供延迟队列功能,但是可以使用 : TTL + DLX 来实现延迟队列效果。
延迟队列
RabbitMQ默认日志存放路径: /var/log/rabbitmq/rabbit@xxx.log
日志包含了RabbitMQ的版本号、Erlang的版本号、RabbitMQ服务节点名称、cookie的hash值、RabbitMQ配置文件地址、内存限制、磁盘限制、默认账户guest的创建以及权限配置等等。
RabbitMQ日志
web管控台监控
查看队列# rabbitmqctl list_queues查看exchanges# rabbitmqctl list_exchanges查看用户# rabbitmqctl list_users查看连接# rabbitmqctl list_connections查看消费者信息# rabbitmqctl list_consumers查看环境变量# rabbitmqctl environment查看未被确认的队列# rabbitmqctl list_queues name messages_unacknowledged查看单个队列的内存使用# rabbitmqctl list_queues name memory查看准备就绪的队列# rabbitmqctl list_queues name messages_ready
rabbitmqctl管理和监控
日志与监控
1可能是因为生产者或消费者与RabbitMQ断开了连接,而它们与RabbitMQ又采用了不同的确认机制;
2也有可能是因为交换器与队列之间不同的转发策略;甚至是交换器并没有与任何队列进行绑定,生产者又不感知或者没有采取相应的措施;
另外RabbitMQ本身的集群策略也可能导致消息的丢失。
在使用任何消息中间件的过程中,难免会出现某条消息异常丢失的情况
firehose的机制是将生产者投递给rabbitmq的消息,rabbitmq投递给消费者的消息按照指定的格式发送到默认的exchange上。这个默认的exchange的名称为amq.rabbitmq.trace,它是一个topic类型的exchange。发送到这个exchange上的消息的routing key为 publish.exchangename 和deliver.queuename。其中exchangename和queuename为实际exchange和queue的名称,分别对应生产者投递到exchange的消息,和消费者从queue上获取的消息。
注意:打开 trace 会影响消息写入功能,适当打开后请关闭。rabbitmqctl trace_on:开启Firehose命令rabbitmqctl trace_off:关闭Firehose命令
消息追踪-Firehose
rabbitmq_tracing和Firehose在实现上如出一辙,只不过rabbitmq_tracing的方式比Firehose多了一层GUI的包装,更容易使用和管理。
启用插件:rabbitmq-plugins enable rabbitmq_tracing
消息追踪-rabbitmq_tracing
在RabbitMQ中可以使用Firehose和rabbitmq_tracing插件功能来实现消息追踪。
消息可靠性分析与追踪
RabbitMQ 高级特性
• 消息补偿机制
消息可靠性保障
幂等性指一次和多次请求某一个资源,对于资源本身应该具有同样的结果。也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同。在MQ中指,消费多条相同的消息,得到与消费该消息一次相同的结果
• 乐观锁解决方案
消息幂等性处理
RabbitMQ 应用问题
RabbitMQ高可用集群
RabbitMQ 集群搭建Conte
RabbitMQ
消息队列
Docker技术
kibana常用命令
使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象
将业务的所有功能集中在一个项目中开发,打成一个包部署
优点:架构简单部署成本低
缺点:耦合度高
单体架构
根据业务功能对系统进行拆分,每个业务模块作为独立项目开发,称为一个服务。
优点:降低服务耦合有利于服务升级拓展
分布式架构
微服务是一种经过良好架构设计的分布式架构方案,微服务架构特征:单一职责:微服务拆分粒度更小,每一个服务都对应唯一的业务能力,做到单一职责,避免重复业务开发面向服务:微服务对外暴露业务接口自治:团队独立、技术独立、数据独立、部署独立隔离性强:服务调用做好隔离、容错、降级,避免出现级联问题
服务治理
服务架构演变
微服务这种方案需要技术框架来落地,全球的互联网公司都在积极尝试自己的微服务落地技术。在国内最知名的就是SpringCloud和阿里巴巴的Dubbo。
使用SpringCloud技术栈服务接口采用Restful风格服务调用采用Feign方式
SpringCloud + Feign
使用SpringCloudAlibaba技术栈服务接口采用Dubbo协议标准服务调用采用Dubbo方式
SpringCloudAlibaba + Dubbo
使用SpringCloudAlibaba技术栈服务接口采用Restful风格服务调用采用Feign方式
SpringCloudAlibaba + Feign
基于Dubbo老旧技术体系服务接口采用Dubbo协议标准服务调用采用Dubbo方式
Dubbo原始模式
微服务
微服务技术对比
SpringCloud是目前国内使用最广泛的微服务框架。官网地址:https://spring.io/projects/spring-cloud。SpringCloud集成了各种微服务功能组件,并基于SpringBoot实现了这些组件的自动装配,从而提供了良好的开箱即用体验:
版本兼容如下
SpringCloud
认识微服务
单一职责:不同微服务,不要重复开发相同业务数据独立:不要访问其它微服务的数据库面向服务:将自己的业务暴露为接口,供其它微服务调用
根据订单id查询订单功能
在order-service的OrderApplication中注册RestTemplate@MapperScan(\"cn.itcast.order.mapper\
1)注册RestTemplate
修改order-service中的OrderService的queryOrderById方法:
@Service public class OrderService { @Autowired private RestTemplate restTemplate; public Order queryOrderById(Long orderId) { // 1.查询订单 Order order = orderMapper.findById(orderId); // TODO 2.查询用户 String url = \"http://localhost:8081/user/\
2)服务远程调用RestTemplate
远程调用
服务拆分及远程调用
服务提供者:一次业务中,被其它微服务调用的服务。(提供接口给其它微服务)服务消费者:一次业务中,调用其它微服务的服务。(调用其它微服务提供的接口)
服务调用关系服务提供者:暴露接口给其它微服务调用服务消费者:调用其它微服务提供的接口提供者与消费者角色其实是 相对的一个服务可以同时是服务提供者和服务消费者
提供者与消费者
微服务拆分案例
远程调用的问题
消费者该如何获取服务提供者具体信息? 服务提供者启动时向eureka注册自己的信息eureka 保存这些信息消费者根据服务名称向eureka拉取提 供者信息如果有多个服务提供者,消费者该如何选择? 服务消费者利用负载均衡算法,从服务列表中挑选一个消费者如何感知服务提供者健康状态? 服务提供者会每隔30秒向EurekaServer发送心跳请求,报告 健康状态eureka会更新记录服务列表信息,心跳不正常会被 剔除消费者就可以拉取到最新的信息
eureka原理
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency>
搭建EurekaServer服务步骤如下:1.创建项目,引入spring-cloud-starter-netflix-eureka-server的依赖
2.编写启动类,添加@EnableEurekaServer注解
server: port: 10086spring: application: name: eurekaservereureka: client: service-url: defaultZone: http://127.0.0.1:10086/eureka/
3.添加application.yml文件
搭建EurekaServer
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
在user-service项目引入spring-cloud-starter-netflix-eureka-client的依赖
spring: application: name: userservice eureka: client: service-url: defaultZone: http://127.0.0.1:10086/eureka/
在application.yml文件
服务注册
<dependency> <groupId>org.springframework.cloud</groupId>| <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
spring: application: name: orderservice eureka: client: service-url: defaultZone: http://127.0.0.1:10086/eureka/
order-service虽然是消费者,但与user-service一样都是eureka的client端,同样可以实现服务注册在order-service项目引入spring-cloud-starter-netflix-eureka-client的依赖在application.yml文件,编写配置
order-service完成服务注册
服务拉取是基于服务名称获取服务列表,然后在对服务列表做负载均衡
String url = \"http://userservice/user/\" + order.getUserId();
1.修改OrderService的代码,修改访问的url路径,用服务名代替ip、端口:
@Bean@LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); }
2.在order-service项目的启动类OrderApplication中的RestTemplate添加负载均衡注解:
在order-service完成服务拉取
服务发现
eureka注册中心
负载均衡流程
负载均衡原理
@Beanpublic IRule randomRule(){ return new RandomRule(); }
代码方式:在order-service中的OrderApplication类中,定义一个新的IRule:
userservice: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule# 负载均衡规则
配置文件方式:在order-service的application.yml文件中,添加新的配置也可以修改规则:
通过定义IRule实现可以修改负载均衡规则,有两种方式:
负载均衡策略
ribbon: eager-load: enabled: true # 开启饥饿加载 clients: userservice # 指定对userservice这个服务饥饿加载
Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启饥饿加载:
饥饿加载
懒加载
Ribbon负载均衡原理
Nacos是阿里巴巴的产品,现在是SpringCloud中的一个组件。相比Eureka功能更加丰富
# 2.Linux安装Linux或者Mac安装方式与Windows类似。## 21.安装JDKNacos依赖于JDK运行,索引Linux上也需要安装JDK才行。上传jdk安装包:上传到某个目录,例如:`/usr/local/`然后解压缩:```shtar -xvf jdk-8u144-linux-x64.tar.gz```然后重命名为java配置环境变量:```shexport JAVA_HOME=/usr/local/javaexport PATH=$PATH:$JAVA_HOME/bin```设置环境变量:```shsource /etc/profile``## 2.2.上传安装包如图:也可以直接使用课前资料中的tar.gz:上传到Linux服务器的某个目录,例如`/usr/local/src`目录下:## 2.3.解压令解压缩安装包:```shtar -xvf nacos-server-1.4.1.tar.gz```然后删除安装包:```shrm -rf nacos-server-1.4.1.tar.gz```目录中最终样式:目录内部:## 2.4.端口配置与windows中类似## 2.5.启动在nacos/bin目录中,输入命令启动Nacos:```shsh startup.sh -m standalone```# 3.Nacos的依赖父工程:```xml<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.2.5.RELEASE</version> <type>pom</type> <scope>import</scope></dependency>```客户端:```xml<!-- nacos客户端依赖包 --><dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>```
# Nacos安装指南# 1.Windows安装开发阶段采用单机安装即可。## 1.1.下载安装包在Nacos的GitHub页面,提供有下载链接,可以下载编译好的Nacos服务端或者源代码:GitHub主页:https://github.com/alibaba/nacosGitHub的Release下载页:https://github.com/alibaba/nacos/releases如图:本课程采用1.4.1.版本的Nacos,课前资料已经准备了安装包:windows版本使用`nacos-server-1.4.1.zip`包即可。## 1.2.解压将这个包解压到任意非中文目录下,如图:目录说明:- bin:启动脚本- conf:配置文件## 1.3.端口配置Nacos的默认端口是8848,如果你电脑上的其它进程占用了8848端口,请先尝试关闭该进程。**无法关闭占用8848端口的进程**,也可以进入nacos的conf目录,修改配置文件中的端口:修改其中的内容:## 1.4.启动启动非常简单,进入bin目录,结构如下:然后执行命令即可:- windows命令: ``` startup.cmd -m standalone ```执行后的效果如图:## .5.访问在浏览器输入地址:http://127.0.0.1:8848/nacos即可:默认的账号和密码都是nacos,进入后:
windows
Nacos安装
认识和安装Nacos
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.2.6.RELEASE</version> <type>pom</type> <scope>import</scope></dependency>
1.在cloud-demo父工程中添加spring-cloud-alilbaba的管理依赖:
2.注释掉order-service和user-service中原有的eureka依赖
<!-- nacos客户端依赖 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>
添加nacos的客户端依赖:
spring: cloud: nacos: server-addr: localhost:8848 # nacos 服务端地址
4.修改user-service&order-service中的application.yml文件,注释eureka地址,添加nacos地址:
启动并测试
服务注册到Nacos
Nacos快速入门
模型案例
服务调用尽可能选择本地集群的服务,跨集群调用延迟较高本地集群不可访问时,再去访问其它集群
服务跨集群调用问题
spring: cloud: nacos: server-addr: localhost:8848 # nacos 服务端地址 discovery: cluster-name: HZ # 配置集群名称,也就是机房位置,例如:HZ,杭州
1.修改application.yml,添加如下内容
2.在Nacos控制台可以看到集群变化
服务集群属性
spring: cloud: nacos: server-addr: localhost:8848 # nacos 服务端地址 discovery: cluster-name: HZ # 配置集群名称,也就是机房位置
1、修改order-service中的application.yml,设置集群为HZ
userservice: ribbon: NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则
2、然后在order-service中设置负载均衡的IRule为NacosRule,这个规则优先会寻找与自己同集群的服务:
根据集群负载均衡
实际部署中会出现这样的场景:服务器设备性能有差异,部分实例所在机器性能较好,另一些较差,我们希望性能好的机器承担更多的用户请求Nacos提供了权重配置来控制访问频率,权重越大则访问频率越高
、根据权重负载均衡
Nacos服务分级存储模型
Nacos中服务存储和数据存储的最外层都是一个名为namespace的东西,用来做最外层隔离
在Nacos控制台可以创建namespace,用来隔离不同环境
然后填写一个新的命名空间信息:
保存后会看到这个namespace的ID
spring: datasource: url: jdbc:mysql://localhost:3306/heima?useSSL=false username: root password: 123 driver-class-name: com.mysql.jdbc.Driver cloud: nacos: server-addr: localhost:8848 discovery: cluster-name: SH # 上海 namespace: 492a7d5d-237b-46a1-a99a-fa8e98e4b0f9 # 命名空间,填ID
修改order-service的application.yml,添加namespace
重启order-service后,再来查看控制台
此时访问order-service,因为namespace不同,会导致找不到userservice
namespace
Nacos环境隔离
服务注册到Nacos时,可以选择注册为临时或非临时实例,通过下面的配置来设置
临时实例宕机时,会从nacos的服务列表中剔除,而非临时实例则不会
spring: cloud: nacos: discovery: ephemeral: false # 设置为非临时实例
临时实例和非临时实例
Nacos注册中心原理
都支持服务提供者心跳方式做健康检测
都支持服务注册和服务拉取
Nacos与eureka的共同点
Nacos支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式
临时实例心跳不正常会被剔除,非临时实例则不会被剔除
Nacos支持服务列表变更的消息推送模式,服务列表更新更及时
Nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式;Eureka采用AP方式
Nacos与Eureka的区别
nacos注册中心
向nacos中添加配置文件
在弹出表单中填写配置信息
配置更改热更新
配置获取的步骤如下:
<!--nacos配置管理依赖--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency>
1.引入Nacos的配置管理客户端依赖
spring: application: name: userservice # 服务名称 profiles: active: dev #开发环境,这里是dev cloud: nacos: server-addr: localhost:8848 # Nacos地址 config: file-extension: yaml # 文件后缀名
2.在userservice中的resource目录添加一个bootstrap.yml文件,这个文件是引导文件,优先级高于application.yml
@RestController @RequestMapping(\"/user\") public class UserController { // 注入nacos中的配置属性 @Value(\"${pattern.dateformat}\") private String dateformat; // 编写controller,通过日期格式化器来格式化现在时间并返回 @GetMapping(\"now\
3.在user-service中将pattern.dateformat这个属性注入到UserController中做测试
具体代码
在Nacos中添加配置文件
在微服务中引入nacos的config依赖
在微服务中添加bootstrap.yml,配置nacos地址、当前环境、服务名称、文件后缀名。这些决定了程序启动时去nacos读取哪个文件
将配置交给Nacos管理的步骤
统一配置管理
方式一:在@Value注入的变量所在类上添加注解@RefreshScope
方式二:使用@ConfigurationProperties注解
Nacos配置更改后,微服务可以实现热更新
不是所有的配置都适合放到配置中心,维护起来比较麻烦
建议将一些关键参数,需要运行时调整的参数放到nacos配置中心,一般都是自定义配置
注意事项
Nacos中的配置文件变更后,微服务无需重启就可以感知。不过需要通过下面两种配置实现
配置热更新
无论profile如何变化,[spring.application.name].yaml这个文件一定会加载,因此多环境共享配置可以写入这个文件
[spring.application.name]-[spring.profiles.active].yaml,例如:userservice-dev.yaml[spring.application.name].yaml,例如:userservice.yaml
多种配置的优先级
[服务名]-[spring.profile.active].yaml,环境配置
[服务名].yaml,默认配置,多环境共享
微服务会从nacos读取的配置文件
优先级:[服务名]-[环境].yaml >[服务名].yaml > 本地配置
多种配置
方式一
方式二
不同微服务之间可以共享配置文件,通过下面的两种方式来指定
多服务共享配置
配置共享
搭建MySQL集群并初始化数据库表
下载解压nacos
修改集群配置(节点信息)、数据库配置
分别启动多个nacos节点
nginx反向代理
集群搭建步骤
搭建Nacos集群
Nacos配置管理
代码可读性差,编程体验不统一
参数复杂URL难以维护
存在下面的问题
RestTemplate方式调用存在的问题
Feign是一个声明式的http客户端
Feign的介绍
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId></dependency>
在order-service的启动类添加注解开启Feign的功能
编写Feign客户端
用Feign客户端代替RestTemplate
使用Feign的步骤如下
定义和使用Feign客户端
Feign替代RestTemplete
Feign运行自定义配置来覆盖默认配置,可以修改的配置如下 一般需要的是日志级别
全局生效
局部生效
方式一:配置文件方式
@EnableFeignClients(defaultConfiguration = FeignClientConfiguration.class)
而后如果是全局配置,则把它放到@EnableFeignClients这个注解中
@FeignClient(value = \"userservice\
如果是局部配置,则把它放到@FeignClient这个注解中
方式二:java代码方式
配置Feign日志有两种方式
自定义Feign的配置
自定义配置
URLConnection:默认实现,不支持连接池
Apache HttpClient :支持连接池
OKHttp:支持连接池
Feign底层的客户端实现
使用连接池代替默认的URLConnection
日志级别,最好用basic或none
因此优化Feign的性能主要包括
Feign的性能优化
配置连接池
Feign添加HttpClient的支持
Feign的性能优化-连接池配置
Feign使用优化
给消费者的FeignClient和提供者的controller定义统一的父接口作为标准
父接口参数列表中的映射不会被继承
服务紧耦合
方式一(继承)
方式二(抽取):将FeignClient抽取为独立模块,并且把接口有关的POJO、默认的Feign配置都放到这个模块中,提供给所有消费者使用
首先创建一个module,命名为feign-api,然后引入feign的starter依赖
将order-service中编写的UserClient、User、DefaultFeignConfiguration都复制到feign-api项目中
在order-service中引入feign-api的依赖
修改order-service中的所有与上述三个组件有关的import部分,改成导入feign-api中的包
重启测试
当定义的FeignClient不在SpringBootApplication的扫描包范围时,这些FeignClient无法使用。有两种方式解决
抽取FeignClient
最佳实践
Feign远程调用
身份认证和权限校验
服务路由、负载均衡
请求限流
网关功能
而SpringCloudGateway则是基于Spring5中提供的WebFlux,属于响应式编程的实现,具备更好的性能。
gateway
Zuul是基于Servlet的实现,属于阻塞式编程。
zuul
在SpringCloud中网关的实现包括两种
网关的技术实现
为什么需要网关
创建新的module,引入SpringCloudGateway的依赖和nacos的服务发现依赖
编写路由配置及nacos地址
路由id:路由的唯一标示
路由目标(uri):路由的目标地址,http代表固定地址,lb代表根据服务名负载均衡
路由断言(predicates):判断路由的规则
路由过滤器(filters):对请求或响应做处理
路由配置包括
搭建网关服务的步骤
gateway快速入门
路由id:路由唯一标示
uri:路由目的地,支持lb和http两种
predicates:路由断言,判断请求是否符合要求,符合则转发到路由目的地
filters:路由过滤器,处理请求或响应
网关路由可以配置的内容包括
路由断言工厂Route Predicate Factory
断言工厂
GatewayFilter是网关中提供的一种过滤器可以对进入网关的请求和微服务返回的响应做处理
给所有进入userservice的请求添加一个请求头
如果要对所有的路由都生效,则可以将过滤器工厂写到default下
默认过滤器
路由过滤器 GatewayFilter
过滤器工厂
全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用一样。区别在于GatewayFilter通过配置定义,处理逻辑是固定的。而GlobalFilter的逻辑需要自己写代码实现。
定义方式是实现GlobalFilter接口
需求:定义全局过滤器,拦截请求,判断请求的参数是否满足下面条件:参数中是否有authorization,authorization参数值是否为admin如果同时满足则放行,否则拦截
步骤1:自定义过滤器
定义全局过滤器,拦截并判断用户身份
请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器
当前路由的过滤器、DefaultFilter、GlobalFilter
每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前。GlobalFilter通过实现Ordered接口,或者添加@Order注解来指定order值,由我们自己指定路由过滤器和defaultFilter的order由Spring指定,默认是按照声明顺序从1递增。当过滤器的order值一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter的顺序执行。
过滤器执行顺序
全局过滤器
域名不同: www.taobao.com 和 www.taobao.org 和 www.jd.com 和 miaosha.jd.com
域名相同,端口不同:localhost:8080和localhost8081
域名不一致就是跨域,主要包括
解决方案:CORS
跨域问题:浏览器禁止请求的发起者与服务端发生跨域ajax请求,请求被浏览器拦截的问题
网关处理跨域采用的同样是CORS方案,并且只需要简单配置即可实现
跨域问题
限流过滤器-漏桶算法
令牌桶算法
计数器算法
统一网关Gateway
GateWay服务网关
微服务治理
生产者发送的消息未送达exchange
消息到达exchange后未到达queue
发送时丢失
MQ宕机,queue将消息丢失
consumer接收到消息后未消费就宕机
消息从生产者发送到exchange,再到queue,再到消费者,有哪些导致消息丢失的可能性?
消息成功投递到交换机,返回ack
消息未投递到交换机,返回nack
publisher-confirm,发送者确认
消息投递到交换机了,但是没有路由到队列。返回ACK,及路由失败原因
publisher-return,发送者回执
RabbitMQ提供了publisher confirm机制来避免消息发送到MQ过程中丢失。消息发送到MQ以后,会返回一个结果给发送者,表示消息是否处理成功。
在publisher这个微服务的application.yml中添加配置
simple:同步等待confirm结果,直到超时
correlated:异步回调,定义ConfirmCallback,MQ返回结果时会回调这个ConfirmCallback
publish-confirm-type:开启publisher-confirm,这里支持两种类型
publish-returns:开启publish-return功能,同样是基于callback机制,不过是定义ReturnCallback
template.mandatory:定义消息路由失败时的策略。true,则调用ReturnCallback;false:则直接丢弃消息
配置说明
每个RabbitTemplate只能配置一个ReturnCallback
发送消息,指定消息ID、消息ConfirmCallback
SpringAMQP实现生产者确认
生产者消息确认
交换机持久化
队列持久化
消息持久化,SpringAMQP中的的消息默认是持久的,可以通过MessageProperties中的DeliveryMode来指定的
MQ默认是内存存储消息,开启持久化功能可以确保缓存在MQ中的消息不丢失。
消息持久化
RabbitMQ支持消费者确认机制,即:消费者处理消息后可以向MQ发送ack回执,MQ收到ack回执后才会删除该消息
manual:手动ack,需要在业务代码结束后,调用api发送ack。
auto:自动ack,由spring监测listener代码是否出现异常,没有异常则返回ack;抛出异常则返回nack
none:关闭ack,MQ假定消费者获取消息后会成功处理,因此消息投递后立即被删除
SpringAMQP则允许配置三种确认模式
配置方式
消费者消息确认
当消费者出现异常后,消息会不断requeue(重新入队)到队列,再重新发送给消费者,然后再次异常,再次requeue,无限循环,导致mq的消息处理飙升,带来不必要的压力
RejectAndDontRequeueRecoverer:重试耗尽后,直接reject,丢弃消息。默认就是这种方式
ImmediateRequeueMessageRecoverer:重试耗尽后,返回nack,消息重新入队
RepublishMessageRecoverer:重试耗尽后,将失败消息投递到指定的交换机
在开启重试模式后,重试次数耗尽,如果消息依然失败,则需要有MessageRecoverer接口来处理
消费者失败消息处理策略
消费失败重试机制
消息可靠性
消费者使用basic.reject或 basic.nack声明消费失败,并且消息的requeue参数设置为false
消息是一个过期消息,超时无人消费
要投递的队列消息堆积满了,最早的消息可能成为死信
当一个队列中的消息满足下列情况之一时,可以成为死信(dead letter)
如果该队列配置了dead-letter-exchange属性,指定了一个交换机,那么队列中的死信就会投递到这个交换机中,而这个交换机称为死信交换机(Dead Letter Exchange,简称DLX)
初始死信队列
消息所在的队列设置了存活时间
消息本身设置了存活时间
TTL,也就是Time-To-Live。如果一个队列中的消息TTL结束仍未消费,则会变为死信,ttl超时分为两种情况
基于注解方式生成
利用TTL结合死信交换机,我们实现了消息发出后,消费者延迟收到消息的效果。这种消息模式就称为延迟队列(Delay Queue)模式。
延迟发送短信
用户下单,如果用户在15 分钟内未支付,则自动取消
预约工作会议,20分钟后自动通知所有参会人员
延迟队列的使用场景包括
延迟队列插件
DelayExchange的本质还是官方的三种交换机,只是添加了延迟功能。因此使用时只需要声明一个交换机,交换机的类型可以是任意类型,然后设定delayed属性为true即可。
然后我们向这个delay为true的交换机中发送消息,一定要给消息添加一个header:x-delay,值为延迟的时间,单位为毫秒
死信交换机
当生产者发送消息的速度超过了消费者处理消息的速度,就会导致队列中的消息堆积,直到队列存储消息达到上限。最早接收到的消息,可能就会成为死信,会被丢弃,这就是消息堆积问题。
增加更多消费者,提高消费速度
在消费者内开启线程池加快消息处理速度
扩大队列容积,提高堆积上限
解决消息堆积有三种种思路
消息堆积问题
从RabbitMQ的3.6.0版本开始,就增加了Lazy Queues的概念,也就是惰性队列
接收到消息后直接存入磁盘而非内存
消费者要消费消息时才会从磁盘中读取并加载到内存
支持数百万条的消息存储
惰性队列的特征如下
rabbitmqctl set_policy Lazy \"^lazy-queue$\" '{\"queue-mode\":\"lazy\"}' --apply-to queues
要设置一个队列为惰性队列,只需要在声明队列时,指定x-queue-mode属性为lazy即可。可以通过命令行将一个运行中的队列修改为惰性队列
惰性队列
RabbitMQ的是基于Erlang语言编写,而Erlang又是一个面向并发的语言,天然支持集群模式。RabbitMQ的集群有两种模式
集群分类
普通集群:是一种分布式集群,将队列分散到集群的各个节点,从而提高整个集群的并发能力。
会在集群的各个节点间共享部分数据,包括:交换机、队列元信息。不包含队列中的消息。
当访问集群某节点时,如果队列不在该节点,会从数据所在节点传递到当前节点并返回
队列所在节点宕机,队列中的消息就会丢失
具备下列特征
普通集群
镜像集群:是一种主从集群,普通集群的基础上,添加了主从备份功能,提高集群的数据可用性。
交换机、队列、队列中的消息会在各个mq的镜像节点之间同步备份。
创建队列的节点被称为该队列的主节点,备份到的其它节点叫做该队列的镜像节点
一个队列的主节点可能是另一个队列的镜像节点
所有操作都是主节点完成,然后同步给镜像节点
主宕机后,镜像节点会替代成新的主
镜像集群:本质是主从模式,具备下面的特征
镜像集群
底层采用Raft协议确保主从的数据一致性。
与镜像队列一样,都是主从模式,支持主从数据同步
使用非常简单,没有复杂的配置
主从同步基于Raft协议,强一致
仲裁队列:仲裁队列是3.8版本以后才有的新功能,用来替代镜像队列,具备下列特征
仲裁队列
MQ集群
异步通信
RDB全称Redis Database Backup file(Redis数据备份文件),也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后,从磁盘读取快照文件,恢复数据。
快照文件称为RDB文件,默认是保存在当前运行目录。
bgsave开始时会fork主进程得到子进程,子进程共享主进程的内存数据。完成fork后读取内存数据并写入 RDB 文件。
当主进程执行读操作时,访问共享内存
当主进程执行写操作时,则会拷贝一份数据,执行写操作
fork采用的是copy-on-write技术
fork主进程得到一个子进程,共享内存空间
子进程读取内存数据并写入新的RDB文件
用新RDB文件替换旧的RDB文件
RDB方式bgsave的基本流程
默认是服务停止时
代表60秒内至少执行1000次修改则触发RDB
RDB会在什么时候执行?save 60 1000代表什么含义
RDB执行间隔时间长,两次RDB之间写入数据有丢失的风险
fork子进程、压缩、写出RDB文件都比较耗时
RDB的缺点
RDB
AOF全称为Append Only File(追加文件)。Redis处理的每一个写命令都会记录在AOF文件,可以看做是命令日志文件
AOF默认是关闭的,需要修改redis.conf配置文件来开启AOF
因为是记录命令,AOF文件会比RDB文件大的多。而且AOF会记录对同一个key的多次写操作但只有最后一次写操作才有意义。通过执行bgrewriteaof命令,可以让AOF文件执行重写功能,用最少的命令达到相同效果。
RDB和AOF各有自己的优缺点,如果对数据安全性要求较高,在实际开发中往往会结合两者来使用。
AOF
Redis持久化
单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离
原理图
搭建主从架构
主从第一次同步是全量同步
Replication Id:简称replid,是数据集的标记,id一致则说明是同一数据集。每一个master都有唯一的replid,slave则会继承master节点的replid
offset:偏移量,随着记录在repl_baklog中的数据增多而逐渐增大。slave完成同步时也会记录当前同步的offset。如果slave的offset小于master的offset,说明slave数据落后于master,需要更新。
master如何判断slave是不是第一次来同步数据?
slave节点请求增量同步
master节点判断replid,发现不一致,拒绝增量同步
master将完整内存数据生成RDB,发送RDB到slave
slave清空本地数据,加载master的RDB
master将RDB期间的命令记录在repl_baklog,并持续将log中的命令发送给slave
slave执行接收到的命令,保持与master之间的同步
简述全量同步的流程?
如果slave重启后同步,则执行增量同步
主从数据同步原理
在master中配置repl-diskless-sync yes启用无磁盘复制,避免全量同步时的磁盘IO
Redis单节点上的内存占用不要太大,减少RDB导致的过多磁盘IO
适当提高repl_baklog的大小,发现slave宕机时尽快实现故障恢复,尽可能避免全量同步
限制一个master上的slave节点数量,如果实在是太多slave,则可以采用主-从-从链式结构,减少master压力
可以从以下几个方面来优化Redis主从就集群
全量同步:master将完整内存数据生成RDB,发送RDB到slave。后续命令则记录在repl_baklog,逐个发送给slave。
增量同步:slave提交自己的offset到master,master获取repl_baklog中从offset之后的命令给slave
简述全量同步和增量同步区别
slave节点第一次连接master节点时
slave节点断开时间太久,repl_baklog中的offset已经被覆盖时
什么时候执行全量同步?
slave节点断开又恢复,并且在repl_baklog中能找到offset时
什么时候执行增量同步?
Redis主从
Sentinel基于心跳机制监测服务状态,每隔1秒向集群的每个实例发送ping命令
主观下线:如果某sentinel节点发现某实例未在规定时间响应,则认为该实例主观下线
客观下线:若超过指定数量(quorum)的sentinel都认为该实例主观下线,则该实例客观下线。quorum值最好超过Sentinel实例数量的一半。
首先会判断slave节点与master节点断开时间长短,如果超过指定值(down-after-milliseconds * 10)则会排除该slave节点
然后判断slave节点的slave-priority值,越小优先级越高,如果是0则永不参与选举
如果slave-prority一样,则判断slave节点的offset值,越大说明数据越新,优先级越高
最后是判断slave节点的运行id大小,越小优先级越高。
一旦发现master故障,sentinel需要在salve中选择一个作为新的master,选择依据是这样的
选举新的master
监控:Sentinel 会不断检查您的master和slave是否按预期工作
sentinel给备选的slave1节点发送slaveof no one命令,让该节点成为master
sentinel给所有其它slave发送slaveof 192.168.150.101 7002 命令,让这些slave成为新master的从节点,开始从新的master上同步数据。
最后,sentinel将故障节点标记为slave,当故障节点恢复后会自动成为新的master的slave节点
当选中了其中一个slave为新的master后(例如slave1),故障的转移的步骤如下:
如何实现故障转移
自动故障恢复:如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主
通知:Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端
Redis提供了哨兵(Sentinel)机制来实现主从集群的自动故障恢复。哨兵的结构和作用如下:
监控
故障转移
通知
Sentinel的三个作用是什么?
每隔1秒发送一次ping命令,如果超过一定时间没有相向则认为是主观下线如果大多数sentinel都认为实例主观下线,则判定服务下线
Sentinel如何判断一个redis实例是否健康?
首先选定一个slave作为新的master,执行slaveof no one
然后让所有节点都执行slaveof 新master
修改故障节点配置,添加slaveof 新master
故障转移步骤有哪些
哨兵的作用和原理
在Sentinel集群监管下的Redis主从集群,其节点会因为自动故障转移而发生变化,Redis的客户端必须感知这种变化,及时更新连接信息。Spring的RedisTemplate底层利用lettuce实现了节点的感知和自动切换
搭建哨兵集群
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency>
在pom文件中引入redis的starter依赖
spring: redis: sentinel: master: mymaster # 指定master名称 nodes: # 指定redis-sentinel集群信息 - 192.168.150.101:27001 - 192.168.150.101:27002 - 192.168.150.101:27003
然后在配置文件application.yml中指定sentinel相关信息
配置主从读写分离
RedisTemplate的哨兵模式
Redis哨兵
海量数据存储问题
高并发写的问题
主从和哨兵可以解决高可用、高并发读的问题。但是依然有两个问题没有解决:
集群中有多个master,每个master保存不同数据
每个master都可以有多个slave节点
master之间通过ping监测彼此健康状态
客户端请求可以访问集群任意节点,最终都会被转发到正确节点
使用分片集群可以解决上述问题,分片集群特征
搭建分片集群
Redis会把每一个master节点映射到0~16383共16384个插槽(hash slot)上,查看集群信息时就能看到
将16384个插槽分配到不同的实例根据key的有效部分计算哈希值,对16384取余余数作为插槽,寻找插槽所在实例即可
Redis如何判断某个key应该在哪个实例?
这一类数据使用相同的有效部分,例如key都以{typeId}为前缀
如何将同一类数据固定的保存在同一个Redis实例?
散列插槽
添加一个节点到集群
集群伸缩
当集群中有一个master宕机会发生什么呢?
利用cluster failover命令可以手动让集群中的某个master宕机,切换到执行cluster failover命令的这个slave节点,实现无感知的数据迁移。其流程如下
缺省:默认的流程,如图1~6歩
force:省略了对offset的一致性校验
takeover:直接执行第5歩,忽略数据一致性、忽略master状态和其它master的意见
手动的Failover支持三种不同模式
使用命令:执行cluster failover命令 重新成为master
数据迁移
引入redis的starter依赖配置分片集群地址配置读写分离
与哨兵模式相比,其中只有分片集群的配置方式略有差异
RedisTemplate底层同样基于lettuce实现了分片集群的支持,而使用的步骤与哨兵模式基本一致:
RedisTemplate访问分片集群
Redis分片集群
分布式缓存
ES
分布式搜索
微服务调用链路中的某个服务故障,引起整个链路中的所有微服务都不可用,这就是雪崩。
图示
设定超时时间,请求超过一定时间没有响应就返回错误信息,不会无休止等待
超时处理
方案一
限定每个业务能使用的线程数,避免耗尽整个tomcat的资源,因此也叫线程隔离。
舱壁模式
方案二
由断路器统计业务执行的异常比例,如果超出阈值则会熔断该业务,拦截访问该业务的一切请求。
熔断降级
方案三
限制业务访问的QPS,避免服务因流量的突增而故障。
流量控制
方案四
解决方案
雪崩问题及解决方案
服务保护技术对比
Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
丰富的应用场景
Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
完备的实时监控
Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
广泛的开源生态
Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
完善的 SPI 扩展点
Sentinel 具有以下特征
官网地址
Sentinel介绍和安装
引入sentinel依赖
配置控制台地址
访问任何地址,都可验证
微服务整合Sentinel
初识Sentinel
就是项目内的调用链路,链路中被监控的每个接口就是一个资源。默认情况下sentinel会监控SpringMVC的每一个端点(Endpoint),因此SpringMVC的每一个端点(Endpoint)就是调用链路中的一个资源。
流控、熔断等都是针对簇点链路中的资源来设置的
通过添加流控规则实现
簇点链路
统计当前资源的请求,触发阈值时对当前资源直接限流,也是默认的模式
直接
统计与当前资源相关的另一个资源,触发阈值时,对当前资源限流
当/write资源访问量触发阈值时,就会对/read资源限流,避免影响/write资源
比如用户支付时需要修改订单状态,同时用户要查询订单。查询和修改操作会争抢数据库锁,产生竞争。业务需求是有限支付和更新订单的业务,因此当修改订单业务触发阈值时,需要对查询订单业务限流。
需求:在OrderController新建两个端点:/order/query和/order/update,无需实现业务配置流控规则,当/order/ update资源被访问的QPS超过5时,对/order/query请求限流
满足下面条件可以使用关联模式:两个有竞争关系的资源一个优先级较高,一个优先级较低
使用场景
关联
统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流
只针对从指定链路访问到本资源的请求做统计,判断是否超过阈值
步骤:1.在OrderService中添加一个queryGoods方法,不用实现业务在OrderController中,2.改造/order/query端点,调用OrderService中的queryGoods方法3.在OrderController中添加一个/order/save的端点,调用OrderService的queryGoods方法给queryGoods4.设置限流规则,从/order/query进入queryGoods的方法限制QPS必须小于2
需求:有查询订单和创建订单业务,两者都需要查询商品。针对从查询订单进入到查询商品的请求统计,并设置限流。
需求
链路
流控模式
达到阈值后,新的请求会被立即拒绝并抛出FlowException异常。是默认的处理方式
快速失败
warm up也叫预热模式,是应对服务冷启动的一种方案。请求阈值初始值是 threshold / coldFactor,持续指定时长后,逐渐提高到threshold值。而coldFactor的默认值是3.例如,我设置QPS的threshold为10,预热时间为5秒,那么初始阈值就是 10 / 3 ,也就是3,然后在5秒后逐渐增长到10.
预热模式,对超出阈值的请求同样是拒绝并抛出异常。但这种模式阈值会动态变化,从一个较小值逐渐增加到最大阈值。
warm up
当请求超过QPS阈值时,快速失败和warm up 会拒绝新的请求并抛出异常。而排队等待则是让所有请求进入一个队列中,然后按照阈值允许的时间间隔依次执行。后来的请求必须等待前面执行完成,如果请求预期的等待时间超出最大时长,则会被拒绝。
让所有的请求按照先后次序排队执行,两个请求的间隔不能小于指定时长
排队等待
流控效果
热点参数限流是分别统计参数值相同的请求,判断是否超过QPS阈值。
在热点参数限流的高级选项中,可以对部分参数设置例外配置
热点参数限流
虽然限流可以尽量避免因高并发而引起的服务故障,但服务还会因为其它原因而故障。而要将这些故障控制在一定范围,避免雪崩,就要靠线程隔离(舱壁模式)和熔断降级手段了。
不管是线程隔离还是熔断降级,都是对客户端(调用方)的保护
修改OrderService的application.yml文件,开启Feign的Sentinel功能
方式一:FallbackClass,无法对远程调用的异常做处理
FallbackFactory,可以对远程调用的异常做处理
给FeignClient编写失败后的降级逻辑
在feing-api项目中定义类,实现FallbackFactory
步骤二:在feing-api项目中的DefaultFeignConfiguration类中将UserClientFallbackFactory注册为一个Bean
步骤三:在feing-api项目中的UserClient接口中使用UserClientFallbackFactory
FeignClient整合Sentinel
支持主动超时支持异步调用
优点
线程的额外开销比较大
缺点
低扇出
场景
线程池隔离
轻量级,无额外开销
不支持主动超时不支持异步调用
高频调用高扇出
信号量隔离(Sentinel默认采用)
线程隔离有两种方式实现
QPS:就是每秒的请求数线程数:是该资源能使用用的tomcat线程数的最大值。也就是通过限制线程数量,实现舱壁模式。
在添加限流规则时,可以选择两种阈值类型
线程隔离(舱壁模式)
熔断降级是解决雪崩问题的重要手段。其思路是由断路器统计服务调用的异常比例、慢请求比例,如果超出阈值则会熔断该服务。即拦截访问该服务的一切请求;而当服务恢复时,断路器会放行访问该服务的请求。
慢调用:业务的响应时长(RT)大于指定时长的请求认定为慢调用请求。在指定时间内,如果请求数量超过设定的最小数量,慢调用比例大于设定的阈值,则触发熔断。
RT超过500ms的调用是慢调用,统计最近10000ms内的请求,如果请求量超过10次,并且慢调用比例不低于0.5,则触发熔断,熔断时长为5秒。然后进入half-open状态,放行一次请求做测试。
需求:给 UserClient的查询用户接口设置降级规则,慢调用的RT阈值为50ms,统计时间为1秒,最小请求数量为5,失败阈值比例为0.4,熔断时长为5
熔断策略-慢调用
异常比例或异常数:统计指定时间内的调用,如果调用次数超过指定请求数,并且出现异常的比例达到设定的比例阈值(或超过指定异常数),则触发熔断。
需求:给 UserClient的查询用户接口设置降级规则,统计时间为1秒,最小请求数量为5,失败阈值比例为0.4,熔断时长为5s
熔断策略-异常比例、异常数
隔离和降级
白名单:来源(origin)在白名单内的调用者允许访问
黑名单:来源(origin)在黑名单内的调用者不允许访问
授权规则可以对调用方的来源做控制,有白名单和黑名单两种方式
public interface RequestOriginParser { /** * 从请求request对象中获取origin,获取方式自定义 */ String parseOrigin(HttpServletRequest request); }
Sentinel是通过RequestOriginParser这个接口的parseOrigin来获取请求的来源的
在gateway服务中,利用网关的过滤器添加名为gateway的origin头
给/order/{orderId} 配置授权规则
默认情况下,发生限流、降级、授权拦截时,都会抛出异常到调用方。如果要自定义异常时的返回结果,需要实现BlockExceptionHandler接口
BlockException包含多个子类
自定义异常结果
授权规则
管理的三种模式
控制台配置的规则直接推送到Sentinel客户端,也就是我们的应用。然后保存在内存中,服务重启则丢失
规则管理模式-原始模式
pull模式:控制台将配置的规则推送到Sentinel客户端,而客户端会将配置规则保存在本地文件或数据库中。以后会定时去本地文件或数据库中查询,更新本地规则。
规则管理模式-pull模式
push模式:控制台将配置规则推送到远程配置中心,例如Nacos。Sentinel客户端监听Nacos,获取配置变更的推送消息,完成本地配置更新。
规则管理模式-push模式
实现push模式
规则持久化
微服务保护
事务中的所有操作,要么全部成功,要么全部失败
A——原子性
要保证数据库内部完整性约束、声明性约束
C——一致性
对同一资源操作的事务不能同时发生
I——隔离性
对数据库做的一切修改将永久保存,不管是否出现故障
D——持久性
事务的四个属性(ACID)
在分布式系统下,一个业务跨越多个服务或数据源,每个服务都是一个分支事务,要保证所有分支事务最终状态一致,这样的事务就是分布式事务。
分布式事务问题
Consistency(一致性):用户访问分布式系统中的任意节点,得到的数据必须一致
Consistency(一致性)
Availability (可用性):用户访问集群中的任意健康节点,必须能得到响应,而不是超时或拒绝
Availability(可用性)
Partition(分区):因为网络故障或其它原因导致分布式系统中的部分节点与其它节点失去连接,形成独立分区。Tolerance(容错):在集群出现分区时,整个系统也要持续对外提供服务
Partition tolerance (分区容错性)
分布式系统节点通过网络连接,一定会出现分区问题(P)当分区出现时,系统的一致性(C)和可用性(A)就无法同时满足
CAP定理
Basically Available (基本可用):分布式系统在出现故障时,允许损失部分可用性,即保证核心可用。
Soft State(软状态):在一定时间内,允许出现中间状态,比如临时的不一致状态。
Eventually Consistent(最终一致性):虽然无法保证强一致性,但是在软状态结束后,最终达到数据一致。
BASE理论是对CAP的一种解决思路,包含三个思想
BASE理论
各子事务分别执行和提交,允许出现结果不一致,然后采用弥补措施恢复数据即可,实现最终一致。
AP模式
各个子事务执行后互相等待,同时提交,同时回滚,达成强一致。但事务等待过程中,处于弱可用状态。
CP模式
解决分布式事务,各个子系统之间必须能感知到彼此的事务状态,才能保证状态一致,因此需要一个事务协调者来协调每一个事务的参与者(子系统事务)。这里的子系统事务,称为分支事务;有关联的各个分支事务在一起称为全局事务
分布式事务模型
理论基础
致力于提供高性能和简单易用的分布式事务服务,为用户打造一站式的分布式解决方案。
TC (Transaction Coordinator) - 事务协调者:维护全局和分支事务的状态,协调全局事务提交或回滚。
TM (Transaction Manager) - 事务管理器:定义全局事务的范围、开始全局事务、提交或回滚全局事务。
RM (Resource Manager) - 资源管理器:管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
Seata事务管理中有三个重要的角色
XA模式:强一致性分阶段事务模式,牺牲了一定的可用性,无业务侵入
TCC模式:最终一致的分阶段事务模式,有业务侵入
AT模式:最终一致的分阶段事务模式,无业务侵入,也是Seata的默认模式
SAGA模式:长事务模式,有业务侵入
Seata提供了四种不同的分布式事务解决方案
Seata的架构
部署TC服务
首先,引入seata相关依赖
然后,配置application.yml,让微服务通过注册中心找到seata-tc-server
微服务集成Seata
初识Seata
XA 规范 是 X/Open 组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准,XA 规范 描述了全局的TM与局部的RM之间的接口,几乎所有主流的数据库都对 XA 规范 提供了支持。
注册分支事务到TC
执行分支业务sql但不提交
报告执行状态到TC
RM一阶段的工作
如果都成功,通知所有RM提交事务
如果有失败,通知所有RM回滚事务
TC检测各分支事务执行状态
TC二阶段的工作
接收TC指令,提交或回滚事务
RM二阶段的工作
流程图
seata的XA模式做了一些调整,但大体相似
事务的强一致性,满足ACID原则
常用数据库都支持,实现简单,并且没有代码侵入
XA模式的优点是
因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差
依赖关系型数据库实现事务
XA模式的缺点
修改application.yml文件(每个参与事务的微服务),开启XA模式:
给发起全局事务的入口方法添加@GlobalTransactional注解,本例中是OrderServiceImpl中的create方法
重启服务并测试
Seata的starter已经完成了XA模式的自动装配
实现XA模式
XA模式
AT模式同样是分阶段提交的事务模型,不过缺弥补了XA模型中资源锁定周期过长的缺陷。
注册分支事务
记录undo-log(数据快照)
执行业务sql并提交
报告事务状态
阶段一RM的工作
删除undo-log即可
阶段二提交时RM的工作
根据undo-log恢复数据到更新前
阶段三提交时RM的工作
一个分支业务的SQL是这样的:update tb_account set money = money - 10 where id = 1
XA模式一阶段不提交事务,锁定资源;AT模式一阶段直接提交,不锁定资源。XA模式依赖数据库机制实现回滚;AT模式利用数据快照实现数据回滚。XA模式强一致;AT模式最终一致
AT模式与XA模式最大的区别是什么?
AT模式的脏写问题
一阶段完成直接提交事务,释放数据库资源,性能比较好利用全局锁实现读写隔离没有代码侵入,框架自动完成回滚和提交
AT模式的优点
两阶段之间属于软状态,属于最终一致框架的快照功能会影响性能,但比XA模式要好很多
AT模式的缺点
将事务模式改为AT
实现AT
AT模式
Try:资源的检测和预留
Confirm:完成资源操作业务;要求 Try 成功 Confirm 一定要能成功
Cancel:预留资源释放,可以理解为try的反向操作
TCC模式与AT模式非常相似,每阶段都是独立事务,不同的是TCC通过人工编码来实现数据恢复。需要实现三个方法
TCC模式原理
TCC模式工作图
Try:资源检查和预留
Confirm:业务执行和提交
Cancel:预留资源的释放
TCC模式的每个阶段是做什么的?
一阶段完成直接提交事务,释放数据库资源,性能好
相比AT模型,无需生成快照,无需使用全局锁,性能最强
不依赖数据库事务,而是依赖补偿操作,可以用于非事务型数据库
TCC的优点是什么?
有代码侵入,需要人为编写try、Confirm和Cancel接口,太麻烦
软状态,事务是最终一致
需要考虑Confirm和Cancel的失败情况,做好幂等处理
TCC的缺点是什么?
TCC模式
一阶段:直接提交本地事务
二阶段:成功则什么都不做;失败则通过编写补偿业务来回滚
Saga模式是SEATA提供的长事务解决方案。也分为两个阶段
事务参与者可以基于事件驱动实现异步调用,吞吐高一阶段直接提交事务,无锁,性能好不用编写TCC中的三个阶段,实现简单
Saga模式优点
软状态持续时间不确定,时效性差没有锁,没有事务隔离,会有脏写
SAGA模式
动手实践
TC服务作为Seata的核心服务,一定要保证高可用和异地容灾
TC的异地多机房容灾架构
高可用集群结构
实现高可用集群
高可用
分布式事务
请求要经过Tomcat处理,Tomcat的性能成为整个系统的瓶颈
Redis缓存失效时,会对数据库产生冲击
统的缓存策略一般是请求到达Tomcat后,先查询Redis,如果未命中则查询数据库,存在下面的问题
传统缓存的问题
缓存图描述
缓存在日常开发中启动至关重要的作用,由于是存储在内存中,数据的读取速度是非常快的,能大量减少对数据库的访问,减少数据库的压力。我们把缓存分为两类
优点:存储容量更大、可靠性更好、可以在集群间共享
缺点:访问缓存有网络开销
场景:缓存数据量较大、可靠性要求较高、需要在集群间共享
分布式缓存,例如Redis
优点:读取本地内存,没有网络开销,速度更快
缺点:存储容量有限、可靠性较低、无法共享
场景:性能要求较高,缓存数据量较小
进程本地缓存,例如HashMap、GuavaCache
本地进程缓存
Caffeine是一个基于Java8开发的,提供了近乎最佳命中率的高性能的本地缓存库。目前Spring内部的缓存使用的就是Caffeine
实现进程缓存
基于容量:设置缓存的数量上限
基于时间:设置缓存的有效时间
基于引用:设置缓存为软引用或弱引用,利用GC来回收缓存数据。性能较差,不建议使用。
在默认情况下,当一个缓存元素过期的时候,Caffeine不会自动立即将其清理和驱逐。而是在一次读或写操作后,或者在空闲时间完成对失效数据的驱逐。
Caffeine提供了三种缓存驱逐策略
初识Caffeine
JVM进程缓存
Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
在Linux虚拟机的任意目录下,新建一个hello.lua文件
添加下面的内容
运行
HelloWorld
初识Lua
数据类型
Lua声明变量的时候,并不需要指定数据类型
-- 访问数组,lua数组的角标从1开始print(arr[1])-- 访问tableprint(map['name'])print(map.name)
访问table
变量
遍历数组
遍历table
数组、table都可以利用for循环来遍历
循环
变量和循环
定义函数的语法
函数
条件控制
条件控制、函数
Lua语法入门
OpenResty® 是一个基于 Nginx的高性能 Web 平台,用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关
具备Nginx的完整功能基于Lua语言进行扩展,集成了大量精良的 Lua 库、第三方模块允许使用Lua自定义业务逻辑、自定义库
安装OpenResty
步骤一:修改nginx.conf文件
步骤二:编写item.lua文件
OpenResty快速入门,实现商品详情页数据查询
OpenResty快速入门
OpenResty获取请求参数
获取请求路径中的商品id信息,拼接到json结果中返回
请求参数处理
获取请求路径中的商品id信息,根据id向Tomcat查询商品信息
nginx内部发送Http请求
封装http查询的函数
JSON结果处理
查询Tomcat
冷启动:服务刚刚启动时,Redis中并没有缓存,如果所有商品数据都在第一次查询时添加缓存,可能会给数据库带来较大压力。
缓存预热:在实际开发中,我们可以利用大数据统计用户访问的热点数据,在项目启动时将这些热点数据提前查询并保存到Redis中。
docker run --name redis -p 6379:6379 -d redis redis-server --appendonly yes
1.利用Docker安装Redis
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
2.在item-service服务中引入Redis依赖
spring: redis: host: 192.168.150.101
3.配置Redis地址
@Component public class RedisHandler implements InitializingBean { @Autowired private StringRedisTemplate redisTemplate; @Override public void afterPropertiesSet() throws Exception { // 初始化缓存 ... } }
编写初始化类
Redis缓存预热
OpenResty提供了操作Redis的模块,我们只要引入该模块就能直接使用
-- 引入redis模块local redis = require(\"resty.redis\
引入Redis模块,并初始化Redis对象
封装函数,用来释放Redis连接,其实是放入连接池
OpenResty的Redis模块
查询Redis缓存
OpenResty为Nginx提供了shard dict的功能,可以在nginx的多个worker之间共享数据,实现缓存功能
# 共享字典,也就是本地缓存,名称叫做:item_cache,大小150m lua_shared_dict item_cache 150m;
开启共享字典,在nginx.conf的http下添加配置:
操作共享字典
在查询商品时,优先查询OpenResty的本地缓存
Nginx本地缓存
多级缓存
优势:简单、方便
缺点:时效性差,缓存过期之前可能不一致
场景:更新频率较低,时效性要求低的业务
设置有效期:给缓存设置有效期,到期后自动删除。再次查询时更新
优势:时效性强,缓存与数据库强一致
缺点:有代码侵入,耦合度高
场景:对一致性、时效性要求较高的缓存数据
同步双写:在修改数据库的同时,直接修改缓存
优势:低耦合,可以同时通知多个缓存服务
缺点:时效性一般,可能存在中间不一致状态
场景:时效性要求一般,有多个服务需要同步
异步通知:修改数据库时发送事件通知,相关服务监听到通知后修改缓存数据
缓存同步策略
数据同步策略
基于Java开发。基于数据库增量日志解析,提供增量数据订阅&消费
MySQL master 将数据变更写入二进制日志( binary log),其中记录的数据叫做binary log events
MySQL slave 将 master 的 binary log events拷贝到它的中继日志(relay log)
MySQL slave 重放 relay log 中事件,将数据变更反映它自己的数据
Canal是基于mysql的主从同步来实现的,MySQL主从同步的原理如下
Canal就是把自己伪装成MySQL的一个slave节点,从而监听master的binary log变化。再把得到的变化信息通知给Canal的客户端,进而完成对其它数据库的同步。
安装Canal
Canal提供了各种语言的客户端,当Canal监听到binlog变化时,会通知Canal的客户端。
编写监听器,监听Canal消息
Canal推送给canal-client的是被修改的这一行数据(row),而我们引入的canal-client则会帮我们把行数据封装到Item实体类中。这个过程中需要知道数据库与实体的映射关系,要用到JPA的几个注解
Canal客户端
监听Canal
微服务(SpringCloud)
Hyper Text Markup Language(超文本标记语言)
多个浏览器使用
市场需求大
跨平台
HTML的优势
W3CWorld Wide Web Consortium(万维网联盟)成立于1994年,Web技术领域最权威和具影响力的国际中立性技术标准机构http://www.w3.org/http://www.chinaw3c.org/
W3C标准包括结构化标准语言(XHTML 、XML)表现标准语言(CSS)行为标准(DOM、ECMAScript )
<!DOCTYPE html><html lang=\"en\"><head> <meta charset=\"UTF-8\"> <title>静夜思</title></head><body><h1>静夜思</h1><em>朝代:唐代</em> 作者:<strong>李白</strong></em><br/><hr/>原文:<p> 床前明月光,<br/> 疑是地上霜。<br/> 举头望明月,<br/> 低头思故乡。<br/></p></body></html>
演示案例:静夜思
W3C
记事本NotePad++SublimeVsCodeWebStormHBuidlerIDEA
常用编辑器
1. 强调HTML标签都以“< >”开始、“</ >”结束2. 说明网页基本结构中这几个标签的用法3. 网页中所有的内容都放在之间
<html><head></head><body></body></html>
<!DOCTYPE html><html lang=\"en\"><head> <meta charset=\"UTF-8\"> <title>小樊Java之路</title> <meta charset=\"UTF-8\"> <meta name=\"keywords\" content=\
<!DOCTYPE> 声明位于文档中的最前面的位置,处于 <html> 标签之前。<!DOCTYPE> 声明不是一个 HTML 标签;它是用来告知 Web 浏览器页面使用了哪种 HTML 版本。在 HTML 4.01 中,<!DOCTYPE> 声明需引用 DTD (文档类型声明),因为 HTML 4.01 是基于 SGML (Standard Generalized Markup Language 标准通用标记语言)。DTD 指定了标记语言的规则,确保了浏览器能够正确的渲染内容。
HTML的基本结构
<h>
段落标签<p>
换行标签<br/>
水平线标签<hr/>
字体样式标签加粗:<strong></strong>斜体:<em></em>
网页的基本标签
<!DOCTYPE html><html lang=\"en\"><head> <meta charset=\"UTF-8\"> <title>特殊符号</title></head><body><!--空格--><p> 狂神说 Java<br/> 狂神说 Java<br/> 狂神说 Java<br/></p><!--大于小于--><p> > < <br> > <</p><!--引号--><p> "狂神"</p><!--版权--><p> © 2017-2019 狂神说Java</p><!--万能的公式 &符号+xxx --></body></html>
注释和特殊符号
图像标签
target:常用有_self _blank前者为本页刷新,后者为跳出本页
锚链接
链接标签
行内元素和块儿元素
HTML基础
<!--ul 声明无序列表--><ul> <!--li 声明列表项--> <li>语文</li> <li>数学</li> <li>英语</li> <li>计算机</li></ul>
列表项中可以包含图片、文本,还可以嵌套列表、其他标签等无序列表的特性没有顺序,每个< li>标签独占一行(块元素)默认< li>标签项前面有个实心小圆点一般用于无序类型的列表,如导航、侧边栏新闻、有规律的图文组合模块等
无序元素
<!--ol 声明有序列表--><ol> <li>语文</li> <li>数学</li> <li>英语</li> <li>计算机</li></ol>
有序列表默认以数字序号显示有序列表与无序列表一样,也可以嵌套列表、可以包含图片、文本、其他标签等有序列表的特性有顺序,每个< li>标签独占一行(块元素)默认< li>标签项前面有顺序标记一般用于排序类型的列表,如试卷、问卷选项等
有序列表
<!--dl 声明定义列表--><dl> <!--dt 声明列表项--> <dt>水果</dt> <!--dd 定义列表项内容--> <dd>苹果</dd> <dd>桃子</dd> <dd>李子</dd></dl>
定义列表也可以嵌套列表、包含图片、文本、其他标签等以后的网页制作中经常会用到定义列表,特别是图文混排的情况定义列表的特性没有顺序,每个< dt>标签、< dd>标签独占一行(块元素)默认没有标记一般用于一个标题下有一个或多个列表项的情况
自定义列表
列表
基本语法:<!--table表格标签--><table border=\"1px\"> <!--tr 行标签--> <tr><!--td 单元格标签--> <td>第1个单元格的内容</td> <td>第2个单元格的内容</td> …… </tr> <tr> <td>第1个单元格的内容</td> <td>第2个单元格的内容</td> …… </tr></table>
<table> <tr><!--colspan 所跨的列数--> <td colspan=\"n\">单元格内容</td> </tr> <tr> <td>单元格内容</td> …… </tr> ......</table>
表格的跨列
<table > <tr> <!--rowspan 所跨的行数--> <td rowspan=\"n\"> </td> <td> </td> </tr> <tr> <td> </td> </tr></table>
表格的跨行
<tr> <!--跨列--> <td colspan=\"3\">学生成绩</td></tr><tr> <!--跨行--> <td rowspan=\"2\">张三</td> <td>语文</td> <td>98</td></tr>
表格的跨行和跨列
表格
src:指定要播放的视频文件的路径controls:提供播放、暂停和音量的控件autoplay:自动播放属性loop:视频的循环播放<video src=\"视频路径\" controls autoplay></video>
视频
src:指定要播放的音频文件的路径trols:提供播放、暂停和音量的控件<audio src=\"音频路径\" controls autoplay></video>
音频
音频视频
src:引用页面地址name:框架标识名<iframe src=\"path\" name=\"mainFrame\" ></iframe>
iframe单页面连接
在被打开的框架上加name属性<iframe name=\"mainFrame\"></iframe>在超链接上设置target目标窗口属性为希望显示的框架窗口名<a href=\"https://www.baidu.com/\" target=\"mainFrame\">加载</a>
页面间相互跳转
内联框架
列表表格和媒体元素
method: 规定如何发送表单数据常用值:get post 在实际网页开发中通常采用post方式提交表单数据action: 表示向何处发送表单数据<form method=\"post\" action=\"result.html\"> <p>名字:<input name=\"name\" type=\"text\" > </p> <p>密码:<input name=\"pass\" type=\"password\" > </p> <p> <input type=\"submit\" name=\"Button\" value=\"提交\"/> <input type=\"reset\" name=\"Reset\" value=\"重填\"/> </p></form>
讲解表单的创建方法,以及method和action的作用分别把method的值设置为get和post,然后提交表单,查看页面效果;通过演示可看到method设置不同值时,表单数据在地址栏显示的不同情况最后根据演示情况说明get和post两者的区别最后总结:post方式提交的数据安全性要明显高于get方式提交的数据。因此在实际开发中通常采用post方式提交表单数据。
表单样式
<!--type=\"text\"name:文本框名称(必填)value:文本框初始值size:文本框长度maxlength:文本框可输入最多字符--><input type=\"text\" name=\"userName\" value=\"用户名\" size=\"30\" maxlength=\"20\"/>
文本框
<!--type=\"password\"name:密码框名称(必填)size:密码框长度--><input type=\"password\" name=\"pass\" size=\"20\"/>
密码框
<!--type=\"radio\"name:单选框名称(必填),一组的名称需要相同checked:单选按钮选中状态value:单选框的值--><input name=\"gen\" type=\"radio\" value=\"男\" checked />男<input name=\"gen\" type=\"radio\" value=\"女\" />女
单选框
<!--type=\"checkbox\"name:复选框名称(必填),一组的名称需要相同checked:复选按钮选中状态value:复选框的值--><input type=\"checkbox\" name=\"interest\" value=\"sports\"/>运动<input type=\"checkbox\" name=\"interest\" value=\"talk\" checked />聊天<input type=\"checkbox\" name=\"interest\" value=\"play\"/>玩游戏
复选框
<!--select:下拉列表框--><!--option:选项--><select name=\"列表名称\" size=\"行数\"> <option value=\"选项的值\" selected=\"selected\">…</option > <option value=\"选项的值\">…</option ></select>
下拉列表框
<!--重置按钮--><input type=\"reset\" name=\"butReset\" value=\"reset按钮\"><!--提交按钮--><input type=\"submit\" name=\"butSubmit\" value=\"submit按钮\"><!--普通按钮--><input type=\"button\" name=\"butButton\" value=\"button按钮\"/><!--图片按钮--><input type=\"image\" src=\"images/login.gif\" />
按钮
textarea:多行文本域cols:显示的列数rows:显示的行数<textarea name=\"showText\" cols=\"x\" rows=\"y\">文本内容 </textarea>
多行文本框
enctype:表单编码属性<form action=\"\" method=\"post\" enctype=\"multipart/form-data\"> <p> <!--type=\"file\" 文件域--> <input type=\"file\" name=\"files\" /> <input type=\"submit\" name=\"upload\" value=\"上传\" /> </p></form>
文件域
邮箱:<input type=\"email\" name=\"email\"/>
邮箱
请输入你的网址:<input type=\"url\" name=\"userUrl\"/>
网址
min:最小值max:最大值step:步长请输入数字:<input type=\"number\" name=\"num\" min=\"0\" max=\"100\" step=\"10\"/>
数字
type值为range即为滑块。请输入数字:<input type=\"range\" name=\"range1\" min=\"0\" max=\"10\" step=\"2\"/>
滑块
请输入搜索的关键词:<input type=\"search\" name=\"sousuo\"/>
搜索框
表单元素
在某些注册页面或本图片中订单信息页面,必须同意一些条款按钮才能使用等等
隐藏域在浏览器中看不到隐藏域,但是在提交表单时可以看到隐藏域的内容被提交至服务器<input type=\"hidden\" value=\"666\" name=\"userid\">
只读、禁用讲解只读和禁用的语法,强调不能单写readonly或disabled,必须写readonly=”readonly”和disabled=“disabled”,介绍只读和禁用的使用场合<input name=\"name\" type=\"text\" value=\"张三\" readonly><input type=\"submit\" disabled value=\"保存\" >
<!--它的for属性对应的id与表单元素id一致--><label for=\"id\">标注的文本</label><input type=\"radio\" name=\"gender\" id=\"male\"/>
表单元素的注用
表单高级应用
placeholder提示语默认显示,当文本框中输入内容时提示语消失required规定文本框填写内容不能为空,否则不允许用户提交表单pattern用户输入的内容必须符合正则表达式所指的规则,否则就不能提交表单
表单的初级认证
表单
Html
Cascading Style Sheet 级联样式表。表现HTML或XHTML文件样式的计算机语言。包括对字体、颜色、边距、高度、宽度、背景图片、网页定位等设定。
<h1 style=\"color:red;\">style属性的应用</h1><p style=\"font-size:14px; color:green;\">直接在HTML标签中设置的样式</p>
行内引入
<style> h1{color: green; }</style>
内部样式表
外部样式表
引入css方式
HTML标签作为标签选择器的名称<h1>…<h6>、<p>、<img/>
类选择器和id选择器
css选择器
body p{ background: red; }
后代选择器
body>p{ background: pink; }
子选择器
.active+p { background: green;}
相邻兄弟选择器
.active~p{ background: yellow; }
通用兄弟选择器
ul li:first-child{ background: red;}ul li:last-child{ background: green;}p:nth-child(1){ background: yellow;}p:nth-of-type(2){ background: blue;}
<html><head lang=\"en\"> <meta charset=\"UTF-8\"> <title>使用CSS3结构伪类选择器</title></head><body> <p>p1</p> <p>p2</p> <p>p3</p> <ul> <li>li1</li> <li>li2</li> <li>li3</li> </ul></body></html>
结构伪类选择器
css高级选择器
E[attr]a[id] {background: yellow;}
属性选择器
CSS概述
< span>标签 的作用:能让某几个文字或者某个词语凸显出来,从而添加对应的样式!
span标签
字体类型
单位px(像素)em、rem、cm、mm、pt、pc
h1{font-size:24px;}h2{font-size:16px;}h3{font-size:2em;}span{font-size:12pt;}strong{font-size:13pc;}
字体大小
normal、italic和oblique
字体风格 font-style
字体属性的顺序:字体风格→字体粗细→字体大小→字体类型
p span{ font:oblique bold 12px \"楷体\";}
字体属性 font
字体样式
RGB
RGBA在RGB基础上增加了控制alpha透明度的参数,其中这个透明通道值为0~1
文本颜色
水平对齐方式:text-align属性
首行缩进:text-indent:em或px行高:line-height:px
排版文本段落
文本装饰:text-decoration属性(后面的讲解中会大量用到)
垂直对齐方式:vertical-align属性:middle、top、bottom
文本修饰和垂直对齐
文本阴影
文体样式
超链接伪类
list-style-typelist-style-imagelist-style-positionlist-style
列表样式
常见的背景样式:背景图像background-image背景颜色background-color
.title {font-size:18px;font-weight:bold;color:#FFF;text-indent:1em;line-height:35px;background:#C00 url(../image/arrow-down.gif) 205px 10px no-repeat;}background: 背景颜色 背景图像 背景定位 背景不重复显示
设置背景
http://color.oulu.me/
参考网站
背景尺寸
背景样式
美化网页元素
边框颜色 border-color边框颜色设置方式与文本颜色对比讲解,都是使用十六进制强调同时设置4个边框颜色时,顺序为上右下左详细讲解分别上、下、左、右各边框颜色的不同设置方式,及属性值的顺序
border-top-width:5px;border-right-width:10px;border-bottom-width:8px;border-left-width:22px;border-width:5px ;border-width:20px 2px;border-width:5px 1px 6px;border-width:1px 3px 5px 2px;
像素值
border-widththinmediumthick
边框粗细
border-top-style:solid;border-right-style:solid;border-bottom-style:solid;border-left-style:solid;border-style:solid ;border-style:solid dotted;border-style:solid dotted dashed;border-style:solid dotted dashed double;
border-stylenonehiddendotteddashedsoliddouble
边框样式
同时设置边框的颜色、粗细和样式border:1px solid #3a6587;border: 1px dashed red;
border简写
边框
<!DOCTYPE html><html><head lang=\"en\"> <meta charset=\"UTF-8\"> <title>box-sizing</title> <style> div{ width: 100px; height: 100px; padding: 5px; margin: 10px; border: 1px solid #000000; box-sizing: border-box; /*box-sizing: content-box; /!* 默认值*!/*/ } </style></head><body> <div></div></body></html>
外边距 marginmargin-topmargin-rightmargin-bottommargin-left———————————margin-top: 1 pxmargin-right : 2 pxmargin-bottom : 2 pxmargin-left : 1 pxmargin :3px 5px 7px 4px;margin :3px 5px;margin :3px 5px 7px;margin :8px;
外边距
内外边距
border-radius: 20px 10px 50px 30px;
利用border-radius属性制作圆形的两个要点元素的宽度和高度必须相同圆角的半径为元素宽度的一半,或者直接设置圆角半径值为50%
<!DOCTYPE html><html><head lang=\"en\"> <meta charset=\"UTF-8\"> <title>border-radius制作圆形</title> <style> div{ width: 100px; height: 100px; border: 4px solid red; border-radius: 50%; } </style></head><body> <div></div></body></html>
div{ width: 100px; height: 100px; border: 4px solid red; border-radius: 50%;}
border-radius制作特殊图形:圆形
<!DOCTYPE html><html><head lang=\"en\"> <meta charset=\"UTF-8\"> <title>border-radius制作半圆形</title> <style> div{ background: red; margin: 30px; } div:nth-of-type(1){ width: 100px; height: 50px; border-radius: 50px 50px 0 0; } div:nth-of-type(2){ width: 100px; height: 50px; border-radius:0 0 50px 50px; } div:nth-of-type(3){ width: 50px; height: 100px; border-radius:0 50px 50px 0; } div:nth-of-type(4){ width: 50px; height: 100px; border-radius: 50px 0 0 50px; } </style></head><body> <div></div> <div></div> <div></div> <div></div></body></html>
利用border-radius属性制作半圆形的两个要点制作上半圆或下半圆时,元素的宽度是高度的2倍,而且圆角半径为元素的高度值制作左半圆或右半圆时,元素的高度是宽度的2倍,而且圆角半径为元素的宽度值
使用border-radius制作特殊图形:半圆形
<!DOCTYPE html><html><head lang=\"en\"> <meta charset=\"UTF-8\"> <title>border-radius制作扇形</title> <style> div{ background: red; margin: 30px; } div:nth-of-type(1){ width: 50px; height: 50px; border-radius: 50px 0 0 0; }div:nth-of-type(2){ width: 50px; height: 50px; border-radius:0 50px 0 0; } div:nth-of-type(3){ width: 50px; height: 50px; border-radius:0 0 50px 0; } div:nth-of-type(4){ width: 50px; height: 50px; border-radius: 0 0 0 50px; } </style></head><body> <div></div> <div></div> <div></div> <div></div></body></html>
使用border-radius制作特殊图形:扇形
圆角边框
<!DOCTYPE html><html><head lang=\"en\"> <meta charset=\"UTF-8\"> <title>box-shadow的使用</title> <style> div{ width: 100px; height: 100px; border: 1px solid red; border-radius: 8px; margin: 20px; /*box-shadow: 20px 10px 10px #06c; /!*内阴影*!/*//*box-shadow: 0px 0px 20px #06c; /!*只设置模糊半径的阴影*!/*/ box-shadow: inset 3px 3px 10px #06c; /*内阴影*/ } </style></head><body><div></div></body></html>
盒子阴影
盒子模型
标准文档流:指元素根据块元素或行内元素的特性按从上到下,从左到右的方式自然排列。这也是元素默认的排列方式
<h1>…<h6>、<p>、<div>、列表
块级元素
<span>、<a>、<img/>、<strong>...
内联元素
组成
标准文档流
块级元素与行级元素的转变(block、inline)控制块元素排到一行(inline-block)控制元素的显示和隐藏(none)
特性
可以使用什么属性使块元素排在一行?inline-blockfloat
块元素排在一起的方法
display
<body> <div id=\"father\"> <div class=\"layer01\"><img src=\"image/photo-1.jpg\" alt=\"日用品\" /></div> <div class=\"layer02\"><img src=\"image/photo-2.jpg\" alt=\"图书\" /></div> <div class=\"layer03\"><img src=\"image/photo-3.jpg\" alt=\"鞋子\" /></div> <div class=\"layer04\">浮动的盒子……</div> </div></body>
float属性
浮动
.layer04 { clear:both; #清除两侧浮动}.layer04 {clear:left; #清除左侧浮动}.layer04 {clear:right; #清除右侧浮动}
clear属性
<div id=\"father\"> <div class=\"layer01\"><img src=\"image/photo-1.jpg\" alt=\"日用品\" /></div> <div class=\"layer02\"><img src=\"image/photo-2.jpg\" alt=\"图书\" /></div> <div class=\"layer03\"><img src=\"image/photo-3.jpg\" alt=\"鞋子\" /></div> <div class=\"layer04\">浮动的盒子……</div> <div class=\"clear\"></div></div>.clear{ clear: both; margin: 0; padding: 0;}
浮动元素后边添加div
<div id=\"father\"> <div class=\"layer01\"><img src=\"image/photo-1.jpg\" alt=\"日用品\" /></div> <div class=\"layer02\"><img src=\"image/photo-2.jpg\" alt=\"图书\" /></div> <div class=\"layer03\"><img src=\"image/photo-3.jpg\" alt=\"鞋子\" /></div> <div class=\"layer04\">浮动的盒子……</div></div>#father {height: 400px; border:1px #000 solid; }
设置父元素的高度
父级添加overflow属性
<div id=\"father\"> <div class=\"layer01\"><img src=\"image/photo-1.jpg\" alt=\"日用品\" /></div> <div class=\"layer02\"><img src=\"image/photo-2.jpg\" alt=\"图书\" /></div> <div class=\"layer03\"><img src=\"image/photo-3.jpg\" alt=\"鞋子\" /></div> <div class=\"layer04\">浮动的盒子……</div></div>#father {overflow: hidden;border:1px #000 solid; }
hidden属性值,这个值在网页中经常使用通常与< div>宽度结合使用设置< div>自动扩展高度,或者隐藏超出的内容
<div id=\"father\" class=\"clear\"> <div class=\"layer01\"><img src=\"image/photo-1.jpg\" alt=\"日用品\" /></div> <div class=\"layer02\"><img src=\"image/photo-2.jpg\" alt=\"图书\" /></div> <div class=\"layer03\"><img src=\"image/photo-3.jpg\" alt=\"鞋子\" /></div> <div class=\"layer04\">浮动的盒子……</div></div>.clear:after{ content: ''; /*在clear类后面添加内容为空*/ display: block; /*把添加的内容转化为块元素*/ clear: both; /*清除这个元素两边的浮动*/}
父级添加伪类after
解决父级边框塌陷的方法
display:inline-block可以让元素排在一行,并且支持宽度和高度,代码实现起来方便位置方向不可控制,会解析空格IE 6、IE 7上不支持float可以让元素排在一行并且支持宽度和高度,可以决定排列方向float 浮动以后元素脱离文档流,会对周围元素产生影响,必须在它的父级上添加清除浮动的样式
inline-block和float区别
边框塌陷
position属性static:默认值,没有定位
设置俩个盒子的相对定位#first {background-color:#FC9;border:1px #B55A00 dashed;position:relative;right:20px;bottom:20px;}#second {background-color:#CCF;border:1px #0000A8 dashed;float:right;position:relative;left:20px;top:-20px;}
relative:相对定位相对自身原来位置进行偏移,偏移设置:top、left、right、bottom
absolute属性值:偏移设置: left、right、top、bottom
绝对定位:使用了绝对定位的元素以它最近的一个“已经定位”的“祖先元素” 为基准进行偏移如果没有已经定位的祖先元素,会以浏览器窗口为基准进行定位
绝对定位
fixed属性值偏移设置: left、right、top、bottom
固定定位
调整元素定位时重叠层的上下位置z-index属性值:整数,默认值为0设置了positon属性时,z-index属性可以设置各元素之间的重叠高低关系z-index值大的层位于其值小的层上方
z-index属性
定位
变形函数translate():平移函数,基于X、Y坐标重新定位元素的位置scale():缩放函数,可以使任意元素对象尺寸发生变化rotate():旋转函数,取值是一个度数值skew():倾斜函数,取值是一个度数值
CSS变形
CSS过渡
CSS动画
制作网页动画
调试:在console中进行
js基础
123; // 整数1230.456; // 浮点数0.4561.2345e3; // 科学计数法表示1.2345x1000,等同于1234.5-99; // 负数NaN; // NaN表示Not a Number,当无法计算结果时用NaN表示Infinity; // Infinity表示无限大,当数值超过了JavaScript的Number所能表示的最大值时,就表示为Infinity
Number
\"a\
字符串
布尔值,与或非
NaN === NaN; // false 这个特殊值与任何值都不相等
isNaN(NaN); // true
判断方法
(1)== 比较,它会自动转换数据类型再比较,很多时候,会得到非常诡异的结果;(2)=== 比较,它不会自动转换数据类型,如果数据类型不一致,返回 false ,如果一致,再比较。
null 表示一个“空”的值,它和 0 以及空字符串 '' 不同, 0 是一个数值, '' 表示长度为0的字符串,而 null 表示“空”。
null和undefined
数组
对象
var a; // 申明了变量a,此时a的值为undefinedvar $b = 1; // 申明了变量$b,同时给$b赋值,此时$b的值为1var s_007 = '007'; // s_007是一个字符串var Answer = true; // Answer是一个布尔值truevar t = null; // t的值是null
数据类型和变量
不用 var 申明的变量会被视为全局变量,为了避免这一缺陷,所有的JavaScript代码都应该使用strict模式。我们在后面编写的JavaScript代码将全部采用strict模式。i = 10; // i现在是全局变量
strict模式
转义字符 \\ 可以转义很多字符,比如 \ 表示换行, \\t 表示制表符,字符 \\ 本身也要转义,所以 \\\\ 表示的字符就是 \\ 。
JavaScript的字符串就是用 '' 或 \"\" 括起来的字符表示
'\\x41'; // 完全等同于 'A'
由于多行字符串用 \ 写起来比较费事,所以最新的ES6标准新增了一种多行字符串的表示方法,用反引号 ``表示
多行字符串
var s = 'Hello';s.toUpperCase(); // 返回'HELLO'
toUpperCase() 把一个字符串全部变为大写
toLowerCase() 把一个字符串全部变为小写
indexOf() 会搜索指定字符串出现的位置
substring() 返回指定索引区间的子串
indexOf:查索引位置
slice() 就是对应String的 substring() 版本,它截取 Array 的部分元素,然后返回一个新的 Array
push() 向 Array 的末尾添加若干元素, pop() 则把 Array 的最后一个元素删除掉
push和pop
如果要往 Array 的头部添加若干元素,使用 unshift() 方法, shift() 方法则把Array 的第一个元素删掉
unshift和shift
sort() 可以对当前 Array 进行排序,它会直接修改当前 Array 的元素位置,直接调用时,按照默认顺序排序
sort
reverse
splice
concat
join
常用方法
对象格式
var age = 3;if (age >= 18) { alert('adult');} else if (age >= 6) { alert('teenager');} else { alert('kid');}
if
var x = 0;var i;for (i=1; i<=10000; i++) { x = x + i;}x; // 50005000
var x = 0;for (;;) { // 将无限循环下去 if (x > 100) { break; // 通过if判断来退出循环 } x ++;}
无限循环
for
var x = 0;var n = 99;while (n > 0) { x = x + n; n = n - 2;}x; // 2500
基本操作
var n = 0;do { n = n + 1;} while (n < 100);n; // 100
do while
流程控制
Map键值对集合
通过 delete(key) 方法可以删除元素
通过 add(key) 方法可以添加元素到 Set 中,可以重复添加,但是没有效果
Set
Map和Set
Array,Map,Set 属于;具有 iterable 类型的集合可以通过新的 for ... of 循环来遍历
Iterable
function abs(x) { if (x >= 0) { return x; } else { return -x; }}
var abs = function (x) { if (x >= 0) { return x; } else { return -x; }};
定义
function abs() { if (arguments.length === 0) { return 0; } var x = arguments[0]; return x >= 0 ? x : -x;}abs(); // 0abs(10); // 10abs(-9); // 9
arguments
为了获取除了已定义参数 a 、 b 之外的参数,我们不得不用 arguments ,并且循环要从索引2 开始以便排除前两个参数,这种写法很别扭,只是为了获得额外的 rest 参数,有没有更好的方法?
rest
函数的定义和调用
变量作用域
方法
要指定函数的 this 指向哪个对象,可以用函数本身的 apply 方法,它接收两个参数,第一个参数就是需要绑定的 this 变量,第二个参数是 Array ,表示函数本身的参数。
apply
typeof 123; // 'number'typeof NaN; // 'number'typeof 'str'; // 'string'typeof true; // 'boolean'typeof undefined; // 'undefined'typeof Math.abs; // 'function'typeof null; // 'object'typeof []; // 'object'typeof {}; // 'object'
date
{\"name\": \"QinJiang\"}{\"age\": \"3\"}{\"sex\": \"男\"}
<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"UTF-8\"><title>JSON_秦疆</title></head><body><script type=\"text/javascript\">//编写一个js的对象var user = {name:\"秦疆\
JSON和JS之间的相互转换
标准对象
面向对象编程
window 对象不但充当全局作用域,而且表示浏览器窗口
window 对象有 innerWidth 和 innerHeight 属性,可以获取浏览器窗口的内部宽度和高度。内部宽高是指除去菜单栏、工具栏、边框等占位元素后,用于显示网页的净宽高
window
navigator 对象表示浏览器的信息,最常用的属性包括:navigator.appName:浏览器名称;navigator.appVersion:浏览器版本;navigator.language:浏览器设置的语言;navigator.platform:操作系统类型;navigator.userAgent:浏览器设定的 User-Agent 字符串。
'use strict';console.log('appName = ' + navigator.appName);console.log('appVersion = ' + navigator.appVersion);console.log('language = ' + navigator.language);console.log('platform = ' + navigator.platform);console.log('userAgent = ' + navigator.userAgent);
var width = window.innerWidth || document.body.clientWidth;
var width;if (getIEVersion(navigator.userAgent) < 9) { width = document.body.clientWidth;} else { width = window.innerWidth;}
navigator
console.log('Screen size = ' + screen.width + ' x ' + screen.height);
screen 对象表示屏幕的信息,常用的属性有:screen.width:屏幕宽度,以像素为单位;screen.height:屏幕高度,以像素为单位;screen.colorDepth:返回颜色位数,如8、16、24。
screen
要加载一个新页面,可以调用 location.assign() 。如果要重新加载当前页面,调用location.reload() 方法非常方便。
location.reload();location.assign('https://blog.kuangstudy.com/'); // 设置一个新的URL地址
location.protocol; // 'http'location.host; // 'www.example.com'location.port; // '8080'location.pathname; // '/path/index.html'location.search; // '?a=1&b=2'location.hash; // 'TOP'
location
font color=\"#d32f2f\
<dl id=\"code-menu\" style=\"border:solid 1px #ccc;padding:6px;\"> <dt>Java</dt> <dd>Spring</dd> <dt>Python</dt> <dd>Django</dd> <dt>Linux</dt> <dd>Docker</dd></dl>
document
JavaScript可以调用 history 对象的 back() 或forward () ,相当于用户点击了浏览器的“后退”或“前进”按钮。
history(尽量不使用)
操作Bom
document.getElementById() 和 document.getElementsByTagName() ,以及CSS选择器document.getElementsByClassName() 。
querySelector() 和 querySelectorAll()
选取dom节点
用 innerHTML 时要注意,是否需要写入HTML。如果写入的字符串是通过网络拿到了,要注意对字符编码来避免XSS攻击。
// 获取<p id=\"p-id\">...</p>var p = document.getElementById('p-id');// 设置文本为abc:p.innerHTML = 'ABC'; // <p id=\"p-id\">ABC</p>// 设置HTML:p.innerHTML = 'ABC <span style=\"color:red\">RED</span> XYZ';// <p>...</p>的内部结构已修改
一种是修改 innerHTML 属性
// 获取<p id=\"p-id\">...</p>var p = document.getElementById('p-id');// 设置文本:p.innerText = '<script>alert(\"Hi\")</script>';// HTML被自动编码,无法设置一个<script>节点:// <p id=\"p-id\"><script>alert(\"Hi\")</script></p>
第二种是修改 innerText 属性
更新DOM
<!-- HTML结构 --><div id=\"list\"> <p id=\"java\">Java</p> <p id=\"python\">Python</p> <p id=\"scheme\">Scheme</p> <p id=\"js\">JavaScript</p></div>
结果
<!-- HTML结构 --><p id=\"js\">JavaScript</p><div id=\"list\"> <p id=\"java\">Java</p> <p id=\"python\">Python</p> <p id=\"scheme\">Scheme</p></div>
动态添加新节点
插入DOM
// 拿到待删除节点:var self = document.getElementById('to-be-removed');// 拿到父节点:var parent = self.parentElement;// 删除:var removed = parent.removeChild(self);removed === self; // true
删除DOM
操作Dom
// <input type=\"text\" id=\"email\">var input = document.getElementById('email');input.value; // '用户输入的值'// <label><input type=\"radio\" name=\"weekday\" id=\"monday\" value=\"1\">Monday</label>// <label><input type=\"radio\" name=\"weekday\" id=\"tuesday\" value=\"2\">Tuesday</label>var mon = document.getElementById('monday');var tue = document.getElementById('tuesday');mon.value; // '1'tue.value; // '2'mon.checked; // true或者falsetue.checked; // true或者false
获取值
// <input type=\"text\" id=\"email\">var input = document.getElementById('email');input.value = 'test@example.com'; // 文本框的内容已更新
设置值
<!-- HTML --><form id=\"test-form\"> <input type=\"text\" name=\"test\"> <button type=\"button\" onclick=\"doSubmitForm()\">Submit</button></form><script>function doSubmitForm() { var form = document.getElementById('test-form'); // 可以在此修改form的input... // 提交form: form.submit();}</script>
方式一是通过 <form> 元素的 submit() 方法提交一个表单
提交表单
操作表单
前端体系
基础语法
VUE
let 所声明的变量,只在 let 命令所在的代码块内有效
const 声明的变量是常量,不能被修改,类似于java中final关键字。
let 和 const 命令
includes() :返回布尔值,表示是否找到了参数字符串。
startsWith() :返回布尔值,表示参数字符串是否在原字符串的头部。
endsWith() :返回布尔值,表示参数字符串是否在原字符串的尾部。
字符串扩展
什么是解构? -- ES6中允许按照一定模式从数组和对象中提取值,然后对变量进行赋值,这被称为解构 (Destructuring)。
数组解构
解构表达式
函数参数默认值
一个参数
多个参数
没有参数
代码不止一行,可以用 {} 括起来。
箭头函数
对象的函数属性简写
箭头函数结合解构表达式
map() :接收一个函数,将原数组中的所有元素用这个函数处理后放入新数组返回。
举例:有一个字符串数组,我们希望转为int数组
reduce() 会从左到右依次把数组中的元素用reduce处理,并把处理的结果作为下次reduce的第一个参数。如果是第一次,会把前两个元素作为计算参数,或者把用户指定的初始值作为起始参数
reduce() :接收一个函数(必须)和一个初始值(可选),该函数接收两个参数:第一个参数是上一次reduce处理的结果第二个参数是数组中要处理的下一个元素
map和reduce
扩展运算符
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
Promise
set.add(1);// 添加 set.clear();// 清空 set.delete(2);// 删除指定元素 set.has(2); // 判断是否存在 set.forEach(function(){})//遍历元素 set.size; // 元素个数。是属性,不是方法。
set
map,本质是与Object类似的结构。不同在于,Object强制规定key只能是字符串。而Map结构的key可以是任意对象。
map
set和map
基本使用
class(类)的基本语法
Generator函数有两个特征: 一是 function命令与函数名 之间有一个星号: 二是 函数体内部使用 yield吾句定义不同的内部状态。
通过hello()返回的h对象,每调用一次next()方法返回一个对象,该对象包含了value值和done状态。直到遇到return关键字或者函数执行完毕,这个时候返回的状态为ture,表示已经执行结束了。
for...of循环
Generator函数
修饰器(Decorator)是一个函数, 用来修改类的行为
修饰器(Decorator)
Babel (babeljs.io)是一个广为使用的 ES6 转码器,可以将 ES6 代码转为 ES5 代码,从而 在浏览器或其他环境执行 。Google 公司的 Traceur 转码器 Cgithub.com/google/traceur-compiler), 也可 以将 ES6 代码转为ES5的代码。
转码器
类似于Java中的导出类
模块化
函数优化
ES6语法
前端学习
react是一个构建用户界面的JavaScript库用户界面:HTML界面React主要用来写HTML界面或构建web应用
什么是react
组件最重要,通过组合复用多个组件,可以实现完整的页面功能
声明式基于组件
react特点
react概述
npm i react react-dom
react包是核心,提供创建元素、组件等功能react-dom 包括dom相关功能等
react的安装
引入俩个文件<script src=\"./node_modules/react/umd/react.development.js\"></script><script src=\"./node_modules/react-dom/umd/react-dom.development.js\"></script>
2.创建React元素3.渲染React元素到页面中
<div id=\"root\
react的使用
react基本使用
脚手架开发是现代应用的必备
充分利用Webpack、Babel、ESLint等工具辅助项目开发。
零配置,无需手动配置过多的东西
关注业务而不是工具配置
脚手架意义
npx create-react-app my-app
使用脚手化初始化项目
在项目根目录执行npm start
启动项目
react的脚手架使用
认识React
//使用JSX语法,创建react元素:const title = <h1>Hello JSX</h1>
1.JSX 不是标准的ECMAScript 语法,它是ECMAScript 的语法扩展。2.需要使用babel 编译处理后,才能在浏览器环境中使用。3.create-react-app 脚手架中已经默认有该配置,无需手动配置。4.编译JSX 语法的包为:@babel/preset-react。
为什么脚手架中可以使用JSX 语法?
驼峰命名法 class->className for -> htmlFor tabindex -> tabIndex
没有子节点的react元素可以靠/>结束
可以使用小括号包括,避免js插入分号
注意点
JSX的基本使用
嵌入js表达式const name=\"张三\"const dv=(<div>my name is {name}</div>)
不能在括号中出现if for
JSX中使用JavaScript表达式
根据特定的场景,可以使用 if for 逻辑运算符, 或者三元运算符来实现
const loadData= () => {if (isLoading) {return <div>数据加载中,请稍后...</div>}return (<div>数据加载完成,此处显示加载后的数据</div>)}const dv = (<div>{loadData()}</div>)
JSX的条件渲染
注意:渲染列表时应该添加key 属性,key 属性的值要保证唯一原则:map() 遍历谁,就给谁添加key 属性注意:尽量避免使用索引号作为key
如果要渲染一组数据用map
{songs.map(item => <li key={item.id}>{item.name}</li>)}
JSX的列表渲染
行内样式
<h1 className=\"title\">JSX的样式处理</h1>
类名 className
JSX的样式处理
JSX
特点:可复用、独立、可组合
React组件介绍
函数名称必须以大写字母开头
函数组件必须有返回值,表示该组件的结构 如果返回值为 null,表示不渲染任何内容
function Hello() {return (<div>这是我的第一个函数组件!</div>) }
用函数名作为组件标签名
1. 使用函数创建组件
类组件:使用 ES6 的 class 创建的组件约定1:类名称也必须以大写字母开头约定2:类组件应该继承 React.Component 父类,从而可以使用父类中提供的方法或属性约定3:类组件必须提供 render() 方法约定4:render() 方法必须有返回值,表示该组件的结构
2. 使用类创建组件
选择一:将所有组件放在同一个JS文件中选择二:将每个组件放到单独的JS文件中组件作为一个独立的个体,一般都会放到一个单独的 JS 文件中
// Hello.jsimport React from 'react'class Hello extends React.Component {render() {return <div>Hello Class Component!</div>} }// 导出Hello组件export default Hello
1. 创建Hello.js2. 在 Hello.js 中导入React3. 创建组件(函数 或 类)4. 在 Hello.js 中导出该组件5. 在 index.js 中导入 Hello 组件6. 渲染组件
3 抽离为独立 JS 文件
React组件创建的俩种方式
class App extends React.Component {handleClick() {console.log('单击事件触发了')}render() {return (<button onClick={this.handleClick}></button>) } }
function App() {function handleClick() {console.log('单击事件触发了')}return (<button onClick={handleClick}>点我</button>)}
语法:on+事件名称={事件处理程序},onClick={() => {}}React 事件采用驼峰命名法onMouseEnter、onFocus
事件绑定
可以通过事件处理程序的参数获取到事件对象React 中的事件对象叫做:合成事件(对象)合成事件:兼容所有浏览器,无需担心跨浏览器兼容性问题
事件对象
React事件处理
函数组件又叫做无状态组件,类组件又叫做有状态组件状态(state)即数据函数组件没有自己的状态,只负责数据展示(静)类组件有自己的状态,负责更新 UI,让页面“动” 起来
有状态组件和无状态组件
class Hello extends React.Component {// 简化语法state= {count: 0}render() {return (<div>有状态组件</div>) } }
class Hello extends React.Component {constructor() {super()// 初始化statethis.state = {count: 0} }render() {return (<div>有状态组件</div>) } }
获取状态stateclass Hello extends React.Component {// 简化语法state= {count: 0}render() {return (<div>有状态组件,{this.state.count}</div>) } }
state 的值是对象,表示一个组件中可以有多个数据
1. state的基本使用
setState() 作用:1. 修改 state 2. 更新UI不能直接修改state值
2. setState()修改状态
组件中的state和setState
class Hello extends React.Component {onIncrement() {this.setState({ … })}render() {// 箭头函数中的this指向外部环境,此处为:render()方法return (<button onClick={() => this.onIncrement()}></button>) } }
利用箭头函数自身不绑定this的特点render() 方法中的 this 为组件实例,可以获取到 setState()
1. 箭头函数
class Hello extends React.Component {constructor() {super()this.onIncrement = this.onIncrement.bind(this) }// ...省略 onIncrementrender() {return (<button onClick={this.onIncrement}></button>) } }
利用ES5中的bind方法,将事件处理程序中的this与组件实例绑定到一起
2. Function.prototype.bind()
class Hello extends React.Component {onIncrement = () => {this.setState({ … })}render() {return (<button onClick={this.onIncrement}></button>) } }
3. class 的实例方法
事件绑定this指向
HTML 中的表单元素是可输入的,也就是有自己的可变状态而React 中可变状态通常保存在 state 中,并且只能通过 setState() 方法来修改React将 state 与表单元素值value绑定到一起,由 state 的值来控制表单元素的值受控组件:其值受到 React 控制的表单元素
1. 受控组件
借助ref使用dom元素
案例:评论列表
2. 非受控组件(DOM方式)
表单处理
props的作用:接收传递给组件的数据
传递数据:给组件标签添加属性
接收数据:函数组件通过参数props接收数据,类组件通过 this.props 接收数据
1. 可以给组件传递任意类型的数据
2. props 是只读的对象,只能读取属性的值,无法修改对象
3. 注意:使用类组件时,如果写了构造函数,应该将 props 传递给 super(),否则,无法在构造函数中获取到 props!
props特点:
使用props接收数据
组件是独立且封闭的单元,默认情况下,只能使用组件自己的数据。在组件化过程中,我们将一个完整的功能拆分成多个组件,以更好的完成整个应用的功能。而在这个过程中,多个组件之间不可避免的要共享某些数据。为了实现这些功能,就需要打破组件的独立封闭性,让其与外界沟通。这个过程就是组件通讯。
class Parent extends React.Component {state = { lastName: '王' }render() {return (<div>传递数据给子组件:<Child name={this.state.lastName} /></div>) } }
function Child(props) {return <div>子组件接收到数据:{props.name}</div> }
1. 父组件提供要传递的state数据2. 给子组件标签添加属性,值为 state 中的数据3. 子组件中通过 props 接收父组件中传递的数据
父组件 -> 子组件
1. 父组件提供一个回调函数(用于接收数据)2. 将该函数作为属性的值,传递给子组件
class Child extends React.Component {state = { childMsg: 'React' } handleClick = () => {this.props.getMsg(this.state.childMsg) }return (<button onClick={this.handleClick}>点我,给父组件传递数据</button>) }
3. 子组件通过 props 调用回调函数4. 将子组件的数据作为参数传递给回调函数
子组件 -> 父组件
将共享状态提升到最近的公共父组件中,由公共父组件管理这个状态 思想:状态提升 公共父组件职责:1. 提供共享状态 2. 提供操作共享状态的方法 要通讯的子组件只需通过 props 接收状态或操作状态的方法
兄弟组件
组件通讯的三种方式
组件通讯
跨组件传递数据
2. 使用 Provider 组件作为父节点。<Provider><div className=\"App\"><Child1 /></div></Provider>
3. 设置 value 属性,表示要传递的数据。<Provider value=\"pink\">
4. 调用 Consumer 组件接收数据。<Consumer>{data => <span>data参数表示接收到的数据 -- {data}</span>}</Consumer>
总结:1. 如果两个组件是远方亲戚(比如,嵌套多层)可以使用Context实现组件通讯2. Context提供了两个组件:Provider 和 Consumer3. Provider组件:用来提供数据4. Consumer组件:用来消费数据
Context
children 属性children 属性:表示组件标签的子节点。当组件标签有子节点时,props 就会有该属性children 属性与普通的props一样,值可以是任意值(文本、React元素、组件,甚至是函数)
function Hello(props) {return (<div>组件的子节点:{props.children}</div>) }<Hello>我是子节点</Hello>
Children属性
2.props 校验:允许在创建组件的时候,就指定 props 的类型、格式等作用:捕获使用组件时因为props导致的错误,给出明确的错误提示,增加组件的健壮性
1. 安装包 prop-types (yarn add prop-types / npm i props-types)2. 导入 prop-types 包3. 使用组件名.propTypes = {} 来给组件的props添加校验规则4. 校验规则通过 PropTypes 对象来指定
约束规则1. 常见类型:array、bool、func、number、object、string2. React元素类型:element3. 必填项:isRequired4. 特定结构的对象:shape({ })
function App(props) {return (<div>此处展示props的默认值:{props.pageSize}</div>) }// 设置默认值App.defaultProps = {pageSize: 10}// 不传入pageSize属性<App />
props 的默认值 场景:分页组件 每页显示条数 作用:给 props 设置默认值,在未传入 props 时生效
props校验
props深入
意义:组件的生命周期有助于理解组件的运行方式、完成更复杂的组件功能、分析组件错误原因等组件的生命周期:组件从被创建到挂载到页面中运行,再到组件不用时卸载的过程生命周期的每个阶段总是伴随着一些方法调用,这些方法就是生命周期的钩子函数。钩子函数的作用:为开发人员在不同阶段操作组件提供了时机。只有 类组件 才有生命周期
创建时
更新时
卸载时
1. 每个阶段的执行时机2. 每个阶段钩子函数的执行顺序3. 每个阶段钩子函数的作用
三个阶段
组件的生命周期
思路:将要复用的state和操作state的方法封装到一个组件中问题1:如何拿到该组件中复用的state?在使用组件时,添加一个值为函数的prop,通过 函数参数 来获取(需要组件内部实现)
问题2:如何渲染任意的UI?使用该函数的返回值作为要渲染的UI内容(需要组件内部实现)<Mouse render={(mouse) => {}}/><Mouse render={(mouse) => (<p>鼠标当前位置 {mouse.x},{mouse.y}</p>)}
使用步骤1. 创建Mouse组件,在组件中提供复用的状态逻辑代码(1. 状态 2. 操作状态的方法)2. 将要复用的状态作为 props.render(state) 方法的参数,暴露到组件外部3. 使用 props.render() 的返回值作为要渲染的内容
class Mouse extends React.Component {// … 省略state和操作state的方法render() {return this.props.render(this.state) } }<Mouse render={(mouse) => <p>鼠标当前位置 {mouse.x},{mouse.y}</p>}/>
演示Mouse组件的复用
children代替render属性
代码优化
高阶组件(HOC,Higher-Order Component)是一个函数,接收要包装的组件,返回增强后的组件高阶组件内部创建一个类组件,在这个类组件中提供复用的状态逻辑代码,通过prop将复用的状态传递给被包装组件 WrappedComponentconst EnhancedComponent = withHOC(WrappedComponent)// 高阶组件内部创建的类组件:class Mouse extends React.Component {render() {return <WrappedComponent {...this.state} />} }
1. 创建一个函数,名称约定以 with 开头2. 指定函数参数,参数应该以大写字母开头(作为要渲染的组件)3. 在函数内部创建一个类组件,提供复用的状态逻辑代码,并返回4. 在该组件中,渲染参数组件,同时将状态通过prop传递给参数组件5. 调用该高阶组件,传入要增强的组件,通过返回值拿到增强后的组件,并将其渲染到页面中
使用高阶组件存在的问题:得到的两个组件名称相同原因:默认情况下,React使用组件名称作为 displayName解决方式:为 高阶组件 设置 displayName 便于调试时区分不同的组件displayName的作用:用于设置调试信息(React Developer Tools信息)设置方式:Mouse.displayName = `WithMouse${getDisplayName(WrappedComponent)}`function getDisplayName(WrappedComponent) {return WrappedComponent.displayName || WrappedComponent.name || 'Component' }
设置displayname
高阶组件
问题:props丢失 原因:高阶组件没有往下传递props 解决方式:渲染 WrappedComponent 时,将 state 和 this.props 一起传递给组件 传递方式:<WrappedComponent {...this.state} {...this.props} />
传递props
render-props和高阶组件
React组件
setState() 是异步更新数据的注意:使用该语法时,后面的 setState() 不要依赖于前面的 setState()可以多次调用 setState() ,只会触发一次重新渲染this.state = { count: 1 }this.setState({count: this.state.count + 1})console.log(this.state.count) // 1
第二个参数
setState() 的说明
JSX语法的转化
setState() 的两个作用: 1. 修改 state 2. 更新组件(UI 过程:父组件重新渲染时,也会重新渲染子组件。但只会渲染当前组件子树(当前组件及其所有子组件)
组件更新机制
减轻state
避免不必要的重新渲染
class Hello extends React.PureComponent {render() {return (<div>纯组件</div>) } }
let number = 0let newNumber = numbernewNumber = 2console.log(number === newNumber) // falsestate = { number: 0 }setState({number: Math.floor(Math.random() * 3)})// PureComponent内部对比:最新的state.number === 上一次的state.number // false,重新渲染组件
对于引用类型来说:只比较对象的引用(地址)是否相同const obj = { number: 0 }const newObj = objnewObj.number = 2console.log(newObj === obj) // truestate = { obj: { number: 0 } }// 错误做法state.obj.number = 2setState({ obj: state.obj })// PureComponent内部比较:最新的state.obj === 上一次的state.obj // true,不重新渲染组件
纯组件:PureComponent 与 React.Component 功能相似区别:PureComponent 内部自动实现了 shouldComponentUpdate 钩子,不需要手动比较原理:纯组件内部通过分别 对比 前后两次 props 和 state 的值,来决定是否重新渲染组件
纯组件
组件性能优化
React 更新视图的思想是:只要 state 变化就重新渲染视图特点:思路非常清晰问题:组件中只有一个 DOM 元素需要更新时,也得把整个组件的内容重新渲染到页面中? 不是理想状态:部分更新,只更新变化的地方。问题:React 是如何做到部分更新的? 虚拟 DOM 配合 Diff 算法
执行过程1. 初次渲染时,React 会根据初始state(Model),创建一个虚拟 DOM 对象(树)。2. 根据虚拟 DOM 生成真正的 DOM,渲染到页面中。3. 当数据变化后(setState()),重新根据新的数据,创建新的虚拟DOM对象(树)。4. 与上一次得到的虚拟 DOM 对象,使用 Diff 算法 对比(找不同),得到需要更新的内容。5. 最终,React 只将变化的内容更新(patch)到 DOM 中,重新渲染到页面。
虚拟 DOM 和 Diff 算法Conten
React 原理揭秘1. 工作角度:应用第一,原理第二。2. 原理有助于更好地理解 React 的自身运行机制。3. setState()异步更新数据。4. 父组件更新导致子组件更新,纯组件提升性能。5. 思路清晰简单为前提,虚拟 DOM 和 Diff 保效率。6. 虚拟 DOM state + JSX。7. 虚拟 DOM 的真正价值从来都不是性能。
React原理
前端路由是一套映射规则,在React中,是 URL路径 与 组件 的对应关系 使用React路由简单来说,就是配置 路径和组件(配对)
4. 使用 Link 组件作为导航菜单(路由入口)<Link to=\"/first\">页面一</Link>5. 使用 Route 组件配置路由规则和要展示的组件(路由出口)const First = () => <p>页面一的页面内容</p><Router><div className=\"App\"><Link to=\"/first\">页面一</Link><Route path=\"/first\" component={First}></Route></div></Router>
Router 组件:包裹整个应用,一个 React 应用只需要使用一次两种常用 Router:HashRouter 和 BrowserRouterHashRouter:使用 URL 的哈希值实现(localhost:3000/#/first) (推荐)BrowserRouter:使用 H5 的 history API 实现(localhost:3000/first) Router 组件:包裹整个应用,一个 React 应用只需要使用一次两种常用 Router:HashRouter 和 BrowserRouterHashRouter:使用 URL 的哈希值实现(localhost:3000/#/first) (推荐)BrowserRouter:使用 H5 的 history API 实现(localhost:3000/first)
Link 组件:用于指定导航链接(a 标签)// to属性:浏览器地址栏中的pathname(location.pathname) <Link to=\"/first\">页面一</Link>Route 组件:指定路由展示组件相关信息// path属性:路由规则// component属性:展示的组件// Route组件写在哪,渲染出来的组件就展示在哪 <Route path=\"/first\" component={First}></Route>
常用组件说明
1. 点击 Link 组件(a标签),修改了浏览器地址栏中的 url 。2. React 路由监听到地址栏 url 的变化。3. React 路由内部遍历所有 Route 组件,使用路由规则( path )与 pathname 进行匹配。4. 当路由规则(path)能够匹配地址栏中的 pathname 时,就展示该 Route 组件的内容。
路由的执行过程
场景:点击登录按钮,登录成功后,通过代码跳转到后台首页,如何实现?编程式导航:通过 JS 代码来实现页面跳转history 是 React 路由提供的,用于获取浏览器历史记录的相关信息push(path):跳转到某个页面,参数 path 表示要跳转的路径go(n): 前进或后退到某个页面,参数 n 表示前进或后退页面数量(比如:-1 表示后退到上一页)class Login extends Component {handleLogin = () => {// ...this.props.history.push('/home')}render() {...省略其他代码} }
编程式导航
问题:现在的路由都是点击导航菜单后展示的,如何在进入页面的时候就展示呢?默认路由:表示进入页面时就会匹配的路由默认路由path为:/ <Route path=\"/\" component={Home} />
默认路由
问题:当 Link组件的 to 属性值为 “/login”时,为什么 默认路由 也被匹配成功?默认情况下,React 路由是模糊匹配模式模糊匹配规则:只要 pathname 以 path 开头就会匹配成功
<Link to=\"/login\">登录页面</Link><Route path=\"/\" component={Home} /> 匹配成功
// path 代表Route组件的path属性// pathname 代表Link组件的to属性(也就是 location.pathname)
模糊匹配模式
问题:默认路由任何情况下都会展示,如何避免这种问题?给 Route 组件添加 exact 属性,让其变为精确匹配模式精确匹配:只有当 path 和 pathname 完全匹配时才会展示该路由// 此时,该组件只能匹配 pathname=“/” 这一种情况<Route exact path=\"/\" component=... />推荐:给默认路由添加 exact 属性。
精确匹配
匹配模式
React 路由基础1. React 路由可以有效的管理多个视图(组件)实现 SPA2. Router组件包裹整个应用,只需要使用一次3. Link组件是入口,Route组件是出口4. 通过 props.history 实现编程式导航5. 默认模糊匹配,添加 exact 变精确匹配6. React 路由的一切都是组件,可以像思考组件一样思考路由
React路由
宜居商城
好客租房
项目
React
Java——1995年sun公司提出的高级语言,2009年被Oracle收购
Java之父——詹姆斯高斯林
Java具有可移植性,安全可靠,性能较好,开发社区完善,,功能丰富
JDK,jre 1. java -verison查看版本号,确认是否安装成
Java环境搭建
public class HelloWorld { public static void main(String[] args) { System.out.println(\"Hello World\"); }}
HelloWord-程序
开发步骤
windows名没有勾选
代码写了未保存
文件名和类名不一致
大小写错误
括号不对等
......
常见问题
Java的虚拟机,真正代码运行的地方
Jvm
Java的运行环境
jre
Java开发工具包
jdk
JDK组成
Java跨平台
第一个程序
常用快捷键
Idea
Java语言
环境搭建
1、单行:// .....
2、多行:/* 注释内容可以多行 */
3、文档:/** 注释内容可以多行 */ 一般用到类上
byte(1字节) short(2字节) int(4字节) long(8字节) float(4字节) double(8字节) boolean char
计算机要存字符,但是又不能直接存储字符,只能存储字符编号的二进制形式。
结论:字符在计算机底层其实可以当整数使用的!
ASCLL编码表
Java基础
关键字:Java自己保留的特殊单词,有特殊含义;同学们需要注意的是不能用关键字取类名,变量名
字母,下划线,美元符,数字组成的名称
不能是数字,关键字,特殊符号开头
变量名称,有意义,全英文,首字母小写,满足驼峰模式
标志符
关键字、标志符
环境基础
byte short int long float double
存在小范围类型的变量赋值给大范围类型的变量
自动类型转换
表达式的最终结果类型示由表达式中的最高类型决定
byte、short、char直接提升成int运算的、int、long、float、double
表达式的自动类型转换
强制类型转换可能出现 数据丢失
小数强制转换成整数,会截断小数部分,直接返回整数。如5.99 转换之后为5
强制转换
10进制转2进制
2进制转10进制
8进制和16进制
面试题:
类型转换
+ - * /
基本运算符
++在前面先赋值,在计算
在后面,先计算再赋值
a++和++a
++ --
自增自减
== > >= < <= !=
关系运算符
作用:都可以判断条件都为true结果才是true
& &&
都可以判断条件只要有一个为true结果就是true
与&一样
| ||
^
& | !^ && ||
逻辑运算符
嵌套为 a>b?(a>c?a:c):(b>c?b:c);
类型 a>b?a:b;
三元运算符
运算优先级
请输入整数:123整数123的个位为:3整数123的十位为:2整数123的百位为:1
1数字拆分
面试
运算符
Scanner sc =new Scanner(System.in);
Scanner
键盘录入
类型运算符
作用:根据条件有选择的执行代码
如果{}中只有一行代码,可以省略{}不写,不建议省略大扩展
if else
值不能是变量,也不能重复
穿透性:含义:如果case中没有写break,遇到这个case会直接往下一个case执行,不会判断了,直到遇到break截止
switch case
分支结构
水仙花数
for(初始化语句 ; 循环条件; 迭代语句) { 循环体代码 }
折纸与珠穆朗玛峰
while
do-while
三种循环
1、for和while是判断后执行 ; do-while 第一次直接执行,再判断
2、for和while功能上没有任何区别,while能做的for一定能做
3、for的变量只能循环内部有效,while的变量从定义开始到循环结束后还可以被使用。
三种循环的差别
代码逻辑错误,程序一直执行
死循环
循环套循环
循环嵌套
break 结束
continue 跳出当前循环继续
跳转关键字:break、continue
循环结构
程序流程控制
猜数字游戏
随机数类
random类
流程控制,random
数组:得到一个容器,存储一批同种类型的数据。
特点:定义数组的时候,数据已经确定了,数据也存入进去了
静态初始化数组
特点:定义的时候只确定数组的元素类型和长度,不存入具体的数据(先定义后赋值)
int arr[]=new int[3];
动态初始化数组
数组的定义
数组名称[索引]
从0开始,长度length为元素个数
数组的访问
for循环
for each
数组的遍历
数组求和
猜数字
求最值
随机排名
数组排序
数组的案例
放类信息class文件
方法区
方法执行的区域,变量存储的区域
栈内存
new出来的东西都在这里,数组对象
堆内存
数组内存图
数组越界
在需要控制的代码行左侧,点击一下,形成断点
选择使用Debug方式启动程序,启动后程序会在断点暂停
控制代码一行一行的往下执行
Debug工具使用
array
概念:方法是一种语法结构,它可以把一段代码封装成一个功能,以方便重复调用。
提高代码的复用,减少代码冗余,提高开发效率
提高程序的逻辑性
方法是否需要返回值类型申明,以及参数要具体业务具体分析
方法必须调用才可以跑起来
格式:方法名称(....)
调用
调用形式
方法的编写顺序无所谓。
方法与方法之间是平级关系,不能嵌套定义。
注意是否有返回值
方法没有被调用的时候,在方法区中的字节码文件中存放方法
被调用的时候,需要进入到栈内存中运行
调用流程
传输的是实参存储的数据值,不是传输本身
基本类型传输的就是存储的数据
引用类型传输的就是存储的地址值
值传递
使用方法接收了数组
参数传递
方法的参数传递机制
同一个类中,出现方法名称相同,形参列表不同的多个方法,他们就是重载的方法。
同一个类中,方法名称相同,形参列表不同就是重载的方法。
形参列表不同指的是个数,类型,顺序不同
方法重载的识别技巧:
方法重载
在任何方法中都可以出现,立即跳出并结束当前方法的执行
return
方法:运行在栈内存
机票价格按照淡季旺季、头等舱和经济舱收费、输入机票原价、月份和头等舱或经济舱。按照如下规则计算机票价格:
旺季(5-10月)头等舱9折,经济舱8.5折,淡季(11月到来年4月)头等舱7折,经济舱6.5折。
买飞机票
素数:如果除了1和它本身以外,不能被其他正整数整除,就叫素数。
找素数
定义方法实现随机产生一个5位的验证码,每位可能是数字、大写字母、小写字母。
开发验证码
把一个数组中的元素复制到另一个新数组中去。
数组元素复制
在唱歌比赛中,有6名评委给选手打分,分数范围是[0 - 100]之间的整数。选手的最后得分为:去掉最高分、最低分后的4个评委的平均分,请完成上述过程并计算出选手的得分。
评委打分
数字加密
中奖号码由6个红球和1个篮球组成(注意:6个红球要求不能重复)。
可以定义方法用于返回一组中奖号码(7个数据),返回的形式是一个整型数组。
模拟双色球
小练习
全部使用对象来解决问题,模仿现实世界的
类:设计图,对象的共同特征的描述
对象:类的具体实例
面向对象中的重要2个概念
在Java代码中:必须先有类,才能new对象
修饰符 数据类型 变量名称 = 初始化值
存在默认值的,一般不需要给初始化值
成员变量的格式
首字母大写,满足驼峰,不能用关键字,必须是标志符,有意义
一个代码文件可以定义多个类,只能一个类是public修饰的,public修饰的类名必须成为代码的文件名
定义类的补充注意事项
类名 对象变量 = new 类名();
创建对象的
定义类,创建对象的代码写法
对象放在堆内存中
对象变量栈内存中,存储的是对象在堆内存中的地址
成员变量的数据存放在对象中,存在于堆内存中。
当堆内存中的类对象或数组对象,没有被任何变量引用(指向)时,就会被判定为内存中的“垃圾
Java存在自动垃圾回收器,会定期进行清理。
垃圾回收
对象内存图
初始化一个类的对象,并返回这个对象的地址
有参数构造器:在初始化对象的时候,同时可以为对象进行赋值。
无参数构造器(默认存在的):初始化的对象时,成员变量的数据均采用默认值。
任何类定义出来,默认就自带了无参数构造器,写不写都有。
一旦定义了有参数构造器,无参数构造器就没有了,此时就需要自己写一个无参数构造器了。
构造器
代表了当前对象的地址
可以访问当前对象的成员变量,可以区分变量是局部的还是对象的成员变量
有参构造函数中
可以在成员方法中出现,用于指定当前变量是访问成员变量
出现位置
this关键字
合理隐藏,合理的暴露
一般把成员变量使用private修饰隐藏,只能本类访问
提供public修饰的getter setter方法暴露取值和赋值
常见形式
可以提升代码的安全性,可以提高开发的一个效率
注意:封装已经成为Java代码的标准,即使毫无意义,代码风格也要满足封装的要求来书写。
封装
是实体类,学生类、老师类、用户类
提供无参数构造器
成员变量全部私有
提供getter setter方法暴露成员变量的取值和赋值
书写规范
标准 JavaBean
Java中提供一个关键字extends,用这个关键字,我们可以让一个类和另一个类建立起父子关系。
提高代码复用性,减少代码冗余,增强类的功能扩展性。
子类extens父类
子类 继承父类,子类可以得到父类的属性和行为,子类可以使用。Java中子类更强大
子类可以继承父类的属性和行为,但是子类不能继承父类的构造器。Java是单继承模式:一个类只能继承一个直接父类。Java不支持多继承、但是支持多层继承。Java中所有的类都是Object类的子类。
不可以的,子类有自己的构造器,父类构造器用于初始化父类对象。
子类是否可以继承父类的构造器?
可以的,只是不能直接访问。
子类是否可以继承父类的私有成员?
子类可以直接使用父类的静态成员(共享)
子类是否可以继承父类的静态成员?
问题
继承后的特点
子类们相同特征(共性属性,共性方法)放在父类中定义,子类独有的的属性和行为应该定义在子类自己里面
如果子类的独有属性、行为定义在父类中,会导致其它子类也会得到这些属性和行为,这不符合面向对象逻辑。
为什么
图解内存原理
1.子类们相同特征(共性属性,共性方法)放在父类中定义。2.子类独有的的属性和行为应该定义在子类自己里面。
要求
设计规范和内存原理
在子类方法中访问成员(成员变量、成员方法)满足:就近原则1.先子类局部范围找2.然后子类成员范围找3.然后父类成员范围找如果父类范围还没有找到则报错。
继承后成员方法和变量访问特点
在继承体系中,子类出现了和父类中一模一样的方法声明,我们就称子类这个方法是重写的方法。
当子类需要父类的功能,但父类的该功能不完全满足自己的需求时。子类可以重写父类中的方法。
满足要求
方法重写注意事项和要求重写方法的名称、形参列表必须与被重写方法的名称和参数列表一致。私有方法不能被重写。子类重写父类方法时,访问权限必须大于或者等于父类 (暂时了解 :缺省 < protected < public)子类不能重写父类的静态方法,如果重写会报错的。
方法重写
1.子类继承父类后构造器的特点:子类中所有的构造器默认都会先访问父类中无参的构造器,再执行自己。2.为什么?子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据。子类初始化之前,一定要调用父类构造器先完成父类数据空间的初始化。3.怎么调用父类构造器的?子类构造器的第一行语句默认都是:super(),不写也存在。
子类构造器的特点
super调用父类有参数构造器的作用: 初始化继承自父类的数据。1.如果父类中没有无参数构造器,只有有参构造器,会出现什么现象呢?会报错。因为子类默认是调用父类无参构造器的。2.如何解决?子类构造器中可以通过书写 super(…),手动调用父类的有参数构造器
子类构造访问父类构造
this(...)和super(…)使用注意点:子类通过 this (...)去调用本类的其他构造器,本类其他构造器会通过 super 去手动调用父类的构造器,最终还是会调用父类构造器的。注意:this(…) super(…) 都只能放在构造器的第一行,所以二者不能共存在同一个构造器中。
this super使用详解
同类型的对象,调用同一个行为,表现出不同的行为特征
父类 对象名称 = new 子类构造器;
接口 对象名称 = new 实现类构造器
形式
对于方法调用:编译看左边,运行看右边
对于变量调用:编译看左边,运行也看左边
多态的识别技巧
必须继承或实现关系
必须父类类型指向子类对象
存在方法重写
多态的使用前提
右边对象解耦合
父类类型变量作为方法的参数,可以接收一切子类对象
多态的优势
多态下无法调用子类独有功能
多态的劣势
自动类型转换:Animal a = new Dog();
强制类型转换 : Dog d = (Dog)a;
有继承关系的两个类就可以强制转换,编译不报错
Cat c = (Cat)a;
运行可能出错
出现类型转换异常
a instanceof Dog
a instanceof Cat
Java建议强制转换前先判断真实类型
多态的类型转换
多态
面向对象
面向对象OOP
定义字符串变量存储字符串对象,同时String类提供了很多操作字符串的功能
String变量每次的修改其实都是产生并指向了新的字符串对象。
原来的字符串对象都是没有改变的,所以称不可变字符串。
String其实常被称为不可变字符串类型,它的对象在创建后不能被更改
为什么不可变
以“”方式给出的字符串对象,在字符串常量池中存储,而且相同内容只会在其中存储一份
通过构造器new对象,每new一次都会产生一个新对象,放在堆内存中。
注意面试题== 和 equals的区别
创建对象的方式
使用String提供的equlas方法。
基本数据类型比较时使用。
子主题
只关心内容一样就返回true。
如果是字符串比较应该使用使用什么方式进行比较,为什么?
开发中什么时候使用==比较数据
面试题
返回此字符串的长度
public int length()
获取某个索引位置处的字符
public char charAt(int index)
将当前字符串转换成字符数组返回
public char[] toCharArray():
根据开始和结束索引进行截取,得到新的字符串(包前不包后)
从传入的索引处截取,截取到末尾,得到新的字符串
public String substring(int beginIndex)
使用新值,将字符串中的旧值替换,得到新的字符串
根据传入的规则切割字符串,得到字符串数组返回
public String[] split(String regex)
常用API
模拟用户登录,验证码问题
String
ArrayList代表的是集合类,集合是一种容器,与数组类似,不同的是集合的大小是不固定的
集合非常适合做元素个数不确定,且要进行增删操作的业务场景。
集合的提供了许多丰富、好用的功能,而数组的功能很单一
创建,添加元素
集合中只能存储引用类型,不支持基本数据类型
public E get(int index) 返回指定索引处的元素
public int size() 返回集合中的元素的个数
public E remove(int index) 删除指定索引处的元素,返回被删除的元素
public boolean remove(Object o) 删除指定的元素,返回删除是否成功
ArrayList
string ArrayList
String ArrayList
每个用户对象要对应一个账户对象:所以需要设计账户类Account
面向对象分析
系统需要提供一个容器用于存储这些账户对象的信息,我们选ArrayList集合。
使用集合容器分析
需要结合分支、循环、跳转等相关操作控制程序的业务逻辑
内容比较,分析,数据处理等需要用到String等常用API
使用常见API
需求分析
账户类、首页设计
用户开户功能实现
用户登录功能实现
用户操作页设计、查询账户、退出账户功能
用户存款、取款功能设计
用户转账功能设计
用户密码修改功能、销户功能
功能设计
ATM系统
JavaSE基础
static是静态的意思,可以修饰成员变量和成员方法。static修饰成员变量表示该成员变量只在内存中只存储一份,可以被共享访问、修改。
静态成员变量(有static修饰,属于类、加载一次,可以被共享访问),访问格式类名.静态成员变量(推荐)对象.静态成员变量(不推荐)。实例成员变量(无static修饰,属于对象),访问格式:对象.实例成员变量。
成员变量的分类和访问分别是什么样的?
两种成员变量各自在什么情况下定义?
成员变量
静态成员方法(有static修饰,属于类),建议用类名访问,也可以用对象访问。
实例成员方法(无static修饰,属于对象),只能用对象触发访问。
表示对象自己的行为的,且方法中需要访问实例成员的,则该方法必须申明成实例方法。如果该方法是以执行一个共用功能为目的,则可以申明成静态方法。
成员方法
内部都是一些静态方法,每个方法完成一个功能一次编写,处处可用,提高代码的重用性。
工具类是什么,有什么好处?
建议工具类的构造器私有化处理。工具类不需要创建对象。
工具类有什么要求?
工具类
static访问注意实现:静态方法只能访问静态的成员,不可以直接访问实例成员。实例方法可以访问静态的成员,也可以访问实例成员。静态方法中是不可以出现this关键字的。
代码块是类的5大成分之一(成员变量、构造器,方法,代码块,内部类),定义在类中方法外。在Java类下,使用 { } 括起来的代码被称为代码块 。
格式:static{}特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发、只执行一次使用场景:在类加载的时候做一些静态数据初始化的操作,以便后续使用。
静态代码块:
格式:{}特点:每次创建对象,调用构造器执行时,都会执行该代码块中的代码,并且在构造器执行前执行使用场景:初始化实例资源。
构造代码块
代码块
斗地主游戏
概念:单例模式可以保证系统中,应用该模式的这个类永远只有一个实例,即一个类永远只能创建一个对象
/** a、定义一个单例类 */ public class SingleInstance { /** c.定义一个静态变量存储一个对象即可 :属于类,与类一起加载一次 */ public static SingleInstance instance = new SingleInstance (); /** b.单例必须私有构造器*/ private SingleInstance (){ System.out.println(\"创建了一个对象\"); } }
饿汉单例的实现步骤?1.定义一个类,把构造器私有2.定义一个静态变量存储一个对象
实现步骤
饿汉单例模式:在创建对象时已经创建好了
1.定义一个类,把构造器私有。2.定义一个静态变量存储一个对象。3.提供一个返回单例对象的方法。
/** 定义一个单例类 */ class SingleInstance{ /** 定义一个静态变量存储一个对象即可 :属于类,与类一起加载一次 */ public static SingleInstance instance ; // null /** 单例必须私有构造器*/ private SingleInstance(){} /** 必须提供一个方法返回一个单例对象 */ public static SingleInstance getInstance(){ ... return ...; } }
懒汉单例模式:先准备好,在调用时才会创建
设计模式之——单例(standalone)
Static单例代码块
同一个包下的类可以互相访问
不同包下的类,必须导包才可以访问:import com....
同一个类中可以使用多个同名的类,但是默认只能导入一个,其他用包名.类名
包
private --> 缺省 --- > protected ---> public
权限修饰符
final修饰类,类不能被继承
final修饰方法,方法不能被重写
fInal修饰变量,有且仅能被赋值一次
final
public static final修饰的成员变量
特点:有且仅能被赋值一次
做系统配置信息
做信息标志和分类
常量
枚举:特殊类型
枚举:信息的标志和分类
枚举类不能被继承
枚举
充当一种模板,能确定能复用的抽象类定义,功能不能确定的定义成抽象方法,子类实现
public abstract class Animal{}
抽象方法:只有方法签名,没有方法体,必须abstract修饰
得到抽象方法,失去了创建对象的能力
抽象类其他成分全部有
抽象方法特点:
抽象类的意义
抽象类为什么不能创建对象? 类有的成员(成员变量、方法、构造器)抽象类都具备抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类一个类继承了抽象类必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。不能用abstract修饰变量、代码块、构造器。
final和abstract是什么关系?互斥关系abstract定义的抽象类作为模板让子类继承,final定义的类不能被继承。抽象方法定义通用功能让子类重写,final定义的方法子类不能重写。
面试注意
某软件公司要为某银行的业务支撑系统开发一个利息结算系统,账户有活期和定期账户两种,活期是0.35%,定期是 1.75%,定期如果满10万额外给予3%的收益。结算利息要先进行用户名、密码验证,验证失败直接提示,登录成功进行结算
分析
1.创建一个抽象的账户类Account作为父类模板,提供属性(卡号,余额)2.在父类Account中提供一个模板方法实现登录验证,利息结算、利息输出。3.具体的利息结算定义成抽象方法,交给子类实现。4.定义活期账户类,让子类重写实现具体的结算方法5.定义定期账户类,让子类重写实现具体的结算方法6.创建账户对象,完成相关功能。
作业:银行利息结算系统
抽象类
接口用关键字interface来定义public interface 接口名 { // 常量 // 抽象方法}
类和类的关系:单继承。类和接口的关系:多实现。接口和接口的关系:多继承,一个接口可以同时继承多个接口。
面试注意
1、接口不能创建对象2、一个类实现多个接口,多个接口中有同样的静态方法不冲突。3、一个类继承了父类,同时又实现了接口,父类中和接口中有同名方法,默认用父类的。4、一个类实现了多个接口,多个接口中存在同名的默认方法,不冲突,这个类重写该方法即可。5、一个接口继承多个接口,是没有问题的,如果多个接口中存在规范冲突则不能多继承。
接口
面向对象进阶
概念:一个类里面的类就是内部类
方便构建子类对象,最终为了简化代码
格式:new 类|抽象类|接口() { 方法重写 }
是一个没有名字的内部类
本身也是一个对象,是自己匿名内部类的对象
这个对象相当于是一个子类对象
使用形式:作为方法的参数,完成对象回调
匿名内部类
静态 成员 匿名
内部类
内部类,API
面向对象进阶(包,抽象类等)
展示子类内容
public String toString()默认是返回当前对象在堆内存中的地址信息:类的全限名@内存地址
Object的equals方法的作用是什么?默认是与另一个对象比较地址是否一样让子类重写,以便比较2个子类对象的内容是否相同
Object的equals相比较于string的equals更安全,因为在比较的时候多了一些判断
public boolean equals(Object o)是比较当前对象与另一个对象的地址是否相同,相同返回true,不同返回false
object
为什么拼接、反转字符串建议使用StringBuilder?1.String :内容是不可变的、拼接字符串性能差。2.StringBuilder:内容是可变的、拼接字符串性能好、代码优雅。3.定义字符串使用String拼接,修改等操作字符串使用StringBuilder
StringBulider
Stringbuffer在使用时,方法上大多数都有synchronized修饰
多线程,帮助buffer更好的拼接字符串
为什么说StringBuffer会更安全
StringBuffer与StringBulider差别
Math
System
解决浮点型运算精度失真问题。
BigDecimal b1 = BigDecimal.valueOf(0.1);
获取方式
BigDecimal
public Date() 创建一个Date对象,代表的是系统当前此刻日期时间
public long getTime() 返回从1970年1月1日 00:00:00走到此刻的总的毫秒数
时间毫秒值怎么恢复成日期对象public Date(long time);public void setTime(long time);
Date类代表当前所在系统的日期时间信息。
data
作用:可以去完成日期时间的格式化操作
public SimpleDateFormat(String pattern)构造一个SimpleDateFormat,使用指定的格式
将日期格式化成日期/时间字符串
public final String format(Date date)
将时间毫秒值式化成日期/时间字符串
public final String format(Object time)
格式化方法
转化方式
例题;请计算出当前时间往后走2天14小时49分06秒后的时间是多少
public Date parse(String source)从给定字符串的开始解析文本以生成日期
SimpleDateFormat解析字符串时间成为日期对象
秒杀系统面试题
SimpleDateFormat
获取一年中的某一天,或者一年中的某一周等
public static Calendar getInstance()获取当前日历对象
Calendar
他们 分别表示日期,时间,日期时间对象,他们的类的实例是不可变的对象。他们三者构建对象和API都是通用的
静态方法,根据当前时间创建对象LocaDate localDate = LocalDate.now(); LocalTime llocalTime = LocalTime.now(); LocalDateTime localDateTime = LocalDateTime.now();
public static Xxxx now();
public static Xxxx of(…);
LocalDate、LocalTime、LocalDateTime
instant
LocalDate:不包含具体时间的日期。LocalTime:不含日期的时间。LocalDateTime:包含了日期及时间。Instant:代表的是时间戳。DateTimeFormatter 用于做时间的格式化和解析的Duration:用于计算两个“时间”间隔 Period:用于计算两个“日期”间隔
Java8新增日期工具
日期
1、包装类是什么,作用是什么?基本数据类型对应的引用类型,实现了一切皆对象。后期集合和泛型不支持基本类型,只能使用包装类。2、包装类有哪些特殊功能?可以把基本类型的数据转换成字符串类型(用处不大)可以把字符串类型的数值转换成真实的数据类型(真的很有用)
包装类
public String[] split(String regex)按照正则表达式匹配的内容进行分割字符串,反回一个字符串数组。
正则表达式
Arrays
1、Lambda表达式的基本作用?简化函数式接口的匿名内部类的写法。2、Lambda表达式有什么使用前提?必须是接口的匿名内部类,接口中只能有一个抽象方法
使用方法和注意事项
Lambda
集合的大小不固定,启动后可以动态变化,类型也可以选择不固定。集合更像气球。
集合非常适合做元素的增删操作。
Collection单列集合,每个元素(数据)只包含一个值。Map双列集合,每个元素包含两个值(键值对)。
如何约定集合存储数据的类型,需要注意什么?集合支持泛型。集合和泛型不支持基本类型,只支持引用数据类型
collection常用方法
Iterator<E> iterator() 返回集合中的迭代器对象,该迭代器对象默认指向当前集合的0索引
E next() 获取当前位置的元素,并同时将迭代器对象移向下一个位置,注意防止取出越界。
使用方法
迭代器
lambda
遍历方式
集合中存储的是元素对象的地址。
存储
集合遍历存储
后进先出,先进后出
栈
先进先出,后进后出
队列
数据结构概述、栈、队列
查询数据通过地址值和索引定位,查询任意数据耗时相同。(元素在内存中是连续存储的)
查询速度快
要将原始数据删除,同时后面每个数据前移。
删除效率低
添加位置后的每个数据后移,再添加元素
添加效率低
链表中的元素是在内存中不连续存储的,每个元素节点包含数据值和下一个元素的地址
链表查询慢。无论查询哪个数据都要从头开始找
链表增删相对快
链表
1.只能有一个根节点: 每个节点最多支持2个直接子节点。2.节点的度: 节点拥有的子树的个数,二叉树的度不大于2 叶子节点 度为0的节点,也称之为终端结点。3.高度:叶子结点的高度为1,叶子结点的父节点高度为2,以此类推,根节点的高度最高。4.层:根节点在第一层,以此类推5.兄弟节点 :拥有共同父节点的节点互称为兄弟节点
目的:提高检索数据的性能。
二叉查找树又称二叉排序树或者二叉搜索树。特点:1,每一个节点上最多有两个子节点 2,左子树上所有节点的值都小于根节点的值 3,右子树上所有节点的值都大于根节点的值
二叉查找树
二叉树
任意节点的左右两个子树的高度差不超过1,任意节点的左右两个子树都是一颗平衡二叉树
基本策略是进行左旋,或者右旋保证平衡。
左左左右右右右左
旋转的情况
如何保证平衡
平衡二叉树
红黑树是一种自平衡的二叉查找树,是计算机科学中用到的一种数据结构。1972年出现,当时被称之为平衡二叉B树。1978年被修改为如今的\"红黑树\"。每一个节点可以是红或者黑;红黑树不是通过高度平衡的,它的平衡是通过“红黑规则”进行实现的。
每一个节点或是红色的,或者是黑色的,根节点必须是黑色。如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)。对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点。
规则
红黑树
常见数据结构
ArrayList、LinekdList :有序,可重复,有索引。有序:存储和取出的元素顺序一致有索引可以通过索引操作元素可重复存储的元素可以重复
常见
ArrayList集合底层原理 ArrayList底层是基于数组实现的:根据索引定位元素快,增删需要做元素的移位操作。 第一次创建集合并添加第一个元素的时候,在底层创建一个默认长度为10的数组。
底层原理
在添加第11个元素时,数组会扩容,扩容大小为1.5倍((oldCapacity * 3)/2 + 1)
扩容问题
迭代器会出现for each 不会出现
当我们从集合中找出某个元素并删除的时候可能出现一种并发修改异常问题。
底层是双向链表,增删快,而且多头尾操作
LinkedList
List
统一数据格式
通配符
泛型深入
集合
无序:存取顺序不一致不重复:可以去除重复无索引没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素。
HashSet : 无序、不重复、无索引。LinkedHashSet:有序、不重复、无索引。TreeSet:排序、不重复、无索引。Set集合的功能上基本上与Collection的API一致。
集合特点
HashSet集合底层采取哈希表存储的数据。哈希表是一种对于增删改查数据性能都较好的结构
哈希值: 是JDK根据对象的地址,按照某种规则算出来的int类型的数值。
同一个对象多次调用hashCode()方法返回的哈希值是相同的默认情况下,不同对象的哈希值是不同的。
对象的哈希特点
JDK8之前由数组和链表组成当挂在元素下面的数据过多时,查询性能降低从JDK8开始后,当链表长度超过8的时候,自动转换为红黑树
判断位置是否为null,如果是则存储,不是的话使用equals方法比较值,相同不存储,不相同存储
如何保证去重
如果希望Set集合认为2个内容一样的对象是重复的,必须重写对象的hashCode()和equals()方法
HashSet
有序、不重复、无索引。这里的有序指的是保证存储和取出的元素顺序一致原理:底层数据结构是依然哈希表,只是每个元素又额外的多了一个双链表的机制记录存储的顺序。
LinkedHashSet
不重复、无索引、可排序按照元素的大小默认升序(有小到大)排序。TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好。注意:TreeSet集合是一定要排序的,可以将元素按照指定的规则进行排序。
TreeSet集合自定义排序规则有几种方式2种。1.类实现Comparable接口,重写比较规则。2.集合自定义Comparator比较器对象,重写比较规则。
TreeSet
Collections.addAll();
调用collections方法
collection
Map集合是一种双列集合,每个元素包含两个数据。Map集合的每个元素的格式:key=value(键值对元素)。Map集合也被称为“键值对集合”。
概述和使用
整体格式
集合实现类的特点
先获取Map集合的全部键的Set集合。遍历键的Set集合,然后通过键提取对应值。
方式一:键找值的方式遍历:先获取Map集合全部的键,再根据遍历键找值。
先把Map集合转换成Set集合,Set集合中每个元素都是键值对实体类型了。遍历Set集合,然后提取键以及提取值。
方式二:键值对的方式遍历,把“键值对“看成一个整体,难度较大。
Map.foreach()
方式三:JDK 1.8开始之后的新技术:Lambda表达式。
由键决定:无序、不重复、无索引。HashMap底层是哈希表结构的。依赖hashCode方法和equals方法保证键的唯一。如果键要存储的是自定义对象,需要重写hashCode和equals方法。基于哈希表。增删改查的性能都较好。
HashMap
由键决定:有序、不重复、无索引。这里的有序指的是保证存储和取出的元素顺序一致原理:底层数据结构是依然哈希表,只是每个键值对元素又额外的多了一个双链表的机制记录存储的顺序。
LinkedMap
由键决定特性:不重复、无索引、可排序可排序:按照键数据的大小默认升序(有小到大)排序。只能对键排序。注意:TreeMap集合是一定要排序的,可以默认排序,也可以将键按照指定的规则进行排序TreeMap跟TreeSet一样底层原理是一样的。
TreeMap
Map
set collection map
如果某个数据不能被修改,把它防御性地拷贝到不可变集合中是个很好的实践。或者当集合对象被不可信的库调用时,不可变形式是安全的。
不可变
不可变集合,就是不可被修改的集合。集合的数据项在创建的时候提供,并且在整个生命周期中都不可改变。否则报错。
List、Set、Map接口中,都存在of方法,可以创建一个不可变的集合
如何创建
可变集合和不可变集合
1、Stream流的作用是什么,结合了什么技术?简化集合、数组操作的API。结合了Lambda表达式。
2、说说Stream流的思想和使用步骤。先得到集合或者数组的Stream流(就是一根传送带)。把元素放上去。用Stream流简化的API来方便的操作元素。
集合:可以使用Collection接口中的默认方法stream()生成流
数组:public static <T> Stream<T> stream(T[] array) public static<T> Stream<T> of(T... values)
void forEach(Consumer action)
long count()
操作方法
Stream流无法直接修改数据
终结方法后流不可以继续使用,非终结方法会返回新的流,支持链式编程。
终结和非终结的含义是什么?
Collections.toList()
Collections.toMap()
Collections.toSet()
收集Stream流的含义:就是把Stream流操作后的结果数据转回到集合或者数组中去。Stream流:方便操作集合/数组的手段。集合/数组:才是开发中的目的。
Stream流
异常是程序在“编译”或者“执行”的过程中可能出现的问题,注意:语法错误不算在异常体系中。 比如:数组索引越界、空指针异常、 日期格式化异常,等…
编译时异常:没有继承RuntimeExcpetion的异常,编译阶段就会出错。
编译异常
运行时异常:继承自RuntimeException的异常或其子类,编译阶段不报错,运行可能报错。
运行异常
异常分类
默认会在出现异常的代码那里自动的创建一个异常对象:ArithmeticException。异常会从方法中出现的点这里抛出给调用者,调用者最终抛出给JVM虚拟机。虚拟机接收到异常对象后,先在控制台直接输出异常栈信息数据。直接从当前执行的异常点干掉当前程序。后续代码没有机会执行了,因为程序已经死亡。
异常的执行流程
throws
try catch finally
两者结合
一、出现异常直接抛出去给调用者,调用者也继续抛出去。 二、出现异常自己捕获处理,不麻烦别人。 三、前两者结合,出现异常直接抛出去给调用者,调用者捕获处理。
编译时异常处理形式
异常
Stream 异常
日志技术具备的优势1.可以将系统执行的信息选择性的记录到指定的位置(控制台、文件中、数据库中)。2.可以随时以开关的形式控制是否记录日志,无需修改源代码。
日志规范接口Commons LoggingJCL
Log4j
JUL(java.util.loggiing)
Logback
其他实现
日志实现框架
Simple Logging Facade for Java
类型
Logback主要分为三个技术模块: logback-core: logback-core 模块为其他两个模块奠定了基础,必须有。 logback-classic:它是log4j的一个改良版本,同时它完整实现了slf4j API。 logback-access 模块与 Tomcat 和 Jetty 等 Servlet 容器集成,以提供 HTTP 访问日志功能
①:在项目下新建文件夹lib,导入Logback的相关jar包到该文件夹下,并添加到项目库中去。②:必须将Logback的核心配置文件logback.xml直接拷贝到src目录下。③:在代码中获取日志的对象④:使用日志对象输出日志信息
通过logback.xml 中的<append>标签可以设置输出位置和日志信息的详细格式。通常可以设置2个日志输出位置:一个是控制台、一个是系统文件中
输出位置
LogBack
级别程度依次是:TRACE< DEBUG< INFO<WARN<ERROR ; 默认级别是debug(忽略大小写)对应其方法。作用:用于控制系统中哪些日志级别是可以输出的,只输出级别不低于设定级别的日志信息。ALL 和 OFF分别是打开全部日志信息,及关闭全部日志信息。
具体在<root level=“INFO”>标签的level属性中设置日志级别。
日志级别
日志框架
系统角色准备
首页需要包含登录,商家入驻,客户注册功能。商家和客户可以共用一个登录功能。判断登录成功的用户的真实类型,根据用户类型完成对应的操作界面设计。
首页、登录、商家界面、用户界面设计
展示本商家的信息和其排片情况。提供影片上架功能:就是创建一个影片对象,存入到商家的集合中去。退出,需要回到登录的首页。
商家功能-展示详情、影片上架、退出
提供影片下架功能:其实就是从商家的集合中删除影片对象。影片修改功能:拿到需要修改的影片对象,修改里面的数据。
商家功能-影片下架、影片修改
用户功能-展示全部影片信息
用户可以选择需要购买票的商家和其电影信息。可以选择购买的数量。购买成功后需要支付金额,并更新商家金额和客户金额
用户功能-购票操作
用户只能对自己已经购买过的电影进行评分。需要在当前用户对象中记录购买的电影信息(包括是否已经评价过)。每部电影的评分信息应该是系统所有用户评分的平均值。应该在系统定义一个集合存储每部电影的评分详情。
用户功能-评分功能
电影售票系统
JavaSE加强
File类在包java.io.File下、代表操作系统的文件对象(文件、文件夹)。File类提供了诸如:定位文件,获取文件本身的信息、删除文件、创建文件(文件夹)等功能
File类概述
public String[] list()
当调用者不存在时,返回null当调用者是一个文件时,返回null当调用者是一个空文件夹时返回一个长度为0的数组当调用者是一个有内容的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回当调用者是一个有隐藏文件的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回,包含隐藏内容当调用者是一个需要权限才能进入的文件夹时,返回null
pubic File[] listFiles()(常用)
file
阶乘问题
斐波那契
递归的公式: f(n) = f(n-1) * n;递归的终结点:f(1) 递归的方向必须走向终结点:
三要素
描述:猴子第一天摘下若干桃子,当即吃了一半,觉得好不过瘾,于是又多吃了一个第二天又吃了前天剩余桃子数量的一半,觉得好不过瘾,于是又多吃了一个以后每天都是吃前天剩余桃子数量的一半,觉得好不过瘾,又多吃了一个等到第10天的时候发现桃子只有1个了。需求:请问猴子第一天摘了多少个桃子?
猴子吃桃问题
啤酒2元1瓶,4个盖子可以换一瓶,2个空瓶可以换一瓶,请问10元钱可以喝多少瓶酒,剩余多少空瓶和盖子。
买啤酒问题
递归
字节输入流:以内存为基准,来自磁盘文件/网络中的数据以字节的形式读入到内存中去的流称为字节输入流。字节输出流:以内存为基准,把内存中的数据以字节写出到磁盘文件或者网络中去的流称为字节输出流。字符输入流:以内存为基准,来自磁盘文件/网络中的数据以字符的形式读入到内存中去的流称为字符输入流。字符输出流:以内存为基准,把内存中的数据以字符写出到磁盘文件或者网络介质中去的流称为字符输出流。
流的4大类
流的体系
finally:在异常处理时提供finally块来执行所有清除操作,比如说IO流中的释放资源特点:被finally控制的语句最终一定会执行,除非JVM退出异常处理标准格式:try….catch…finally
try-catch-finally
try-catch-resource 自动释放资源、代码简洁
资源释放的方式
创建字节输入流管道与源文件对象接通
public FileInputStream(File file)
创建字节输入流管道与源文件路径接通
public FileInputStream(String pathname)
每次读取一个字节返回,如果字节已经没有可读的返回-1
public int read()
每次读取一个字节数组返回,如果字节已经没有可读的返回-1
public int read(byte[] buffer)
存在问题:性能较慢,会出现中文乱码问题
1、如何使用字节输入流读取中文内容输出不乱码呢?定义一个与文件一样大的字节数组,一次性读取完文件的全部字节。2、直接把文件数据全部读取到一个字节数组可以避免乱码,是否存在问题?如果文件过大,字节数组可能引起内存溢出。
问题注意
FileInputStream
写一个字节出去
public void write(int a)
写一个字节数组
public void write(byte[] buffer)
写一部分字节数组
文件拷贝
FileOutputStream
字节流
字节流读取中文输出会存在什么问题?答:会乱码。或者内存溢出。读取中文输出,哪个流更合适,为什么?答:字符流更合适,最小单位是按照单个字符读取的。
public FileReader(File file)
public FileReader(String pathname)
每次读取一个字符返回,如果字符已经没有可读的返回-1
每次读取一个字符数组,返回读取的字符个数,如果字符已经没有可读的返回-1
public int read(char arr[])
Reader
FileWriter
字符流
字节流适合做一切文件数据的拷贝(音视频,文本)字节流不适合读取中文内容输出字符流适合做文本文件的操作(读,写)
字节流、字符流如何选择使用?
缓冲流也称为高效流、或者高级流。之前学习的字节流可以称为原始流。
作用:缓冲流自带缓冲区、可以提高原始字节流、字符流读写数据的性能
体系图
public BufferedInputStream(InputStream is)
public BufferedOutputStream(OutputStream is)
字节缓存输入输出流
public String readLine()
public BufferedReader(Reader r)
BufferedReader
public void newLine()
public BufferedWriter(Writer w)
BufferedWriter
字符缓冲流为什么提高了操作数据的性能?字符缓冲流自带8K缓冲区可以提高原始字符流读写数据的性能字符缓冲font color=\"#d32f2f\
字符缓冲输入输出流
拷贝出师表
缓存流
使用字符输入转换流可以提取文件(GBK)的原始字节流,原始字节不会存在问题。然后把字节流以指定编码转换成字符输入流,这样字符输入流中的字符就不乱码
如何使用字符流直接读取文件不乱码
public InputStreamReader(InputStream is)
可以把原始的字节流按照指定编码转换成字符输入流这样字符流中的字符就不乱码了(重点)
public InputStreamReader(InputStream is ,String charset)
字符输入
public OutputStreamWriter(OutputStream os)
可以把原始的字节输出流按照指定编码转换成字符输出流(重点)
public OutputStreamWriter(OutputStream os,String charset)
OutputStreamWriter
字符输出
转换流
作用:以内存为基准,把内存中的对象存储到磁盘文件中去。使用到的流是对象字节输出流:ObjectOutputStream
public ObjectOutputStream(OutputStream out)
public final void writeObject(Object obj)
序列化方法
序列化
public ObjectInputStream(InputStream out)
public Object readObject()
反序列化
序列化对象
PrintStream
PrintWriter
PrintWriter继承自字符输出流Writer,支持写字符数据出去
PrintStream继承自字节输出流OutputStream,支持写字节
两者区别
打印流
拷贝文件夹
点名
Properties
复制文件。
复制文件夹。
FileUtils
IOUtils
IO框架
IO流
file 递归 IO流
线程(thread)是一个程序内部的一条执行路径。
线程
多线程是指从软硬件上实现多条执行流程的技术。
定义一个子类MyThread继承线程类java.lang.Thread,重写run()方法创建MyThread类的对象调用线程对象的start()方法启动线程(启动后还是执行run方法的)
1、为什么不直接调用了run方法,而是调用start启动线程。直接调用run方法会当成普通方法执行,此时相当于还是单线程执行。只有调用start方法才是启动一个新的线程执行。
2、把主线程任务放在子线程之前了。这样主线程一直是先跑完的,相当于是一个单线程的效果了。
继承Thread类缺点:不利于扩展
定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法创建MyRunnable任务对象把MyRunnable任务对象交给Thread处理。调用线程对象的start()方法启动线程
实现Runnable接口优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强。缺点:编程多一层对象包装,如果线程有执行结果是不可以直接返回的。
得到任务对象定义类实现Callable接口,重写call方法,封装要做的事情。用FutureTask把Callable对象封装成线程任务对象。把线程任务对象交给Thread处理。调用Thread的start方法启动线程,执行任务、线程执行完毕后、通过FutureTask的get方法去获取任务执行的结果
把Callable对象封装成FutureTask对象。
public FutureTask<>(Callable call)
获取线程执行call方法返回的结果。
public V get() throws Exception
FutureTask的API
实现Callable接口可以获取到线程执行结果
创建方法
Thread常用方法
多个线程同时操作同一个共享资源的时候可能会出现业务安全问题,称为线程安全问题。
存在多线程并发同时访问共享资源存在修改共享资源
出现原因
取钱业务
线程安全
加锁:让多个线程实现先后依次访问共享资源,这样就解决了安全问题
作用:把出现线程安全问题的核心代码给上锁。
原理:每次只能一个线程进入,执行完毕后自动解锁,其他线程才可以进来执行。
同步代码块
规范上:建议使用共享资源作为锁对象。对于实例方法建议使用this作为锁对象。对于静态方法建议使用字节码(类名.class)对象作为锁对象。
锁的规范和要求
作用:把出现线程安全问题的核心方法给上锁。原理:每次只能一个线程进入,执行完毕以后自动解锁,其他线程才可以进来执行。
修饰符 synchronized 返回值类型 方法名称(形参列表) { 操作共享资源的代码 }
synchronize
同步方法
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来构建Lock锁对象。
获得Lock锁的实现类对象
public ReentrantLock()
void lock()
void unlock()
Lock锁
线程同步
线程通信
线程池就是一个可以复用线程的技术
使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象
如何得到线程池对象
不能小于0最大数量 >= 核心线程数量不能小于0时间单位不能为null不能为null不能为null
参数一:指定线程池的线程数量(核心线程): corePoolSize参数二:指定线程池可支持的最大线程数: maximumPoolSize参数三:指定临时线程的最大存活时间: keepAliveTime参数四:指定存活时间的单位(秒、分、时、天): unit参数五:指定任务队列: workQueue参数六:指定用哪个线程工厂创建线程: threadFactory参数七:指定线程忙,任务满的时候,新任务来了怎么办: handler
ThreadPoolExecutor构造器的参数说明
临时线程什么时候创建啊?新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程。
什么时候会拒绝任务?核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始任务拒绝。
谁代表线程池?ExecutorService接口
ThreadPoolExecutor创建线程池对象示例
ExecutorService的常用方法
新任务拒绝策略
存在的陷阱
Executors工具类实现线程池
线程池
定时器是一种控制任务延时调用,或者周期调用的技术。作用:闹钟、定时邮件发送。
创建Timer定时器对象
public Timer()
开启一个定时器,按照计划处理TimerTask任务
方式一:Timer方式二: ScheduledExecutorService
Timer定时器的特点和存在的问题1、Timer是单线程,处理多个任务按照顺序执行,存在延时与设置定时器的时间有出入。2、可能因为其中的某个任务的异常使Timer线程死掉,从而影响后续任务执行。
存在问题
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 得到定时器对象
优点1、基于线程池,某个任务的执行情况不会影响其他定时任务的执行。
ScheduledExecutorService定时器
定时器
并发和并行的含义并发:CPU分时轮询的执行线程。并行:同一个时刻同时在执行。
并发,并行
六种状态
线程的六种状态转换
线程的生命周期
多线程
常见的通信模式有如下2种形式:Client-Server(CS) 、 Browser/Server(BS)
网络编程定义
IPV4和IPV6
设备在网络中的地址,是唯一的标识。
IP地址
端口号:标识正在计算机设备上运行的进程(程序),被规定为一个 16 位的二进制,范围是 0~65535。端口类型周知端口:0~1023,被预先定义的知名应用占用(如:HTTP占用 80,FTP占用21) 注册端口:1024~49151,分配给用户进程或某些应用程序。(如:Tomcat占 用8080,MySQL占用3306)动态端口:49152到65535,之所以称为动态端口,是因为它 一般不固定分配某种进程,而是动态分配。注意:我们自己开发的程序选择注册端口,且一个设备中不能出现两个程序的端口号一样,否则出错。
应用程序在设备中唯一的标识。
端口
使用TCP协议,必须双方先建立连接,它是一种面向连接的可靠通信协议。传输前,采用“三次握手”方式建立连接,所以是可靠的 。 在连接中可进行大数据量的传输 。 连接、发送数据都需要确认,且传输完毕后,还需释放已建立的连接,通信效率较低。
TCP协议通信场景对信息安全要求较高的场景,例如:文件下载、金融等数据通信。
三次握手建立
四次挥手断开
需求:客户端实现步骤创建客户端的Socket对象,请求与服务端的连接。使用socket对象调用getOutputStream()方法得到字节输出流。使用字节输出流完成数据的发送。释放资源,关闭socket管道。
客户端发送消息
通信模拟演示
创建发送端的Socket对象与服务端连接,参数为服务端程序的ip和端口。
OutputStream getOutputStream()
InputStream getInputStream()
socket
等待接收客户端的Socket通信连接连接成功返回Socket对象与客户端建立端到端通信
public Socket accept()
public ServerSocket(int port)
serversocket
需求:服务端实现步骤创建ServerSocket对象,注册服务端端口。调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象。通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收。释放资源:关闭socket管道
服务端实现接收消息
TCP获取
需求:使用TCP通信方式实现:多发多收消息。具体要求:可以使用死循环控制服务端收完消息继续等待接收下一个消息。客户端也可以使用死循环等待用户不断输入消息。客户端一旦输入了exit,则关闭客户端程序,并释放资源。
同时接收多个客户端的消息?不可以的。因为服务端现在只有一个线程,只能与一个客户端进行通信。
TCP多发多收消息
主线程定义了循环负责接收客户端Socket管道连接每接收到一个Socket通信管道后分配一个独立的线程负责处理它。
TCP 同时接收多个客户端消息
1、目前的通信架构存在什么问题?客户端与服务端的线程模型是: N-N的关系。客户端并发越多,系统瘫痪的越快。
存在的问题
服务端可以复用线程处理多个客户端,可以避免系统瘫痪。适合客户端通信时长较短的场景。
使用线程池的优势
TCP通信-使用线程池优化
TCP通信实战案例-即时通信
TCP通信实战案例-模拟BS系统
TCP
UDP协议: UDP是一种无连接、不可靠传输的协议。将数据源IP、目的地IP和端口封装成数据包,不需要建立连接 每个数据包的大小限制在64KB内 发送不管对方是否准备好,接收方收到也不确认,故是不可靠的 可以广播发送 ,发送数据结束时无需释放资源,开销小,速度快。UDP协议通信场景语音通话,视频会话等。
创建接收端的数据包对象buf:用来存储接收的内容length:能够接收内容的长度
创建发送端数据包对象buf:要发送的内容,字节数组length:要发送内容的字节长度address:接收端的IP地址对象port:接收端的端口号
DatagramPacket:数据包对象(韭菜盘子)
获得实际接收到的字节个数
public int getLength()
DatagramPacket常用方法
创建发送端的Socket对象,系统会随机分配一个端口号。
public DatagramSocket()
创建接收端的Socket对象并指定端口号
public DatagramSocket(int port)
DatagramSocket:发送端和接收端对象(人)
public void send(DatagramPacket dp)发送
public void receive(DatagramPacket p)接收
DatagramSocket类成员方法
客户端实现步骤创建DatagramSocket对象(发送端对象) 创建DatagramPacket对象封装需要发送的数据(数据包对象)使用DatagramSocket对象的send方法传入DatagramPacket对象 释放资源
接收端实现步骤创建DatagramSocket对象并指定端口(接收端对象)创建DatagramPacket对象接收数据(数据包对象)使用DatagramSocket对象的receive方法传入DatagramPacket对象 释放资源
使用UDP发送消息的步骤
需求使用UDP通信方式开发接收端和发送端。分析发送端可以一直发送消息。接收端可以不断的接收多个发送端的消息展示。发送端输入了exit则结束发送端程序。
使用UDP通信实现:多发多收消息
如何实现广播
实现组播通信
MulticastSocket
socket.joinGroup(new InetSocketAddress(InetAddress.getByName(\"224.0.1.1\
如何实现组播
单播:单台主机与单台主机之间的通信。广播:当前主机与所在网络中的所有主机通信。组播:当前主机与选定的一组主机的通信。
广播,组播通信
UDP
数据在网络中传输的规则,常见的协议有UDP协议和TCP协议。
协议
网络通信三要素
InetAddress 的使用
网络协议模型
网络编程
单元测试就是针对最小的功能单元编写测试代码,Java程序最小的功能单元是方法,因此,单元测试就是针对Java方法的测试,进而检查方法的正确性。
单元测试
反射是指对于任何一个Class类,在\"运行的时候\
反射的基本作用、关键?反射是在运行时获取类的字节码文件对象:然后可以解析类中的全部成分。反射的核心思想和关键就是:得到编译以后的class文件对象。
作用,关键
获取Class类对象,如此才可以解析类的全部成分
反射的第一步是什么
方式一:Class c1 = Class.forName(“全类名”);方式二:Class c2 = 类名.class方式三:Class c3 = 对象.getClass();
获取的三种方式
返回所有构造器对象的数组(只能拿public的)
Constructor<?>[] getConstructors()
返回所有构造器对象的数组,存在就能拿到
Constructor<?>[] getDeclaredConstructors()
返回单个构造器对象(只能拿public的)
Constructor<T> getConstructor(Class<?>... parameterTypes)
返回单个构造器对象,存在就能拿到
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
使用反射技术获取构造器对象并使用
根据指定的构造器创建对象
T newInstance(Object... initargs)
public void setAccessible(boolean flag)
Constructor类中用于创建对象的方法
运行方法参数一:用obj对象调用该方法参数二:调用方法的传递的参数(如果没有就不写)返回值:方法的返回值(如果没有就不写)
使用反射技术获取方法对象并使用
反射的作用?可以在运行时得到一个类的全部成分然后操作。可以破坏封装性。也可以破坏泛型的约束性。更重要的用途是适合:做Java高级框架基本上主流框架都会基于反射设计一些通用技术功能。
反射的作用
Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。Java 语言中的类、构造器、方法、成员变量、参数等都可以被注解进行标注。
ublic @interface 注解名称 { public 属性类型 属性名() default 默认值 ; }
自定义注解
元注解有两个: @Target: 约束自定义注解只能在哪些地方使用, @Retention:申明注解的生命周期
注解反射
XML的几个特点和使用场景一是纯文本,默认使用UTF-8编码;二是可嵌套;如果把XML内容存为文件,那么它就是一个XML文件。XML的使用场景:XML内容经常被当成消息进行网络传输,或者作为配置文件用于存储系统的信息。
特殊字符
通常数据会封装成Java的对象,如单个对象,或者集合对象形式
DOM解析
绝对路径相对路径全文检索属性查找
Xpath解析
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一, 这种类型的设计模式属于创建型模式,它提供了一种获取对象的方式。
工厂设计模式
XML 解析 设计模式
JavaSE高级
JavaSE
Dubbo 是一款高性能、轻量级的开源Java RPC框架,提供面向接口代理的高性能RPC调用、智能负载均衡、服务自动注册和发现、运行期流量调度、可视化服务治理和运维等功能
提供快速的访问体验
高性能
网站服务可以一直使用
通过硬件增加/减少,提高/降低处理能力
可伸缩
系统间的耦合性,方便的通过新增和删除,增加和减少新的功能模块
高可扩展
提供网站安全访问和数据加密,安全存储策略等等
安全性
随需求应变,快速响应
敏捷性
目标
大型互联网架构目标
很多人一起干一样的事儿
集群
很多人一起干不一样的事儿,合起来就是一件大事儿
分布式
集群和分布式
开发部署方便
项目启动变慢
可靠性差,一旦某一个模块出问题,影响全局
扩展性差可伸缩性差
性能低
垂直架构是将单体中多个模块拆分成多个独立的项目,形成多个单体
重复功能太多
垂直架构
指的是在垂直架构的基础上,将公共调用的模块抽取出来作为独立的模块,供系统消费者调用,以便于实现服务共享和重用
RPC调用
存在的问题,一旦一个服务存在变更,所有调用它的服务都会产生变更
SOA架构
微服务是在SOA架构上的升华
原有的单个业务系统会拆分成为多个独立开发,设计,运行的小应用这些小应用之间通过调用服务完成交互和集成
强调重点是:业务需要彻底的组件化和服务化
开发者可以自由的选择开发技术
服务实现组件化
服务之间交互一般用Rest API
去中心化,每一个微服务都有自己的数据库
自动化部署
微服务架构
架构演进
分布式中相关概述
阿里轻量级 开源RPCjava框架
架构
Dubbo概述
zk安装
集成springMVC
Dubbo快速入门
默认端口20880
*代表所有服务
dubbo-admin安装使用
dubbo内部已经将序列化和反序列化封装了
注册中心挂了,服务还能正常使用吗?
地址缓存
dubbo利用超时机制来解决这个问题,设置一个超时时间,在一个时间段内,无法完成服务访问,自动断开连接timeout
配置在服务的提供方
超时
重试属性retries默认为2
重试
超时与重试
version(服务者提供,版本方式)
多版本
Random:权重随机
RoundRobin:按权重轮询
最少活跃调用数,相同数的随机
LeastActive
一致性Hash,相同参数的请求总是发送到同一提供者
ConsistentHash
集群容错
fail:return null 失败后返回null,什么也不显示
服务降级
Dubbo高级
Dubbo学习网站:https://dubbo.apache.org/zh/index.html
TED(最优质的演讲):https://www.ted.com/谷粉学术:https://gfsoso.99lb.net/scholar.html大学资源网:http://www.dxzy163.com/简答题:http://www.jiandati.com/网易公开课:https://open.163.com/ted/网易云课堂:https://study.163.com/中国大学MOOC:www.icourse163.org哔哩哔哩弹幕网:www.bilibili.com我要自学网:www.51zxw.netGitHub:https://github.com/码云:https://gitee.com/源码之家:https://www.mycodes.net/知乎:www.zhihu.com学堂在线:www.xuetangx.comCSDN:https://www.csdn.net/爱课程:www.icourses.cn猫咪论文:https://lunwen.im/iData(论文搜索):www.cn-ki.net文泉考试:https://www.wqkaoshi.com
学习
书栈网:https://www.bookstack.cn/码农之家(计算机电子书下载):www.xz577.com鸠摩搜书:www.jiumodiary.com云海电子图书馆:www.pdfbook.cn周读(书籍搜索):ireadweek.com知轩藏书:http://www.zxcs.me/脚本之家电子书下载:https://www.jb51.net/books/搜书VIP-电子书搜索:http://www.soshuvip.com/all.html书格(在线古籍图书馆):https://new.shuge.org/caj云阅读:http://cajviewer.cnki.net/cajcloud/必看网(人生必看的书籍):https://www.biikan.com/
书籍
上班摸鱼必备(假装电脑系统升级):http://fakeupdate.net/PIECES 拼图(30 个 CSS 碎片进行拼图,呈现 30 种濒临灭绝的动物):http://www.species-in-pieces.com/图片立体像素画:https://pissang.github.io/voxelize-image/福利单词(一个不太正经的背单词网站):http://dict.ftqq.com查无此人(刷新网站,展现一张AI 生成的人脸照片):https://thispersondoesnotexist.com/在线制作地图图例:https://mapchart.net/创意光线绘画:http://weavesilk.com/星系观察:https://stellarium-web.org/煎蛋:http://jandan.net/渣男-说话的艺术:https://lovelive.tools/全历史:https://www.allhistory.com/iData:https://www.cn-ki.net/术语在线:http://www.termonline.cn/
冷知识 / 黑科技
DogeDoge搜索引擎:www.dogedoge.com秘迹搜索:https://mijisou.com/小白盘:https://www.xiaobaipan.com/云盘精灵(资源搜索):www.yunpanjingling.com虫部落(资源搜索):www.chongbuluo.com如风搜(资源搜索):http://www.rufengso.net/爱扒:https://www.zyboe.com/
资源搜索
奶牛快传(在线传输文件利器):cowtransfer.com文叔叔(大文件传输,不限速):https://www.wenshushu.cn/云端超级应用空间(PS,PPT,Excel,Ai):https://uzer.me/香当网(年终总结,个人简历,事迹材料,租赁合同,演讲稿):https://www.xiangdang.net/二维码生成:https://cli.im/搜狗翻译:fanyi.sogou.com熵数(图表制作,数据可视化):https://dydata.io/appv2/#/pages/index/home拷贝兔:https://cp.anyknew.com/AI人工智能图片放大:http://bigjpg.com/zh幕布(在线大纲笔记工具):mubu.com在线转换器(在线转换器转换任何测量单位):https://zh.justcnw.com/调查问卷制作:https://www.wenjuan.com/果核剥壳(软件下载):https://www.ghpym.com/软件下载:https://www.unyoo.com/MSDN我告诉你(windows10系统镜像下载):https://msdn.itellyou.cn/
小工具
世界各国网址大全:http://www.world68.com/小森林导航:http://www.xsldh6.com/简捷工具:http://www.shulijp.com/NiceTool.net 好工具网:http://www.nicetool.net/现实君工具箱(综合型在线工具集成网站):http://tool.uixsj.cn/蓝调网站:http://lcoc.top/偷渡鱼:https://touduyu.com/牛导航:http://www.ziliao6.com/小呆导航:https://www.webjike.com/index.html简法主页:http://www.jianfast.com/KIM主页:https://kim.plopco.com/聚BT:https://jubt.net/cn/index.html精准云工具合集:https://jingzhunyun.com/兔2工具合集:https://www.tool2.cn/爱资料工具(在线实用工具集合):www.toolnb.com工具导航:https://hao.logosc.cn/
导航页
阿木影视:https://www.aosk.online/电影推荐:http://www.mvcat.comAPP影院:https://app.movie去看TV:https://www.qukantv.net/动漫视频网:http://www.zzzfun.com/94神马电影网:http://www.9rmb.com/NO视频官网:http://www.novipnoad.com/蓝光画质电影:http://www.languang.co/在线看剧:http://dy.27234.cn/大数据导航:http://hao.199it.com/多功能图片网站:https://www.logosc.cn/so/牛牛TV:http://www.ziliao6.com/tv/VideoFk解析视频:http://www.videofk.com/蓝调网站:http://lcoc.top/vip2.3/永久资源采集网:http://www.yongjiuzy1.com/
看视频
码力全开(产品/设计师/独立开发者的资源库):https://www.maliquankai.com/designnav/免费音频素材:https://icons8.cn/music新CG儿(视频素材模板,无水印+免费下载):https://www.newcger.com/Iconfont(阿里巴巴矢量图标库):https://www.iconfont.cn/小图标下载:https://www.easyicon.net/Flight Icon:https://www.flighticon.co/第一字体转换器:http://www.diyiziti.com/doyoudosh(平面设计):www.doyoudo.com企业宣传视频在线制作:https://duomu.tv/MAKE海报设计官网:http://maka.im/一键海报神器:https://www.logosc.cn/photo/utm_source=hao.logosc.cn&utm_medium=referral字由(字体设计):http://www.hellofont.cn/查字体网站:https://fonts.safe.360.cn/爱给网(免费素材下载的网站,包括音效、配乐,3D、视频、游戏,平面、教程):http://www.aigei.com/在线视频剪辑:https://bilibili.clipchamp.com/editor
学设计
即书(在线制作PPT):https://www.keysuper.com/PDF处理:https://smallpdf.com/cnPDF处理:https://www.ilovepdf.com/zh-cnPDF处理:https://www.pdfpai.com/PDF处理:https://www.hipdf.cn/图片压缩,PDF处理:https://docsmall.com/腾讯文档(在线协作编辑和管理文档):docs.qq.comProcessOn(在线协作制作结构图):www.processon.comiLovePDF(在线转换PDF利器):www.ilovepdf.comPPT在线制作:https://www.woodo.cn/PDF24工具(pdf处理工具):https://tools.pdf24.org/enIMGBOT(在线图片处理):www.imgbot.ai福昕云编辑(在线编辑PDF):edit.foxitcloud.cnTinyPNG(在线压缩图片):tinypng.comUZER.ME(在线使用各种大应用,在线使用CAD,MATLAB,Office三件套 ):uzer.me优品PPT(模板下载):http://www.ypppt.com/第一PPT(模板下载):http://www.1ppt.com/xiazai/三顿PPT导航:sandunppt.comExcel函数表:https://support.office.com/zh-cn/article/excel-%E5%87%BD%E6%95%B0%EF%BC%88%E6%8C%89%E5%AD%97%E6%AF%8D%E9%A1%BA%E5%BA%8F%EF%BC%89-b3944572-255d-4efb-bb96-c6d90033e188
处理文档
电脑壁纸:http://lcoc.top/bizhi/https://unsplash.com/https://pixabay.com/https://www.pexels.com/https://visualhunt.com/https://www.ssyer.com/彼岸图网:http://pic.netbian.com/极像素(超高清大图):https://www.sigoo.com/免费版权图片搜索:https://www.logosc.cn/so/
找图片
资源网站
存储数据的仓库,数据是有组织的进行存储
数据库
操纵和管理数据库的大型软件
数据库管理系统
操作关系型数据库的编程语言,定义了一套操作关系型数据库统一标准
SQL
MySQL安装和卸载
MySQL概述
1). SQL语句可以单行或多行书写,以分号结尾。2). SQL语句可以使用空格/缩进来增强语句的可读性。3). MySQL数据库的SQL语句不区分大小写,关键字建议使用大写。4). 注释:单行注释:-- 注释内容 或 # 注释内容多行注释:/* 注释内容 */
通用语法
数据定义语言,用来定义数据库对象(数据库,表,字段)
DDL
数据操作语言,用来对数据库表中的数据进行增删改
DML
数据查询语言,用来查询数据库中表的记录
DQL
数据控制语言,用来创建数据库用户、控制数据库的访问权限
DCL
SQL语句,根据其功能,主要分为四类:DDL、DML、DQL、DCL
show databases ;
查询所有数据库
select database() ; 1
查询当前数据库
create database [ if not exists ] 数据库名 [ default charset 字符集 ] [ collate 排序 规则 ] ;
if not exists 解决数据库重名问题
create database itheima default charset utf8mb4;
创建指定字符集数据库
创建数据库
drop database [ if exists ] 数据库名 ;
删除数据库
use 数据库名 ;
切换数据库
数据库操作
show tables
查询当前数据库所有表
desc 表名 ;
查询指定表结构
show create table 表名 ;
查询指定表的建表语句
创建一张表
应用举例
数值类型
char 与 varchar 都可以描述字符串,char是定长字符串,指定长度多长,就占用多少个字符,和字段值的长度无关 。而varchar是变长字符串,指定的长度为最大占用长度 。相对来说,char的性能会更高些
应用
字符串类型
1). 生日字段 birthday birthday date2). 创建时间 createtime createtime datetime
常用
日期类型
设计一张员工信息表,要求如下:1. 编号(纯数字)2. 员工工号 (字符串类型,长度不超过10位) 3. 员工姓名(字符串类型,长度不超过10位)4. 性别(男/女,存储一个汉字)5. 年龄(正常人年龄,不可能存储负数)6. 身份证号(二代身份证号均为18位,身份证中有X这样的字符)7. 入职时间(取值年月日即可)
表操作案例
ALTER TABLE 表名 ADD 字段名 类型 (长度) [ COMMENT 注释 ] [ 约束 ];
例子:ALTER TABLE emp ADD nickname varchar(20) COMMENT '昵称';
添加字段
ALTER TABLE 表名 MODIFY 字段名 新数据类型 (长度);
修改数据类型
ALTER TABLE 表名 CHANGE 旧字段名 新字段名 类型 (长度) [ COMMENT 注释 ] [ 约束 ];
将emp表的nickname字段修改为username,类型为varchar(30)ALTER TABLE emp CHANGE nickname username varchar(30) COMMENT '昵称';
修改字段名和字段类型
ALTER TABLE 表名 DROP 字段名;ALTER TABLE emp DROP username;
删除字段
ALTER TABLE 表名 RENAME TO 新表名;ALTER TABLE emp RENAME TO employee;
修改表名
修改
DROP TABLE [ IF EXISTS ] 表名;
删除表
删除
表操作
可视化工具
批量添加
添加数据
修改数据
DELETE FROM 表名 [ WHERE 条件 ] ;
删除数据
SELECT 字段列表 FROM表名列表 WHERE 条件列表 GROUP BY 分组字段列表 HAVING 分组后条件列表 ORDER BY 排序字段列表 LIMIT分页参数
语法结构
查询多个
字段设置别名
SELECT DISTINCT 字段列表 FROM 表名;
去除重复记录
基础查询
SELECT 字段列表 FROM 表名 WHERE 条件列表 ;
条件查询
聚合函数
SELECT 字段列表 FROM 表名 [ WHERE 条件 ] GROUP BY 分组字段名 [ HAVING 分组 后过滤条件 ];
执行时机不同:where是分组之前进行过滤,不满足where条件,不参与分组;而having是分组之后对结果进行过滤。判断条件不同:where不能对聚合函数进行判断,而having可以。
font color=\"#f44336\
where与having区别
分组查询
排序在日常开发中是非常常见的一个操作,有升序排序,也有降序排序。
排序方式ASC : 升序(默认值)DESC: 降序
根据年龄排序select * from emp order by age asc; select * from emp order by age;
排序查询
注意事项:• 起始索引从0开始,起始索引 = (查询页码 - 1)* 每页显示记录数。• 分页查询是数据库的方言,不同的数据库有不同的实现,MySQL中是LIMIT。• 如果查询的是第一页数据,起始索引可以省略,直接简写为 limit 10。
分页查询
查询性别为 男 ,并且年龄在 20-40 岁(含)以内的姓名为三个字的员工。select * from emp where gender = '男' and ( age between 20 and 40 ) and name like '___';
执行顺序
数据库查询语言
Data Control Language(数据控制语言)
在MySQL中需要通过Host和User来唯一标识一个用户
select * from mysql.user;
CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';
创建用户
ALTER USER '用户名'@'主机名' IDENTIFIED WITH mysql_native_password BY '新密码' ;
修改用户密码
DROP USER '用户名'@'主机名' ;
删除用户
注意事项:• 在MySQL中需要通过用户名@主机名的方式,来唯一标识一个用户。• 主机名可以使用 % 通配。• 这类SQL开发人员操作的比较少,主要是DBA( Database Administrator 数据库管理员)使用。
修改用户heima的访问密码为1234;alter user 'heima'@'%' identified with mysql_native_password by '1234';
删除 itcast@localhost 用户drop user 'itcast'@'localhost';
管理用户
SHOW GRANTS FOR '用户名'@'主机名' ;
查询权限
GRANT 权限列表 ON 数据库名.表名 TO '用户名'@'主机名';
授予权限
REVOKE 权限列表 ON 数据库名.表名 FROM '用户名'@'主机名';
撤销权限
• 多个权限之间,使用逗号分隔• 授权时, 数据库名和表名可以使用 * 进行通配,代表所有。
查询 'heima'@'%' 用户的权限show grants for 'heima'@'
授予 'heima'@'%' 用户itcast数据库所有表的所有操作权限grant all on itcast.* to 'heima'@'%';
撤销 'heima'@'%' 用户的itcast数据库的所有权限revoke all on itcast.* from 'heima'@'%';
权限控制
concat演示
select lower('Hello');
小写
select upper('Hello');
大写
左填充
右填充
select trim(' Hello MySQL ');
去除空格
截取子字符串
字符串函数
数值函数
select curdate();
select curtime()
select now();
select YEAR(now()); select MONTH(now()); select DAY(now());
查询所有员工的入职天数,并根据入职天数倒序排序。
思路: 入职天数,就是通过当前日期 - 入职日期,所以需要使用datediff函数来完成。
日期函数
流程函数
约束是作用于表中字段上的规则,用于限制存储在表中的数据
保证数据库中数据的正确、有效性和完整性。
目的
alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references dept(id) on update cascade on delete cascade ;
级联CASCADE删除父节点时,会相应的删除子节点信息
alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references dept(id) on update set null on delete set null ;
set null当删除更新父节点时,子节点会被设置为null
约束演示
一对一
一对多
多对多
内连接:相当于查询A、B交集部分数据外连接:左外连接:查询左表所有数据,以及两张表交集部分数据右外连接:查询右表所有数据,以及两张表交集部分数据自连接:当前表与自身的连接查询,自连接必须使用表别名
数据准备
连接查询
子查询
隐式内连接
SELECT 字段列表 FROM 表1 [ INNER ] JOIN 表2 ON 连接条件 ... ;
显示内连接
内连接
SELECT 字段列表 FROM 表1 LEFT [ OUTER ] JOIN 表2 ON 条件 ... ;
左外
SELECT 字段列表 FROM 表1 RIGHT [ OUTER ] JOIN 表2 ON 条件 ... ;
右外
外连接
SELECT 字段列表 FROM 表A 别名A JOIN 表A 别名B ON 条件 ... ;
自连接
查询员工 及其 所属领导的名字
SELECT 字段列表 FROM 表A ... UNION [ ALL ] SELECT 字段列表 FROM 表B ....;
select * from emp where salary < 5000 union all select * from emp where age > 50;
联合查询
SELECT * FROM t1 WHERE column1 = ( SELECT column1 FROM t2 );
A. 标量子查询(子查询结果为单个值)B. 列子查询(子查询结果为一列)C. 行子查询(子查询结果为一行)D. 表子查询(子查询结果为多行多列)
A. WHERE之后B. FROM之后C. SELECT之后
查询位置
子查询返回的结果是单个值(数字、字符串、日期等),最简单的形式,这种子查询称为标量子查询
常用的操作符:= <> > >= < <=
select * from emp where dept_id = (select id from dept where name = '销售部');
查询 \"销售部\" 的所有员工信息
select * from emp where entrydate > (select entrydate from emp where name = '方东白');
查询在 \"方东白\" 入职之后的员工信息
标量子查询
子查询返回的结果是一列(可以是多行),这种子查询称为列子查询。
常用的操作符:IN 、NOT IN 、 ANY 、SOME 、 ALL
select * from emp where dept_id in (select id from dept where name = '销售部' or name = '市场部');
查询 \"销售部\" 和 \"市场部\" 的所有员工信息
select * from emp where salary > all ( select salary from emp where dept_id = (select id from dept where name = '财务部') );
查询比 财务部 所有人工资都高的员工信息
select * from emp where salary > any ( select salary from emp where dept_id = (select id from dept where name = '研发部') );
查询比研发部其中任意一人工资高的员工信息
列子查询
子查询返回的结果是一行(可以是多列),这种子查询称为行子查询。
常用的操作符:= 、<> 、IN 、NOT IN
span style=\
行子查询
子查询返回的结果是多行多列,这种子查询称为表子查询。
常用的操作符:IN
查询与 \"鹿杖客\
查询入职日期是 \"2006-01-01\
表子查询
查询员工的姓名、年龄、职位、部门信息 (隐式内连接)
查询年龄小于30岁的员工的姓名、年龄、职位、部门信息(显式内连接)
查询拥有员工的部门ID、部门名称
查询所有员工的工资等级
查询 \"研发部\" 所有员工的信息及 工资等级
查询 \"研发部\" 员工的平均工资
select * from emp where salary > ( select salary from emp where name = '灭绝' );
查询工资比 \"灭绝\" 高的员工信息。
select * from emp where salary > ( select avg(salary) from emp );
select * from emp e2 where e2.salary < ( select avg(e1.salary) from emp e1 where e1.dept_id = e2.dept_id )
查询低于本部门平均工资的员工信息
多表查询案例
多表查询
是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。
未控制事务
SELECT @@autocommit ; SET @@autocommit = 0 ;
查看/设置事务提交方式
COMMIT;
提交事务
ROLLBACK;
回滚事务
控制事务一
开启事务
转账案例:
控制事务二
事务操作
原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败。
一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态。
隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。
持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。
四大特性(ACID)
赃读:一个事务读到另外一个事务还没有提交的数据。
不可重复读:一个事务先后读取同一条记录,但两次读取的数据不同,称之为不可重复读
幻读:一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在,好像出现了 \"幻影\"。
并发事务问题
SELECT @@TRANSACTION_ISOLATION;
查看事务隔离级别
SET [ SESSION | GLOBAL ] TRANSACTION ISOLATION LEVEL { READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE }
设置事务隔离级别
事务的隔离级别
基础部分
进阶
运维
MySQL
1. 数据量的总大小,一个机器放不下时2. 数据的索引(B+ Tree)一个机器的内存放不下时3. 访问量(读写混合)一个实例不能承受
数据存储的瓶颈
单机
Memcached(缓存)+ MySQL + 垂直拆分
MySQL主从读写分离
在Memcached的高速缓存,MySQL的主从复制,读写分离的基础之上,这时MySQL主库的写压力开始出现瓶颈,而数据量的持续猛增,由于MyISAM使用表锁,在高并发下会出现严重的锁问题,大量的高并发MySQL应用开始使用InnoDB引擎代替MyISAM。
分表分库 + 水平拆分 + Mysql 集群
MySQL数据库也经常存储一些大文本的字段,导致数据库表非常的大,在做数据库恢复的时候就导致非常的慢,不容易快速恢复数据库,比如1000万4KB大小的文本就接近40GB的大小,如果能把这些数据从MySQL省去,MySQL将变的非常的小,关系数据库很强大,但是它并不能很好的应付所有的应用场景,MySQL的扩展性差(需要复杂的技术来实现),大数据下IO压力大,表结构更改困难,正是当前使用MySQL的开发人员面临的问题。
MySQL 的扩展性瓶颈
如今的状态
今天我们可以通过第三方平台(如:Google,FaceBook等)可以很容易的访问和抓取数据。用户的个人信息,社交网络,地理位置,用户生成的数据和用户操作日志已经成倍的增加、我们如果要对这些用户数据进行挖掘,那SQL数据库已经不适合这些应用了,而NoSQL数据库的发展却能很好的处理这些大的数据!
为什么用NoSQL
非关系型数据库
NoSQL 数据库种类繁多,但是一个共同的特点都是去掉关系数据库的关系型特性。数据之间无关系,这样就非常容易扩展,也无形之间,在架构的层面上带来了可扩展的能力
易扩展
NoSQL数据库都具有非常高的读写性能,尤其是在大数据量下,同样表现优秀。这得益于它的非关系性,数据库的结构简单。一般MySQL使用Query Cache,每次表的更新Cache就失效,是一种大力度的Cache,在针对Web2.0的交互频繁应用,Cache性能不高,而NoSQL的Cache是记录级的,是一种细粒度的Cache,所以NoSQL在这个层面上来说就要性能高很多了。官方记录:Redis 一秒可以写8万次,读11万次!
大数据量高性能
NoSQL无需事先为要存储的数据建立字段,随时可以存储自定义的数据格式,而在关系数据库里,增删字段是一件非常麻烦的事情。如果是非常大数据量的表,增加字段简直就是噩梦
多样灵活的数据模型
传统的关系型数据库 RDBMS- 高度组织化结构化数据- 结构化查询语言(SQL)- 数据和关系都存储在单独的表中- 数据操纵语言,数据定义语言- 严格的一致性- 基础事务NoSQL- 代表着不仅仅是SQL- 没有声明性查询语言- 没有预定义的模式- 键值对存储,列存储,文档存储,图形数据库- 最终一致性,而非ACID属性- 非结构化和不可预知的数据- CAP定理- 高性能,高可用性 和 可伸缩性
传统的RDBMS VS NoSQL
什么是NoSQL
难点:数据类型的多样性数据源多样性和变化重构数据源改造而数据服务平台不需要大面积重构
大型互联网应用(大数据,高并发,多样数据类型)的难点和解决方案
经典应用分析
KV键值:新浪:BerkeleyDB+redis美团:redis+tair阿里、百度:memcache+redis
文档型数据库(bson格式比较多):CouchDBMongoDBMongoDB 是一个基于分布式文件存储的数据库。由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。
NoSQL数据模型分类
这两步要么一起完成,要么一起不完成,如果只完成第一步,第二步失败,钱会莫名其妙少了100元。
A (Atomicity) 原子性
事务前后数据的完整性必须保持一致。
C (Consistency) 一致性
所谓的独立性是指并发的事务之间不会互相影响,如果一个事务要访问的数据正在被另外一个事务修改,只要另外一个事务未提交,它所访问的数据就不受未提交事务的影响。
I (Isolation) 隔离性
持久性是指一旦事务提交后,它所做的修改将会永久的保存在数据库上,即使出现宕机也不会丢失。
D (Durability) 持久性
传统的ACID分别是什么?
CA - 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。CP - 满足一致性,分区容忍必的系统,通常性能不是特别高。AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些
C : Consistency(强一致性)A : Availability(可用性)P : Partition tolerance(分区容错性)
CAP
其核心思想是即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性
Base
CAP+Base
NoSQL概述
Redis:REmote DIctionary Server(远程字典服务器)
Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。Redis不仅仅支持简单的 key-value 类型的数据,同时还提供list、set、zset、hash等数据结构的存储。Redis支持数据的备份,即master-slave模式的数据备份。
Redis是什么
内存存储和持久化:redis支持异步将内存中的数据写到硬盘上,同时不影响继续服务取最新N个数据的操作,如:可以将最新的10条评论的ID放在Redis的List集合里面发布、订阅消息系统地图信息分析定时器、计数器
Redis能干嘛
数据类型、基本操作和配置持久化和复制,RDB、AOF事务的控制
windows安装
1、下载获得 redis-5.0.7.tar.gz 后将它放到我们Linux的目录下 /opt2、/opt 目录下,解压命令 : tar -zxvf redis-5.0.7.tar.gz3、解压完成后出现文件夹:redis-5.0.74、进入目录: cd redis-5.0.75、在 redis-5.0.7 目录下执行 make 命令6、如果make完成后继续执行 make install7、查看默认安装目录:usr/local/bin8、拷贝配置文件(备用)
redis.conf配置文件中daemonize守护线程,默认是NO。daemonize是用来指定redis是否要用守护线程的方式启动。
linux安装
# 测试一:100个并发连接,100000个请求,检测host为localhost 端口为6379的redis服务器性能redis-benchmark -h localhost -p 6379 -c 100 -n 100000# 测试出来的所有命令只举例一个!====== SET ====== 100000 requests completed in 1.88 seconds # 对集合写入测试 100 parallel clients # 每次请求有100个并发客户端 3 bytes payload # 每次写入3个字节的数据,有效载荷keep alive: 1 # 保持一个连接,一台服务器来处理这些请求17.05% <= 1 milliseconds97.35% <= 2 milliseconds99.97% <= 3 milliseconds100.00% <= 3 milliseconds # 所有请求在 3 毫秒内完成53248.14 requests per second # 每秒处理 53248.14 次请求
redis压力测试工具-----Redis-benchmark
查看 redis.conf ,里面有默认的配置databases 16
27.0.0.1:6379> select 7OK127.0.0.1:6379[7]># 不同的库可以存不同的数据
select 切换数据库
127.0.0.1:6379> select 7OK127.0.0.1:6379[7]> DBSIZE(integer) 0127.0.0.1:6379[7]> select 0OK127.0.0.1:6379> DBSIZE(integer) 5127.0.0.1:6379> keys * # 查看具体的key1) \"counter:__rand_int__\"2) \"mylist\"3) \"k1\"4) \"myset:__rand_int__\"5) \"key:__rand_int__\"
Dbsize查看当前数据库的key的数量
127.0.0.1:6379> DBSIZE(integer) 5127.0.0.1:6379> FLUSHDBOK127.0.0.1:6379> DBSIZE(integer) 0
Flushdb:清空当前库Flushall:清空全部的库
默认16个数据库
redis 核心就是 如果我的数据全都在内存里,我单线程的去操作 就是效率最高的
Redis为什么这么快?
基础知识
Redis入门
# keys * 查看所有的key127.0.0.1:6379> keys *(empty list or set)127.0.0.1:6379> set name qinjiangOK127.0.0.1:6379> keys *1) \"name\"# exists key 的名字,判断某个key是否存在127.0.0.1:6379> EXISTS name(integer) 1127.0.0.1:6379> EXISTS name1(integer) 0# move key db ---> 当前库就没有了,被移除了127.0.0.1:6379> move name 1(integer) 1127.0.0.1:6379> keys *(empty list or set)# expire key 秒钟:为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除。# ttl key 查看还有多少秒过期,-1 表示永不过期,-2 表示已过期127.0.0.1:6379> set name qinjiangOK127.0.0.1:6379> EXPIRE name 10(integer) 1127.0.0.1:6379> ttl name(integer) 4127.0.0.1:6379> ttl name(integer) 3127.0.0.1:6379> ttl name(integer) 2127.0.0.1:6379> ttl name(integer) 1127.0.0.1:6379> ttl name(integer) -2127.0.0.1:6379> keys *(empty list or set)(empty list or set)# type key 查看你的key是什么类型127.0.0.1:6379> set name qinjiangOK127.0.0.1:6379> get name\"qinjiang\"127.0.0.1:6379> type namestring
key 的操作
Redis键(key,value)
# ===================================================# set、get、del、append、strlen# ===================================================127.0.0.1:6379> set key1 value1 # 设置值OK127.0.0.1:6379> get key1 # 获得key\"value1\"127.0.0.1:6379> del key1 # 删除key(integer) 1127.0.0.1:6379> keys * # 查看全部的key(empty list or set)127.0.0.1:6379> exists key1 # 确保 key1 不存在(integer) 0127.0.0.1:6379> append key1 \"hello\" # 对不存在的 key 进行 APPEND ,等同于 SETkey1 \"hello\"(integer) 5 # 字符长度127.0.0.1:6379> APPEND key1 \"-2333\" # 对已存在的字符串进行 APPEND(integer) 10 # 长度从 5 个字符增加到 10 个字符127.0.0.1:6379> get key1\"hello-2333\"127.0.0.1:6379> STRLEN key1 # # 获取字符串的长度(integer) 10 # ===================================================# incr、decr 一定要是数字才能进行加减,+1 和 -1。# incrby、decrby 命令将 key 中储存的数字加上指定的增量值。# ===================================================127.0.0.1:6379> set views 0 # 设置浏览量为0OK127.0.0.1:6379> incr views # 浏览 + 1(integer) 1127.0.0.1:6379> incr views # 浏览 + 1(integer) 2127.0.0.1:6379> decr views # 浏览 - 1(integer) 1127.0.0.1:6379> incrby views 10 # +10(integer) 11127.0.0.1:6379> decrby views 10 # -10(integer) 1# ===================================================# range [范围]# getrange 获取指定区间范围内的值,类似between...and的关系,从零到负一表示全部# ===================================================127.0.0.1:6379> set key2 abcd123456 # 设置key2的值OK127.0.0.1:6379> getrange key2 0 -1 # 获得全部的值\"abcd123456\"127.0.0.1:6379> getrange key2 0 2 # 截取部分字符串\"abc\"# ===================================================# setrange 设置指定区间范围内的值,格式是setrange key值 具体值# ===================================================127.0.0.1:6379> get key2\"abcd123456\"127.0.0.1:6379> SETRANGE key2 1 xx # 替换值(integer) 10127.0.0.1:6379> get key2\"axxd123456\"# ===================================================# setex(set with expire)键秒值# setnx(set if not exist)# ===================================================127.0.0.1:6379> setex key3 60 expire # 设置过期时间OK127.0.0.1:6379> ttl key3 # 查看剩余的时间(integer) 55127.0.0.1:6379> setnx mykey \"redis\" # 如果不存在就设置,成功返回1(integer) 1127.0.0.1:6379> setnx mykey \"mongodb\" # 如果存在就设置,失败返回0(integer) 0127.0.0.1:6379> get mykey\"redis\"# ===================================================# mset Mset 命令用于同时设置一个或多个 key-value 对。# mget Mget 命令返回所有(一个或多个)给定 key 的值。# 如果给定的 key 里面,有某个 key 不存在,那么这个 key 返回特殊值 nil 。# msetnx 当所有 key 都成功设置,返回 1 。# 如果所有给定 key 都设置失败(至少有一个 key 已经存在),那么返回 0 。原子操作# ===================================================127.0.0.1:6379> mset k10 v10 k11 v11 k12 v12OK127.0.0.1:6379> keys *1) \"k12\"2) \"k11\"3) \"k10\"127.0.0.1:6379> mget k10 k11 k12 k131) \"v10\"2) \"v11\"3) \"v12\"4) (nil)127.0.0.1:6379> msetnx k10 v10 k15 v15 # 原子性操作!(integer) 0127.0.0.1:6379> get key15(nil)# 传统对象缓存set user:1 value(json数据)# 可以用来缓存对象mset user:1:name zhangsan user:1:age 2mget user:1:name user:1:age# ===================================================# getset(先get再set)# ===================================================127.0.0.1:6379> getset db mongodb # 没有旧值,返回 nil(nil)127.0.0.1:6379> get db\"mongodb\"127.0.0.1:6379> getset db redis # 返回旧值 mongodb\"mongodb\"127.0.0.1:6379> get db\"redis\"
String是redis最基本的类型,你可以理解成Memcached一模一样的类型,一个key对应一个value。String类型是二进制安全的,意思是redis的string可以包含任何数据,比如jpg图片或者序列化的对象。String类型是redis最基本的数据类型,一个redis中字符串value最多可以是512M。
字符串(String)
# ===================================================# Lpush:将一个或多个值插入到列表头部。(左)# rpush:将一个或多个值插入到列表尾部。(右)# lrange:返回列表中指定区间内的元素,区间以偏移量 START 和 END 指定。# 其中 0 表示列表的第一个元素, 1 表示列表的第二个元素,以此类推。# 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。# ===================================================127.0.0.1:6379> LPUSH list \"one\"(integer) 1127.0.0.1:6379> LPUSH list \"two\"(integer) 2127.0.0.1:6379> RPUSH list \"right\"(integer) 3127.0.0.1:6379> Lrange list 0 -11) \"two\"2) \"one\"3) \"right\"127.0.0.1:6379> Lrange list 0 11) \"two\"2) \"one\"# ===================================================# lpop 命令用于移除并返回列表的第一个元素。当列表 key 不存在时,返回 nil 。# rpop 移除列表的最后一个元素,返回值为移除的元素。# ===================================================127.0.0.1:6379> Lpop list\"two\"127.0.0.1:6379> Rpop list\"right\"127.0.0.1:6379> Lrange list 0 -11) \"one\"# ===================================================# Lindex,按照索引下标获得元素(-1代表最后一个,0代表是第一个)# ===================================================127.0.0.1:6379> Lindex list 1(nil)127.0.0.1:6379> Lindex list 0\"one\"127.0.0.1:6379> Lindex list -1\"one\"# ===================================================# llen 用于返回列表的长度。# ===================================================127.0.0.1:6379> flushdbOK127.0.0.1:6379> Lpush list \"one\"(integer) 1127.0.0.1:6379> Lpush list \"two\"(integer) 2127.0.0.1:6379> Lpush list \"three\"(integer) 3127.0.0.1:6379> Llen list # 返回列表的长度(integer) 3# ===================================================# lrem key 根据参数 COUNT 的值,移除列表中与参数 VALUE 相等的元素。# ===================================================127.0.0.1:6379> lrem list 1 \"two\"(integer) 1127.0.0.1:6379> Lrange list 0 -11) \"three\"2) \"one\"# ===================================================# Ltrim key 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。# ===================================================127.0.0.1:6379> RPUSH mylist \"hello\"(integer) 1127.0.0.1:6379> RPUSH mylist \"hello\"(integer) 2127.0.0.1:6379> RPUSH mylist \"hello2\"(integer) 3127.0.0.1:6379> RPUSH mylist \"hello3\"(integer) 4127.0.0.1:6379> ltrim mylist 1 2OK127.0.0.1:6379> lrange mylist 0 -11) \"hello\"2) \"hello2\"# ===================================================# rpoplpush 移除列表的最后一个元素,并将该元素添加到另一个列表并返回。# ===================================================127.0.0.1:6379> rpush mylist \"hello\"(integer) 1127.0.0.1:6379> rpush mylist \"foo\"(integer) 2127.0.0.1:6379> rpush mylist \"bar\"(integer) 3127.0.0.1:6379> rpoplpush mylist myotherlist\"bar\"127.0.0.1:6379> lrange mylist 0 -11) \"hello\"2) \"foo\"127.0.0.1:6379> lrange myotherlist 0 -11) \"bar\"# ===================================================# lset key index value 将列表 key 下标为 index 的元素的值设置为 value 。# ===================================================127.0.0.1:6379> exists list # 对空列表(key 不存在)进行 LSET(integer) 0127.0.0.1:6379> lset list 0 item # 报错(error) ERR no such key127.0.0.1:6379> lpush list \"value1\" # 对非空列表进行 LSET(integer) 1127.0.0.1:6379> lrange list 0 01) \"value1\"127.0.0.1:6379> lset list 0 \"new\" # 更新值OK127.0.0.1:6379> lrange list 0 01) \"new\"127.0.0.1:6379> lset list 1 \"new\" # index 超出范围报错(error) ERR index out of range# ===================================================# linsert key before/after pivot value 用于在列表的元素前或者后插入元素。# 将值 value 插入到列表 key 当中,位于值 pivot 之前或之后。# ===================================================redis> RPUSH mylist \"Hello\"(integer) 1redis> RPUSH mylist \"World\"(integer) 2redis> LINSERT mylist BEFORE \"World\" \"There\"(integer) 3redis> LRANGE mylist 0 -11) \"Hello\"2) \"There\"3) \"World\"
单值多value
Redis列表是简单的字符串列表,按照插入顺序排序,你可以添加一个元素到列表的头部(左边)或者尾部(右边)。它的底层实际是
列表(List)
# ===================================================# sadd 将一个或多个成员元素加入到集合中,不能重复# smembers 返回集合中的所有的成员。# sismember 命令判断成员元素是否是集合的成员。# ===================================================127.0.0.1:6379> sadd myset \"hello\"(integer) 1127.0.0.1:6379> sadd myset \"kuangshen\"(integer) 1127.0.0.1:6379> sadd myset \"kuangshen\"(integer) 0127.0.0.1:6379> SMEMBERS myset1) \"kuangshen\"2) \"hello\"127.0.0.1:6379> SISMEMBER myset \"hello\"(integer) 1127.0.0.1:6379> SISMEMBER myset \"world\"(integer) 0# ===================================================# scard,获取集合里面的元素个数# ===================================================127.0.0.1:6379> scard myset(integer) 2# ===================================================# srem key value 用于移除集合中的一个或多个成员元素# ===================================================127.0.0.1:6379> srem myset \"kuangshen\"(integer) 1127.0.0.1:6379> SMEMBERS myset1) \"hello\"# ===================================================# srandmember key 命令用于返回集合中的一个随机元素。# ===================================================127.0.0.1:6379> SMEMBERS myset1) \"kuangshen\"2) \"world\"3) \"hello\"127.0.0.1:6379> SRANDMEMBER myset\"hello\"127.0.0.1:6379> SRANDMEMBER myset 21) \"world\"2) \"kuangshen\"127.0.0.1:6379> SRANDMEMBER myset 21) \"kuangshen\"2) \"hello\"# ===================================================# spop key 用于移除集合中的指定 key 的一个或多个随机元素# ===================================================127.0.0.1:6379> SMEMBERS myset1) \"kuangshen\"2) \"world\"3) \"hello\"127.0.0.1:6379> spop myset\"world\"127.0.0.1:6379> spop myset\"kuangshen\"127.0.0.1:6379> spop myset\"hello\"# ===================================================# smove SOURCE DESTINATION MEMBER# 将指定成员 member 元素从 source 集合移动到 destination 集合。# ===================================================
Redis的Set是String类型的无序集合,它是通过HashTable实现的 !
集合(set)
127.0.0.1:6379> hexists myhash field1(integer) 1127.0.0.1:6379> hexists myhash field3(integer) 0# ===================================================# hkeys 获取哈希表中的所有域(field)。# hvals 返回哈希表所有域(field)的值。# ===================================================127.0.0.1:6379> HKEYS myhash1) \"field2\"2) \"field1\"127.0.0.1:6379> HVALS myhash1) \"World\"2) \"Hello\"# ===================================================# hincrby 为哈希表中的字段值加上指定增量值。# ===================================================127.0.0.1:6379> hset myhash field 5(integer) 1127.0.0.1:6379> HINCRBY myhash field 1(integer) 6127.0.0.1:6379> HINCRBY myhash field -1(integer) 5127.0.0.1:6379> HINCRBY myhash field -10(integer) -5# ===================================================# hsetnx 为哈希表中不存在的的字段赋值 。# ===================================================127.0.0.1:6379> HSETNX myhash field1 \"hello\"(integer) 1 # 设置成功,返回 1 。127.0.0.1:6379> HSETNX myhash field1 \"world\"(integer) 0 # 如果给定字段已经存在,返回 0 。127.0.0.1:6379> HGET myhash field1\"hello\"
# ===================================================# hset、hget 命令用于为哈希表中的字段赋值 。# hmset、hmget 同时将多个field-value对设置到哈希表中。会覆盖哈希表中已存在的字段。# hgetall 用于返回哈希表中,所有的字段和值。# hdel 用于删除哈希表 key 中的一个或多个指定字段# ===================================================127.0.0.1:6379> hset myhash field1 \"kuangshen\"(integer) 1127.0.0.1:6379> hget myhash field1\"kuangshen\"127.0.0.1:6379> HMSET myhash field1 \"Hello\" field2 \"World\"OK127.0.0.1:6379> HGET myhash field1\"Hello\"127.0.0.1:6379> HGET myhash field2\"World\"127.0.0.1:6379> hgetall myhash1) \"field1\"2) \"Hello\"3) \"field2\"4) \"World\"127.0.0.1:6379> HDEL myhash field1(integer) 1127.0.0.1:6379> hgetall myhash1) \"field2\"2) \"World\"# ===================================================# hlen 获取哈希表中字段的数量。# ===================================================127.0.0.1:6379> hlen myhash(integer) 1127.0.0.1:6379> HMSET myhash field1 \"Hello\" field2 \"World\"OK127.0.0.1:6379> hlen myhash(integer) 2# ===================================================# hexists 查看哈希表的指定字段是否存在。# ===================================================
Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。存储部分变更的数据,如用户信息等。
哈希(Hash)
# ===================================================# zadd 将一个或多个成员元素及其分数值加入到有序集当中。# zrange 返回有序集中,指定区间内的成员# ===================================================127.0.0.1:6379> zadd myset 1 \"one\"(integer) 1127.0.0.1:6379> zadd myset 2 \"two\" 3 \"three\"(integer) 2127.0.0.1:6379> ZRANGE myset 0 -11) \"one\"2) \"two\"3) \"three\
Redis zset 和 set 一样,也是String类型元素的集合,且不允许重复的成员
有序集合(Zset)
五大数据类型
127.0.0.1:6379> geoadd china:city 116.23 40.22 北京(integer) 1127.0.0.1:6379> geoadd china:city 121.48 31.40 上海 113.88 22.55 深圳 120.2130.20 杭州(integer) 3127.0.0.1:6379> geoadd china:city 106.54 29.40 重庆 108.93 34.23 西安 114.0230.58 武汉(integer) 3
geoadd
127.0.0.1:6379> geopos china:city 北京1) 1) \"116.23000055551528931\" 2) \"40.2200010338739844\"127.0.0.1:6379> geopos china:city 上海 重庆1) 1) \"121.48000091314315796\" 2) \"31.40000025319353938\"2) 1) \"106.54000014066696167\" 2) \"29.39999880018641676\"127.0.0.1:6379> geopos china:city 新疆1) (nil)
geopos
127.0.0.1:6379> geodist china:city 北京 上海\"1088785.4302\"127.0.0.1:6379> geodist china:city 北京 上海 km\"1088.7854\"127.0.0.1:6379> geodist china:city 重庆 北京 km\"1491.6716\"
geodist
重新连接 redis-cli,增加参数 --raw ,可以强制输出中文,不然会乱码
[root@kuangshen bin]# redis-cli --raw -p 6379# 在 china:city 中寻找坐标 100 30 半径为 1000km 的城市127.0.0.1:6379> georadius china:city 100 30 1000 km重庆西安# withdist 返回位置名称和中心距离127.0.0.1:6379> georadius china:city 100 30 1000 km withdist重庆635.2850西安963.3171# withcoord 返回位置名称和经纬度127.0.0.1:6379> georadius china:city 100 30 1000 km withcoord重庆106.5400001406669616729.39999880018641676西安108.9299985766410827634.23000121926852302# withdist withcoord 返回位置名称 距离 和经纬度 count 限定寻找个数127.0.0.1:6379> georadius china:city 100 30 1000 km withcoord withdist count1重庆635.2850106.5400001406669616729.39999880018641676127.0.0.1:6379> georadius china:city 100 30 1000 km withcoord withdist count2重庆635.2850106.5400001406669616729.39999880018641676西安963.3171108.9299985766410827634.23000121926852302
georadius
127.0.0.1:6379> GEORADIUSBYMEMBER china:city 北京 1000 km北京西安127.0.0.1:6379> GEORADIUSBYMEMBER china:city 上海 400 km杭州上海
georadiusbymember
127.0.0.1:6379> geohash china:city 北京 重庆wx4sucu47r0wm5z22h53v0127.0.0.1:6379> geohash china:city 北京 上海wx4sucu47r0wtw6sk5n300
geohash
127.0.0.1:6379> geoadd china:city 116.23 40.22 beijin1127.0.0.1:6379> zrange china:city 0 -1 # 查看全部的元素重庆西安深圳武汉杭州上海beijin北京127.0.0.1:6379> zrem china:city beijin # 移除元素1127.0.0.1:6379> zrem china:city 北京 # 移除元素1127.0.0.1:6379> zrange china:city 0 -1重庆西安深圳武汉杭州上海
zrem
GEO 的数据结构总共有六个常用命令:geoadd、geopos、geodist、georadius、georadiusbymember、gethash
GEO地理位置
Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的
HyperLogLog则是一种算法,它提供了不精确的去重计数方案。
127.0.0.1:6379> PFADD mykey a b c d e f g h i j1127.0.0.1:6379> PFCOUNT mykey10127.0.0.1:6379> PFADD mykey2 i j z x c v b n m1127.0.0.1:6379> PFMERGE mykey3 mykey mykey2OK127.0.0.1:6379> PFCOUNT mykey315
什么是基数?
HyperLogLog
Bitmap 就是通过操作二进制位来进行记录,即为 0 和 1
# 使用 bitmap 来记录上述事例中一周的打卡记录如下所示:# 周一:1,周二:0,周三:0,周四:1,周五:1,周六:0,周天:0 (1 为打卡,0 为不打卡)127.0.0.1:6379> setbit sign 0 10127.0.0.1:6379> setbit sign 1 00127.0.0.1:6379> setbit sign 2 00127.0.0.1:6379> setbit sign 3 10127.0.0.1:6379> setbit sign 4 10127.0.0.1:6379> setbit sign 5 00127.0.0.1:6379> setbit sign 6 0
setbit
127.0.0.1:6379> getbit sign 3 # 查看周四是否打卡1127.0.0.1:6379> getbit sign 6 # 查看周七是否打卡0
getbit
# 统计这周打卡的记录,可以看到只有3天是打卡的状态:127.0.0.1:6379> bitcount sign3
bitcount统计操作
操作
BitMap
三种特殊数据类型
Redis 的配置文件位于 Redis 安装目录下,文件名为 redis.conf
1、配置大小单位,开头定义了一些基本的度量单位,只支持bytes,不支持bit2、对 大小写 不敏感
unit单位
bind 127.0.0.1 # 绑定的ipprotected-mode yes # 保护模式port 6379 # 默认端口
NETWORK 网络配置
daemonize yes # 默认情况下,Redis不作为守护进程运行。需要开启的话,改为 yessupervised no # 可通过upstart和systemd管理Redis守护进程pidfile /var/run/redis_6379.pid # 以后台进程方式运行redis,则需要指定pid 文件loglevel notice # 日志级别。可选项有: # debug(记录大量日志信息,适用于开发、测试阶段); # verbose(较多日志信息) # notice(适量日志信息,使用于生产环境); # warning(仅有部分重要、关键信息才会被记录)。logfile \"\" # 日志文件的位置,当指定为空字符串时,为标准输出databases 16 # 设置数据库的数目。默认的数据库是DB 0always-show-logo yes # 是否总是显示logo
GENERAL 通用
# 900秒(15分钟)内至少1个key值改变(则进行数据库保存--持久化)save 900 1# 300秒(5分钟)内至少10个key值改变(则进行数据库保存--持久化)save 300 10# 60秒(1分钟)内至少10000个key值改变(则进行数据库保存--持久化)save 60 10000stop-writes-on-bgsave-error yes # 持久化出现错误后,是否依然进行继续进行工作rdbcompression yes # 使用压缩rdb文件 yes:压缩,但是需要一些cpu的消耗。no:不压缩,需要更多的磁盘空间rdbchecksum yes # 是否校验rdb文件,更有利于文件的容错性,但是在保存rdb文件的时候,会有大概10%的性能损耗dbfilename dump.rdb # dbfilenamerdb文件名称dir ./ # dir 数据目录,数据库的写入会在这个目录。rdb、aof文件也会写在这个目录
SNAPSHOPTING快照
# 启动redis# 连接客户端# 获得和设置密码config get requirepassconfig set requirepass \"123456\"#测试ping,发现需要验证127.0.0.1:6379> pingNOAUTH Authentication required.# 验证127.0.0.1:6379> auth 123456OK127.0.0.1:6379> pingPONG
SECURITY安全
appendonly no # 是否以append only模式作为持久化方式,默认使用的是rdb方式持久化,这种方式在许多应用中已经足够用了appendfilename \"appendonly.aof\" # appendfilename AOF 文件名称appendfsync everysec # appendfsync aof持久化策略的配置 # no表示不执行fsync,由操作系统保证数据同步到磁盘,速度最快。 # always表示每次写入都执行fsync,以保证数据同步到磁盘。 # everysec表示每秒执行一次fsync,可能会导致丢失这1s数据。
applyonly
maxclients 10000 # 设置能连上redis的最大客户端连接数量maxmemory <bytes> # redis配置的最大内存容量maxmemory-policy noeviction # maxmemory-policy 内存达到上限的处理策略 #volatile-lru:利用LRU算法移除设置过过期时间的key。 #volatile-random:随机移除设置过过期时间的key。 #volatile-ttl:移除即将过期的key,根据最近过期时间来删除(辅以TTL) #allkeys-lru:利用LRU算法移除任何key。 #allkeys-random:随机移除任何key。 #noeviction:不移除任何key,只是返回一个写错误。
限制
熟悉基本配置
daemonize no
Redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程
pidfile /var/run/redis.pid
当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,可以通过pidfile指定
port 6379
指定Redis监听端口,默认端口为6379,作者在自己的一篇博文中解释了为什么选用6379作为默认端口,因为6379在手机按键上MERZ对应的号码,而MERZ取自意大利歌女Alessia Merz的名字
bind 127.0.0.1
绑定的主机地址
timeout 300
当 客户端闲置多长时间后关闭连接,如果指定为0,表示关闭该功能
loglevel verbose
指定日志记录级别,Redis总共支持四个级别:debug、verbose、notice、warning,默认为verbose
logfile stdout
日志记录方式,默认为标准输出,如果配置Redis为守护进程方式运行,而这里又配置为日志记录方式为标准输出,则日志将会发送给/dev/null
database 16
设置数据库的数量,默认数据库为0,可以使用SELECT 命令在连接上指定数据库id
saveRedis默认配置文件中提供了三个条件:save 900 1save 300 10save 60 10000分别表示900秒(15分钟)内有1个更改,300秒(5分钟)内有10个更改以及60秒内有10000个更改。
指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合
rdbcompression yes
指定存储至本地数据库时是否压缩数据,默认为yes,Redis采用LZF压缩,如果为了节省CPU时间,可以关闭该选项,但会导致数据库文件变的巨大
dbfilename dump.rdb
指定本地数据库文件名,默认值为dump.rdb
dir ./
指定本地数据库存放目录
slaveof
设置当本机为slav服务时,设置master服务的IP地址及端口,在Redis启动时,它会自动从master进行数据同步
master auth
当master服务设置了密码保护时,slav服务连接master的密码
requirepass foobared
设置Redis连接密码,如果配置了连接密码,客户端在连接Redis时需要通过AUTH 命令提供密码,默认关闭
maxclients 128
设置同一时间最大客户端连接数,默认无限制,Redis可以同时打开的客户端连接数为Redis进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis会关闭新的连接并向客户端返回max number of clients reached错误信息
maxmemory
指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key,当此方法处理 后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis新的vm机制,会把Key存放内存,Value会存放在swap区
appendonly no
指定是否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为 redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为no
appendfilename appendonly.aof
指定更新日志文件名,默认为appendonly.aof
no:表示等操作系统进行数据缓存同步到磁盘(快)always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全)everysec:表示每秒同步一次(折衷,默认值)appendfsync everysec
指定更新日志条件,共有3个可选值:
vm-enabled no
指定是否启用虚拟内存机制,默认值为no,简单的介绍一下,VM机制将数据分页存放,由Redis将访问量较少的页即冷数据swap到磁盘上,访问多的页面由磁盘自动换出到内存中
vm-swap-file /tmp/redis.swap
虚拟内存文件路径,默认值为/tmp/redis.swap,不可多个Redis实例共享
vm-max-memory 0
vm-page-size 32
Redis swap文件分成了很多的page,一个对象可以保存在多个page上面,但一个page上不能被多个对象共享,vm-page-size是要根据存储的 数据大小来设定的,作者建议如果存储很多小对象,page大小最好设置为32或者64bytes;如果存储很大大对象,则可以使用更大的page,如果不 确定,就使用默认值
vm-pages 134217728
设置swap文件中的page数量,由于页表(一种表示页面空闲或使用的bitmap)是在放在内存中的,,在磁盘上每8个pages将消耗1byte的内存。
vm-max-threads 4
glueoutputbuf yes
设置在向客户端应答时,是否把较小的包合并为一个包发送,默认为开启
hash-max-zipmap-entries 64hash-max-zipmap-value 512
指定在超过一定的数量或者最大的元素超过某一临界值时,采用一种特殊的哈希算法
activerehashing yes
指定是否激活重置哈希,默认为开启
include /path/to/local.conf
指定包含其它的配置文件,可以在同一主机上多个Redis实例之间使用同一份配置文件,而同时各个实例又拥有自己的特定配置文件
常用配置介绍
Redis.conf
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里。
Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的。这就确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。
Rdb 保存的是 dump.rdb 文件
如果想禁用RDB持久化的策略,只要不设置任何save指令,或者给save传入一个空字符串参数也可以
127.0.0.1:6379> config get dirdir/usr/local/bin
1、将备份文件(dump.rdb)移动到redis安装目录并启动服务即可2、CONFIG GET dir 获取目录
如何恢复
1、适合大规模的数据恢复2、对数据完整性和一致性要求不高
1、在一定间隔时间做一次备份,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改2、Fork的时候,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑。
RDB(Redis Database)
以日志的形式来记录每个写操作,将Redis执行过的所有指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据AOF保存的是appendonly.aof
appendonly no # 是否以append only模式作为持久化方式,默认使用的是rdb方式持久化,这种方式在许多应用中已经足够用了appendfilename \"appendonly.aof\" # appendfilename AOF 文件名称appendfsync everysec # appendfsync aof持久化策略的配置 # no表示不执行fsync,由操作系统保证数据同步到磁盘,速度最快。 # always表示每次写入都执行fsync,以保证数据同步到磁盘。 # everysec表示每秒执行一次fsync,可能会导致丢失这1s数据。No-appendfsync-on-rewrite #重写时是否可以运用Appendfsync,用默认no即可,保证数据安全性Auto-aof-rewrite-min-size # 设置重写的基准值Auto-aof-rewrite-percentage #设置重写的基准值
正常恢复:启动:设置Yes,修改默认的appendonly no,改为yes将有数据的aof文件复制一份保存到对应目录(config get dir)恢复:重启redis然后重新加载异常恢复:启动:设置Yes故意破坏 appendonly.aof 文件!修复: redis-check-aof --fix appendonly.aof 进行修复恢复:重启 redis 然后重新加载
AOF恢复
AOF 采用文件追加方式,文件会越来越大,为避免出现此种情况,新增了重写机制,当AOF文件的大小超过所设定的阈值时,Redis 就会启动AOF 文件的内容压缩,只保留可以恢复数据的最小指令集,可以使用命令 bgrewriteaof !
是什么
AOF 文件持续增长而过大时,会增加出一条新进程来将文件重写(也是先写临时文件最后再rename),遍历新进程的内存中数据,每条记录有一条的Set语句。重写aof文件的操作并没有读取旧的aof文件,这点和快照有点类似!
重写原理
Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M的触发。
触发机制
Rewrite
1、每修改同步:appendfsync always 同步持久化,每次发生数据变更会被立即记录到磁盘,性能较差但数据完整性比较好2、每秒同步: appendfsync everysec 异步操作,每秒记录 ,如果一秒内宕机,有数据丢失3、不同步: appendfsync no 从不同步
1、相同数据集的数据而言,aof 文件要远大于 rdb文件,恢复速度慢于 rdb。2、Aof 运行效率要慢于 rdb,每秒同步策略效率较好,不同步效率和rdb相同
AOF(Append ONLY File)
1、RDB 持久化方式能够在指定的时间间隔内对你的数据进行快照存储2、AOF 持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以Redis 协议追加保存每次写的操作到文件末尾,Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大。3、只做缓存,如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化4、同时开启两种持久化方式在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。RDB 的数据不实时,同时使用两者时服务器重启也只会找AOF文件,那要不要只使用AOF呢?作者建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),快速重启,而且不会有AOF可能潜在的Bug,留着作为一个万一的手段。
因为RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留 save 900 1 这条规则。如果Enable AOF ,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了,代价一是带来了持续的IO,二是AOF rewrite 的最后将 rewrite 过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上,默认超过原大小100%大小重写可以改到适当的数值。如果不Enable AOF ,仅靠 Master-Slave Repllcation 实现高可用性也可以,能省掉一大笔IO,也减少了rewrite时带来的系统波动。代价是如果Master/Slave 同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个 Master/Slave 中的 RDB文件,载入较新的那个,微博就是这种架构。
性能建议
Redis的持久化
Redis事务的概念:Redis 事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。总结说:redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令
批量操作在发送 EXEC 命令前被放入队列缓存,并不会被实际执行!
Redis事务没有隔离级别的概念:
Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。
Redis不保证原子性
开始事务命令入队执行事务
Redis事务的三个阶段
Redis事务相关命令
理论
MULTI-->开启事务set k1 v1set k2 v2get k2set k3 v3exec-->执行事务
正常执行
MULTI-->开启事务set k1 v1set k2 v2DISCARDget k2
放弃事务
若在事务队列中存在命令性错误(类似于java编译性错误),则执行EXEC命令时,所有命令都不会执行
若在事务队列中存在语法性错误(类似于java的1/0的运行时异常),则执行EXEC命令时,其他正确命令会被执行,错误命令抛出异常。
悲观锁
乐观锁
127.0.0.1:6379> set balance 100OK127.0.0.1:6379> set debt 0OK
1、初始化信用卡可用余额和欠额
127.0.0.1:6379> watch balanceOK127.0.0.1:6379> MULTIOK127.0.0.1:6379> decrby balance 20QUEUED127.0.0.1:6379> incrby debt 20QUEUED127.0.0.1:6379> exec1) (integer) 802) (integer) 20
2、使用watch检测balance,事务期间balance数据未变动,事务执行成功
# 窗口一:出现问题后放弃监视,然后重来!127.0.0.1:6379> UNWATCH # 放弃监视OK127.0.0.1:6379> watch balanceOK127.0.0.1:6379> MULTIOK127.0.0.1:6379> decrby balance 20QUEUED127.0.0.1:6379> incrby debt 20QUEUED127.0.0.1:6379> exec # 成功!1) (integer) 1802) (integer) 40
# 窗口一127.0.0.1:6379> watch balanceOK127.0.0.1:6379> MULTI # 执行完毕后,执行窗口二代码测试OK127.0.0.1:6379> decrby balance 20QUEUED127.0.0.1:6379> incrby debt 20QUEUED127.0.0.1:6379> exec # 修改失败!(nil)# 窗口二127.0.0.1:6379> get balance\"80\"127.0.0.1:6379> set balance 200OK
3、使用watch检测balance,事务期间balance数据变动,事务执行失败!
Watch 监控
实践
watch指令类似于乐观锁,在事务提交时,如果watch监控的多个KEY中任何KEY的值已经被其他客户端更改,则使用EXEC执行事务时,事务队列将不会被执行,同时返回Nullmulti-bulk应答以通知调用者事务执行失败。
Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。Redis 客户端可以订阅任意数量的频道。
redis 127.0.0.1:6379> SUBSCRIBE redisChatReading messages... (press Ctrl-C to quit)1) \"subscribe\"2) \"redisChat\"3) (integer) 1
创建了订阅频道名为 redisChat:
redis 127.0.0.1:6379> PUBLISH redisChat \
开启个 redis 客户端,然后在同一个频道 redisChat 发布两次消息,订阅者就能接收到消息
通过 SUBSCRIBE 命令订阅某频道后,redis-server 里维护了一个字典,字典的键就是一个个 channel,而字典的值则是一个链表,链表中保存了所有订阅这个 channel 的客户端。SUBSCRIBE 命令的关键,就是将客户端添加到给定 channel 的订阅链表中。
通过 PUBLISH 命令向订阅者发送消息,redis-server 会使用给定的频道作为键,在它所维护的 channel字典中查找记录了订阅这个频道的所有客户端的链表,遍历这个链表,将消息发布给所有订阅者。
Pub/Sub构建实时消息系统Redis的Pub/Sub系统可以构建实时的消息系统比如很多用Pub/Sub构建的实时聊天系统的例子
发布订阅
Redis事务
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master/leader),后者称为从节点(slave/follower);数据的复制是单向的,只能由主节点到从节点。Master以写为主,Slave 以读为主。
默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。
1、数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
2、故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
3、负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
4、高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。
每次与 master 断开之后,都需要重新连接,除非你配置进 redis.conf 文件!
配从库不配主库,从库配置slaveof 主库ip 主库端口 # 配置主从Info replication # 查看信息
基本配置
1、拷贝每一个redis的redis.conf 文件
修改配置文件
环境配置
测试一:主机挂了,查看从机信息,主机恢复,再次查看信息
测试二:从机挂了,查看主机信息,从机恢复,查看从机信息
上一个Slave 可以是下一个slave 和 Master,Slave 同样可以接收其他 slaves 的连接和同步请求,那么该 slave 作为了链条中下一个的master,可以有效减轻 master 的写压力
测试:6379 设置值以后 6380 和 6381 都可以获取到!OK
层层链路
一主二从的情况下,如果主机断了,从机可以使用命令 SLAVEOF NO ONE 将自己改为主机!这个时候其余的从机链接到这个节点。对一个从属服务器执行命令 SLAVEOF NO ONE 将使得这个从属服务器关闭复制功能,并从从属服务器转变回主服务器,原来同步所得的数据集不会被丢弃。
主机再回来,也只是一个光杆司令了,从机为了正常使用跑到了新的主机上!
谋朝篡位
Slave 启动成功连接到 master 后会发送一个sync命令Master 接到命令,启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后,master将传送整个数据文件到slave,并完成一次完全同步。全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。增量复制:Master 继续将新的所有收集到的修改命令依次传给slave,完成同步但是只要是重新连接master,一次完全同步(全量复制)将被自动执行
复制原理
一主二从
主从切换技术的方法是:当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。Redis从2.8开始正式提供了Sentinel(哨兵) 架构来解决这个问题
哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例
通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。
假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover[故障转移]操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线。
多哨兵
1、调整结构,6379带着80、812、自定义的 /myredis 目录下新建 sentinel.conf 文件,名字千万不要错3、配置哨兵,填写内容sentinel monitor 被监控主机名字 127.0.0.1 6379 1上面最后一个数字1,表示主机挂掉后slave投票看让谁接替成为主机,得票数多少后成为主机4、启动哨兵Redis-sentinel /myredis/sentinel.conf上述目录依照各自的实际情况配置,可能目录不同5、正常主从演示6、原有的Master 挂了7、投票新选8、重新主从继续开工,info replication 查查看
问题:如果之前的master 重启回来,会不会双master 冲突? 之前的回来只能做小弟了
问题?
1. 哨兵集群模式是基于主从模式的,所有主从的优点,哨兵模式同样具有。2. 主从可以切换,故障可以转移,系统可用性更好。3. 哨兵模式是主从模式的升级,系统更健壮,可用性更高。
1. Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。2. 实现哨兵模式的配置也不简单,甚至可以说有些繁琐
# Example sentinel.conf# 哨兵sentinel实例运行的端口 默认26379port 26379# 哨兵sentinel的工作目录dir /tmp# 哨兵sentinel监控的redis主节点的 ip port# master-name 可以自己命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符\".-_\
哨兵配置说明
哨兵模式
Redis主从复制
缓存穿透的概念很简单,用户想要查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中,于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透。
布隆过滤器是一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的查询压力
当存储层不命中后,即使返回的空对象也将其缓存起来,同时会设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护了后端数据源;
1、如果空值能够被缓存起来,这就意味着缓存需要更多的空间存储更多的键,因为这当中可能会有很多的空值的键;2、即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务会有影响。
存在问题:
缓存空对象
缓存穿透
这里需要注意和缓存击穿的区别,缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。当某个key在过期的瞬间,有大量的请求并发访问,这类数据一般是热点数据,由于缓存过期,会同时访问数据库来查询最新数据,并且回写缓存,会导使数据库瞬间压力过大。
从缓存层面来看,没有设置过期时间,所以不会出现热点 key 过期后产生的问题。
设置热点数据永不过期
分布式锁:使用分布式锁,保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可。这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大。
加互斥锁
缓存击穿
缓存雪崩,是指在某一个时间段,缓存集中过期失效。
其实集中过期,倒不是非常致命,比较致命的缓存雪崩,是缓存服务器某个节点宕机或断网。因为自然形成的缓存雪崩,一定是在某个时间段集中创建缓存,这个时候,数据库也是可以顶住压力的。无非就是对数据库产生周期性的压力而已。而缓存服务节点的宕机,对数据库服务器造成的压力是不可预知的,很有可能瞬间就把数据库压垮。
redis高可用
这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
限流降级
数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
数据预热
缓存雪崩
缓存穿透和雪崩
<!-- https://mvnrepository.com/artifact/redis.clients/jedis --><dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.2.0</version></dependency><dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.58</version></dependency>
1、新建一个普通的Maven项目2、导入redis的依赖
import redis.clients.jedis.Jedis;public class Ping { public static void main(String[] args) { Jedis jedis = new Jedis(\"127.0.0.1\
3、编写测试代码
连接成功服务正在运行: PONG
4、启动redis服务5、启动测试,结果
测试联通
public class TestPassword { public static void main(String[] args) { Jedis jedis = new Jedis(\"127.0.0.1\
public class TestKey { public static void main(String[] args) { Jedis jedis = new Jedis(\"127.0.0.1\
对key操作的命令
public class TestString { public static void main(String[] args) { Jedis jedis = new Jedis(\"127.0.0.1\
对String操作的命令
public class TestList { public static void main(String[] args) { Jedis jedis = new Jedis(\"127.0.0.1\
对List
public class TestSet { public static void main(String[] args) { Jedis jedis = new Jedis(\"127.0.0.1\
对set操作的命令
public class TestHash { public static void main(String[] args) { Jedis jedis = new Jedis(\"127.0.0.1\
对Hash操作
import com.alibaba.fastjson.JSONObject;import redis.clients.jedis.Jedis;import redis.clients.jedis.Transaction;public class TestMulti { public static void main(String[] args) { //创建客户端连接服务端,redis服务端需要被开启 Jedis jedis = new Jedis(\"127.0.0.1\
Jedis
1、 JedisPoolConfig (这个是配置连接池)
JredisConnectionFactoryJedisConnectionFactoryLettuceConnectionFactorySrpConnectionFactory
2、 RedisConnectionFactory 这个是配置连接信息,这里的RedisConnectionFactory是一个接口,我们需要使用它的实现类,在SpringData Redis方案中提供了以下四种工厂模型
//1.导入依赖<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency>
//2.yaml基本配置spring: redis: host: 127.0.0.1 port: 6379 password: 123456 jedis: pool: max-active: 8 max-wait: -1ms max-idle: 500 min-idle: 0 lettuce: shutdown-timeout: 0ms
3、RedisTemplate 基本操作
基础使用
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency>
1、新建一个SpringBoot项目2、导入redis的启动器
# Redis服务器地址spring.redis.host=127.0.0.1# Redis服务器连接端口spring.redis.port=6379
3、配置redis,可以查看 RedisProperties 分析
4、分析 RedisAutoConfiguration 自动配置类
import com.fasterxml.jackson.annotation.JsonAutoDetect;import com.fasterxml.jackson.annotation.PropertyAccessor;import com.fasterxml.jackson.databind.ObjectMapper;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;import org.springframework.data.redis.serializer.StringRedisSerializer;@Configurationpublic class RedisConfig { @Bean @SuppressWarnings(\"all\
5、既然自动配置不好用,就重新配置一个RedisTemplate
链接:https://pan.baidu.com/s/19uaB7IjUxKLfnNVarUd8Xg 提取码:p8mv
6、写一个Redis工具类(直接用RedisTemplate操作Redis,需要很多行代码,因此直接封装好一个RedisUtils,这样写代码更方便点。这个RedisUtils交给Spring容器实例化,使用时直接注解注入。
封装工具类
Springboot整合
redis
常遇到的问题
诞生于 2013 年初,基于 Go 语言实现, dotCloud 公司出品(后改名为Docker Inc) • Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的Linux 机器上
容器是完全使用沙箱机制,相互隔离• 容器性能开销极低。• Docker 从 17.03 版本之后分为 CE(Community Edition: 社区版) 和 EE(Enterprise Edition: 企业版)
Docker 是一个开源的应用容器引擎
docker概念
Docker可以运行在MAC、Windows、CentOS、UBUNTU等操作系统上,本课程基于CentOS 7 安装Docker。官网:https://www.docker.com
安装docker
默认情况下,将来从docker hub(https://hub.docker.com/)上下载docker镜像,太慢。一般都会配置镜像加速器:• USTC:中科大镜像加速器(https://docker.mirrors.ustc.edu.cn) • 阿里云• 网易云• 腾讯云
配置 Docker 镜像加速器
• 镜像(Image):Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:16.04 就包含了完整的一套 Ubuntu16.04 最小系统的 root 文件系统。• 容器(Container):镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和对象一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。• 仓库(Repository):仓库可看成一个代码控制中心,用来保存镜像。
docker架构
初识docker
systemctl start docker
• 启动docker服务
systemctl stop docker
• 停止docker服务
systemctl restart docker
• 重启docker服务
systemctl status docker
• 查看docker服务状态
systemctl enable docker
• 开机启动docker服务
进程相关命令
docker imagesdocker images –q # 查看所用镜像的id
• 查看镜像
docker search 镜像名称
• 搜索镜像
从Docker仓库下载镜像到本地,镜像名称格式为 名称:版本号,如果版本号不指定则是最新的版本。如果不知道镜像版本,可以去docker hub 搜索对应镜像查看docker pull 镜像名称
• 拉取镜像
• 删除镜像
镜像相关命令
docker ps # 查看正在运行的容器docker ps –a # 查看所有容器
查看
docker run 参数
创建
docker exec 参数 # 退出容器,容器不会关闭
进入
docker start 容器名称
docker stop 容器名称
停止
docker rm 容器名称
docker inspect 容器名称
查看容器信息
容器相关命令
Docker命令
Docker 容器删除后,在容器中产生的数据还在吗?Docker 容器和外部机器可以直接交换文件吗?
思考
数据卷是宿主机中的一个目录或文件当容器目录和数据卷目录绑定后,对方的修改会立即同步一个数据卷可以被多个容器同时挂载一个容器也可以被挂载多个数据卷
数据卷作用• 容器数据持久化• 外部机器和容器间接通信• 容器之间数据交换
数据卷概念及作用
docker run ... –v 宿主机目录(文件):容器内目录(文件) ...
创建启动容器时,使用 –v 参数 设置数据卷
配置数据卷
多容器进行数据交换1. 多个容器挂载同一个数据卷2. 数据卷容器
数据卷容器
docker run –it --name=c3 –v /volume centos:7 /bin/bash
1. 创建启动c3数据卷容器,使用 –v 参数 设置数据卷
docker run –it --name=c1 --volumes-from c3 centos:7 /bin/bashdocker run –it --name=c2 --volumes-from c3 centos:7 /bin/bash
2. 创建启动 c1 c2 容器,使用 –-volumes-from 参数 设置数据卷
配置数据卷容器
Docker 容器数据卷
容器内的网络服务和外部机器不能直接通信• 外部机器和宿主机可以直接通信• 宿主机和容器可以直接通信• 当容器中的网络服务需要被外部机器访问时,可以将容器中提供服务的端口映射到宿主机的端口上。外部机器访问宿主机的该端口,从而间接访问容器的服务。• 这种操作称为:端口映射
① 搜索mysql镜像
② 拉取mysql镜像
③ 创建容器
④ 操作容器中的mysql
在Docker容器中部署MySQL,并通过外部mysql客户端操作MySQL Server。
MySQL部署
① 搜索tomcat镜像② 拉取tomcat镜像③ 创建容器④ 部署项目⑤ 测试访问
Tomcat部署
① 搜索Nginx镜像② 拉取Nginx镜像③ 创建容器④ 测试访问
Nginx部署
① 搜索Redis镜像② 拉取Redis镜像③ 创建容器④ 测试访问
Redis部署
Docker 应用部署
• 进程调度子系统• 进程通信子系统• 内存管理子系统• 设备管理子系统• 文件管理子系统• 网络通信子系统• 作业控制子系统
操作系统组成部分
bootfs:包含bootloader(引导加载程序)和 kernel(内核)rootfs: root文件系统,包含的就是典型 Linux 系统中的/dev,/proc,/bin,/etc等标准目录和文件
不同的linux发行版,bootfs基本一样,而rootfs不同,如ubuntu,centos等
Linux文件系统由bootfs和rootfs两部分组成
docker commit 容器id 镜像名称:版本号
docker save -o 压缩文件名称 镜像名称:版本号
docker load –i 压缩文件名称
容器转为镜像
镜像制作
Docker 镜像原理
Dochub网址:https://hub.docker.com
• Dockerfile 是一个文本文件• 包含了一条条的指令• 每一条指令构建一层,基于基础镜像,最终构建出一个新的镜像• 对于开发人员:可以为开发团队提供一个完全一致的开发环境• 对于测试人员:可以直接拿开发时所构建的镜像或者通过Dockerfile文件构建一个新的镜像开始工作了• 对于运维人员:在部署时,可以实现应用的无缝移植
dockerfile
Dockerfile概念及作用
查看 文档目录中的《dockerfile.md》
Dockerfile 关键字
Dockerfile关键字
自定义centos7镜像。要求:1. 默认登录路径为 /usr2. 可以使用vim
① 定义父镜像:FROM centos:7② 定义作者信息:MAINTAINER itheima <itheima@itcast.cn>③ 执行安装vim命令: RUN yum install -y vim④ 定义默认的工作目录:WORKDIR /usr⑤ 定义容器启动执行的命令:CMD /bin/bash⑥ 通过dockerfile构建镜像:docker bulid –f dockerfile文件路径 –t 镜像名称:版本
Dockerfile
微服务架构的应用系统中一般包含若干个微服务,每个微服务一般都会部署多个实例,如果每个微服务都要手动启停,维护的工作量会很大。• 要从Dockerfile build image 或者去dockerhub拉取image• 要创建多个container• 要管理这些container(启动停止删除)
服务编排: 按照一定的业务规则批量管理容器
1. 利用 Dockerfile 定义运行环境镜像2. 使用 docker-compose.yml 定义组成应用的各服务3. 运行 docker-compose up 启动应用
Docker Compose是一个编排多容器分布式部署的工具,提供命令集管理容器化应用的完整开发周期,包括服务构建,启动和停止。使用步骤:
Docker 服务编排
Docker官方的Docker hub(https://hub.docker.com)是一个用于管理公共镜像的仓库,我们可以从上面拉取镜像到本地,也可以把我们自己的镜像推送上去。但是,有时候我们的服务器无法访问互联网,或者你不希望将自己的镜像放到公网当中,那么我们就需要搭建自己的私有仓库来存储和管理自己的镜像。
搭建私有仓库
上传镜像到私有仓库
从私有仓库拉取镜像
Docker 私有仓库
docker容器虚拟化 与 传统虚拟机比较
Docker相关概念
Docker
业务应用场景
MongoDB简介
MySQL与MongoDB对比
体系结构
介绍
数据参考类型
1.MongoDB提供高性能的数据持久性。特别是对嵌入式数据模型的支持减少了数据库系统上的I/O活动2.索引支持更快的查询,并且可以包含来自嵌入式文档和数组的键。(文本索引解决搜索的需求,TTL索引解决历史数据自动过期的需求,地理位置索引可用于构建各种O2O应用)3.mmpav1、wiredtiger、mongorocks(rocksdb)、in-memory等多引擎支持满足各种场景需求4.Gridfs解决文件存储的需求
MongoDB的复制工具称为副本集(replica set),它可提供自动故障转移和数据冗余
1.MongoDB提供了水平可扩展性作为其核心功能的一部分2.分片将数据分部在一组集群的机器上。(海量数据存储,服务能力水平扩展)3.从3.4开始MongoDB支持基于片键创建数据区域。在一个平衡的集群中,MongoDB将一个区域所覆盖的读写定向到该区域内的那些片
高扩展
MongoDB支持丰富的查询语言,支持读和写操作(CRUD),比如数据聚合、文本搜索和地理空间查询等
丰富的查询支持
无模式(动态模式)、灵活的文档模型
其它特点
MongoDB特点
MongoDB相关概念
MongoDB官网
1.下载
将压缩包解压到一个目录中。在解压目录中,手动建立一个目录用于存放数据文件,如 data/db
2.解压安装启动
windows系统中的安装部署
方式1:命令行参数方式启动服务在 bin 目录中打开命令行提示符,输入如下命令:mongod --dbpath=..\\data\\db我们在启动信息中可以看到,mongoDB的默认端口是27017,如果我们想改变默认的启动端口,可以通过–port来指定端口。为了方便我们每次启动,可以将安装目录的bin目录设置到环境变量的path中, bin 目录下是一些常用命令,比如 mongod 启动服务用的,mongo 客户端连接服务用的。
storage:#The directory where the mongod instance stores its data.Default Value is \"\\data\\db\" on Windows.dbPath: D:\\mongodb-win32-x86_64-2008plus-ssl-4.0.1\\data
方式2:配置文件方式启动服务在解压目录中新建 config 文件夹,该文件夹中新建配置文件 mongod.conf
https://docs.mongodb.com/manual/reference/configuration-options/
详细配置项内容
启动方式
配置文件中如果使用双引号,比如路径地址,自动会将双引号的内容转义。如果不转义,则会报错:error-parsing-yaml-config-file-yaml-cpp-error-at-line-3-column-15-unknown-escape-character-d解决:a. 对 \\ 换成 / 或 \\b. 如果路径中没有空格,则无需加引号。
systemLog:destination: file#The path of the log file to which mongod or mongos should send all diagnostic logging informationpath: \"D:/02_Server/DBServer/mongodb-win32-x86_64-2008plus-ssl-4.0.1/log/mongod.log\"logAppend: truestorage:journal:enabled: true#The directory where the mongod instance stores its data.Default Value is \"/data/db\".dbPath: \"D:/02_Server/DBServer/mongodb-win32-x86_64-2008plus-ssl-4.0.1/data\"net:#bindIp: 127.0.0.1port: 27017setParameter:enableLocalhostAuthBypass: false
配置文件中不能以Tab分割字段解决:将其转换成空格。启动方式:mongod -f ../config/mongod.conf 或 mongod --config ../config/mongod.conf更多参数配置:
shell连接(Mongo命令)
Compass-图形化界面客户端Navicat也可以做
Linux系统中的安装启动和连接
单机部署
案例需求
选择和创建数据库
数据库的删除
集合的显示创建
集合的隐式创建
集合的删除
集合操作
文档的插入
文档的基本查询
文档的更新修改
删除文档
文档基本的CURD
统计查询
分页列表查询
文档的查询分页
基本常用命令
MongoDB
什么是算法
什么是数据结构
二分查找
一、初识算法
数据结构
Spring是分层的 Java SE/EE应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control:反转控制)和AOP(Aspect Oriented Programming:面向切面编程)为内核。
1)方便解耦,简化开发通过 Spring 提供的 IoC容器,可以将对象间的依赖关系交由 Spring 进行控制,避免硬编码所造成的过度耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。2)AOP 编程的支持通过 Spring的 AOP 功能,方便进行面向切面编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松实现。3)声明式事务的支持可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务管理,提高开发效率和质量。4)方便程序的测试可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情5)方便集成各种优秀框架Spring对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的支持。6)降低 JavaEE API 的使用难度Spring对 JavaEE API(如 JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些 API 的使用难度大为降低。7)Java 源码是经典学习范例Spring的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对Java 设计模式灵活运用以及对 Java技术的高深造诣。它的源代码无意是 Java 技术的最佳实践的范例。
Spring体系架构
Spring简介
① 导入 Spring 开发的基本包坐标② 编写 Dao 接口和实现类③ 创建 Spring 核心配置文件④ 在 Spring 配置文件中配置 UserDaoImpl⑤ 使用 Spring 的 API 获得 Bean 实例
Spring程序开发步骤
<properties> <spring.version>5.0.5.RELEASE</spring.version></properties> <dependencies><!--导入spring的context坐标,context依赖core、beans、expression--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version></dependency></dependencies>
导入Spring开发的基本包坐标
public interface UserDao {public void save();}public class UserDaoImpl implements UserDao {@Overridepublic void save() {System.out.println(\"UserDao save method running....\");} }
编写Dao接口和实现类
在类路径下(resources)创建applicationContext.xml配置文件<?xml version=\"1.0\" encoding=\"UTF-8\" ?><beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\"></beans>
创建Spring核心配置文件
<?xml version=\"1.0\" encoding=\"UTF-8\" ?><beans xmlns=\"http://www.springframework.org/schema/beans\"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"xsi:schemaLocation=\"http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd\"> <bean id=\"userDao\" class=\"com.itheima.dao.impl.UserDaoImpl\"></bean></
在Spring配置文件中配置UserDaoImpl
@Testpublic void test1(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext(\"applicationContext.xml\");UserDao userDao = (UserDao) applicationContext.getBean(\"userDao\");userDao.save();}
使用Spring的API获得Bean实例
Spring快速入门
用于配置对象交由Spring 来创建。默认情况下它调用的是类中的无参构造函数,如果没有无参构造函数则不能创建成功。
基本属性: id:Bean实例在Spring容器中的唯一标识 class:Bean的全限定名称
Bean标签基本配置
1)当scope的取值为singleton时Bean的实例化个数:1个Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例Bean的生命周期:对象创建:当应用加载,创建容器时,对象就被创建了对象运行:只要容器在,对象一直活着对象销毁:当应用卸载,销毁容器时,对象就被销毁了2)当scope的取值为prototype时Bean的实例化个数:多个Bean的实例化时机:当调用getBean()方法时实例化Bean对象创建:当使用对象时,创建新的对象实例对象运行:只要对象在使用中,就一直活着对象销毁:当对象长时间不用时,被 Java 的垃圾回收器回收了
Bean标签范围配置
init-method:指定类中的初始化方法名称destroy-method:指定类中销毁方法名称
Bean的生命周期配置
无参构造方法实例化它会根据默认无参构造方法来创建类对象,如果bean中没有默认无参构造函数,将会创建失败<bean id=\"userDao\" class=\"com.itheima.dao.impl.UserDaoImpl\"/>
<bean id=\"userDao\" class=\"com.itheima.factory.StaticFactoryBean\" factory-method=\"createUserDao\" />
工厂静态方法实例化工厂的静态方法返回Bean实例public class StaticFactoryBean {public static UserDao createUserDao(){return new UserDaoImpl();} }
工厂的非静态方法返回Bean实例public class DynamicFactoryBean {public UserDao createUserDao(){return new UserDaoImpl();} }<bean id=\"factoryBean\" class=\"com.itheima.factory.DynamicFactoryBean\"/> <bean id=\"userDao\" factory-bean=\"factoryBean\" factory-method=\"createUserDao\"/>
工厂实例方法实例化
Bean实例化三种方式
① 创建 UserService,UserService 内部在调用 UserDao的save() 方法public class UserServiceImpl implements UserService {@Overridepublic void save() {ApplicationContext applicationContext = new ClassPathXmlApplicationContext(\"applicationContext.xml\");UserDao userDao = (UserDao) applicationContext.getBean(\"userDao\");userDao.save();} }② 将 UserServiceImpl 的创建权交给 Spring<bean id=\"userService\" class=\"com.itheima.service.impl.UserServiceImpl\"/>③ 从 Spring 容器中获得 UserService 进行操作ApplicationContext applicationContext = new ClassPathXmlApplicationContext(\"applicationContext.xml\");UserService userService = (UserService) applicationContext.getBean(\"userService\");userService.save();
创建 UserService,UserService 内部在调用 UserDao的save() 方法
Bean的依赖注入入门
Bean的依赖注入分析
依赖注入(Dependency Injection):它是 Spring 框架核心 IOC 的具体实现。
1)set方法注入在UserServiceImpl中添加setUserDao方法public class UserServiceImpl implements UserService {private UserDao userDao;public void setUserDao(UserDao userDao) {this.userDao = userDao; }@Overridepublic void save() {userDao.save();} }
1)set方法注入配置Spring容器调用set方法进行注入<bean id=\"userDao\" class=\"com.itheima.dao.impl.UserDaoImpl\"/><bean id=\"userService\" class=\"com.itheima.service.impl.UserServiceImpl\"> <property name=\"userDao\" ref=\"userDao\"/></bean>
1)set方法注入P命名空间注入本质也是set方法注入,但比起上述的set方法注入更加方便,主要体现在配置文件中,如下:首先,需要引入P命名空间:xmlns:p=\"http://www.springframework.org/schema/p\"其次,需要修改注入方式<bean id=\"userService\" class=\"com.itheima.service.impl.UserServiceImpl\" p:userDao\u0002ref=\"userDao\"/>
2)构造方法注入创建有参构造public class UserServiceImpl implements UserService {@Overridepublic void save() {ApplicationContext applicationContext = new ClassPathXmlApplicationContext(\"applicationContext.xml\");UserDao userDao = (UserDao) applicationContext.getBean(\"userDao\");userDao.save();} }
配置Spring容器调用有参构造时进行注入<bean id=\"userDao\" class=\"com.itheima.dao.impl.UserDaoImpl\"/><bean id=\"userService\" class=\"com.itheima.service.impl.UserServiceImpl\"> <constructor-arg name=\"userDao\" ref=\"userDao\"></constructor-arg></bean>
构造方法
构造方法set方法
Bean的依赖注入概念
<bean id=\"userDao\" class=\"com.itheima.dao.impl.UserDaoImpl\"> <property name=\"company\" value=\"传智播客\"></property> <property name=\"age\" value=\"15\"></property></bean>
public class UserDaoImpl implements UserDao {private String company;private int age;public void setCompany(String company) {this.company = company;}public void setAge(int age) {this.age = age;}public void save() {System.out.println(company+\"===\"+age);System.out.println(\"UserDao save method running....\");} }
普通数据类型
引用数据类型
<bean id=\"userDao\" class=\"com.itheima.dao.impl.UserDaoImpl\"> <property name=\"strList\"> <list><value>aaa</value> <value>bbb</value> <value>ccc</value></list></property></bean>
public class UserDaoImpl implements UserDao {private List<String> strList;public void setStrList(List<String> strList) {this.strList = strList;}public void save() {System.out.println(strList);System.out.println(\"UserDao save method running....\");} }
<bean id=\"u1\" class=\"com.itheima.domain.User\"/><bean id=\"u2\" class=\"com.itheima.domain.User\"/><bean id=\"userDao\" class=\"com.itheima.dao.impl.UserDaoImpl\"> <property name=\"userList\"> <list><bean class=\"com.itheima.domain.User\"/><bean class=\"com.itheima.domain.User\"/><ref bean=\"u1\"/><ref bean=\"u2\"/></list></property></bean>
集合List<User>public class UserDaoImpl implements UserDao {private List<User> userList;public void setUserList(List<User> userList) {this.userList = userList;}public void save() {System.out.println(userList);System.out.println(\"UserDao save method running....\");} }
<bean id=\"userDao\" class=\"com.itheima.dao.impl.UserDaoImpl\"> <property name=\"properties\"> <props> <prop key=\"p1\">aaa</prop> <prop key=\"p2\">bbb</prop> <prop key=\"p3\">ccc</prop></props></property></bean>
集合数据类型(Properties)的注入public class UserDaoImpl implements UserDao {private Properties properties;public void setProperties(Properties properties) {this.properties = properties;}public void save() {System.out.println(properties);System.out.println(\"UserDao save method running....\");} }
集合数据类型
Bean的依赖注入的数据类型
spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,而在Spring主配置文件通过import标签进行加载<import resource=\"applicationContext-xxx.xml\"/>
引入其他配置文件
Spring配置文件
applicationContext:接口类型,代表应用上下文,可以通过其实例获得 Spring 容器中的 Bean 对象
ApplicationContext的继承体系
1)ClassPathXmlApplicationContext它是从类的根路径下加载配置文件 推荐使用这种2)FileSystemXmlApplicationContext它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。3)AnnotationConfigApplicationContext当使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解
ApplicationContext的实现类
public Object getBean(String name) throws BeansException {assertBeanFactoryActive();return getBeanFactory().getBean(name);}public <T> T getBean(Class<T> requiredType) throws BeansException {assertBeanFactoryActive();return getBeanFactory().getBean(requiredType);}
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(\"applicationContext.xml\");UserService userService1 = (UserService) applicationContext.getBean(\"userService\");UserService userService2 = applicationContext.getBean(UserService.class);
getBean()方法使用
ApplicationContext app = new ClasspathXmlApplicationContext(\"xml文件\")app.getBean(\"id\")app.getBean(Class)
Spring的重点API
Spring相关API
数据源(连接池)是提高程序性能如出现的事先实例化数据源,初始化部分连接资源使用连接资源时从数据源中获取使用完毕后将连接资源归还给数据源
常见的数据源(连接池):DBCP、C3P0、BoneCP、Druid等
数据源(连接池)的作用
① 导入数据源的坐标和数据库驱动坐标② 创建数据源对象③ 设置数据源的基本连接数据④ 使用数据源获取连接资源和归还连接资源
数据源的开发步骤
② 创建C3P0连接池@Testpublic void testC3P0() throws Exception {//创建数据源ComboPooledDataSource dataSource = new ComboPooledDataSource();//设置数据库连接参数dataSource.setDriverClass(\"com.mysql.jdbc.Driver\");dataSource.setJdbcUrl(\"jdbc:mysql://localhost:3306/test\");dataSource.setUser(\"root\");dataSource.setPassword(\"root\");//获得连接对象Connection connection = dataSource.getConnection();System.out.println(connection);}
② 创建Druid连接池@Testpublic void testDruid() throws Exception {//创建数据源DruidDataSource dataSource = new DruidDataSource();//设置数据库连接参数dataSource.setDriverClassName(\"com.mysql.jdbc.Driver\");dataSource.setUrl(\"jdbc:mysql://localhost:3306/test\");dataSource.setUsername(\"root\");dataSource.setPassword(\"root\");//获得连接对象Connection connection = dataSource.getConnection();System.out.println(connection);}
① 导入c3p0和druid的坐标<!-- C3P0连接池 --> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency><!-- Druid连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency><!-- mysql驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.39</version> </dependency>
③ 提取jdbc.properties配置文件jdbc.driver=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://localhost:3306/testjdbc.username=rootjdbc.password=root
④ 读取jdbc.properties配置文件创建连接池@Testpublic void testC3P0ByProperties() throws Exception {//加载类路径下的jdbc.propertiesResourceBundle rb = ResourceBundle.getBundle(\"jdbc\");ComboPooledDataSource dataSource = new ComboPooledDataSource();dataSource.setDriverClass(rb.getString(\"jdbc.driver\"));dataSource.setJdbcUrl(rb.getString(\"jdbc.url\"));dataSource.setUser(rb.getString(\"jdbc.username\"));dataSource.setPassword(rb.getString(\"jdbc.password\"));Connection connection = dataSource.getConnection();System.out.println(connection);}
数据源的手动创建
DataSource有无参构造方法,而Spring默认就是通过无参构造方法实例化对象的DataSource要想使用需要通过set方法设置数据库连接信息,而Spring可以通过set方法进行字符串注入
<bean id=\"dataSource\" class=\"com.mchange.v2.c3p0.ComboPooledDataSource\"> <property name=\"driverClass\" value=\"com.mysql.jdbc.Driver\"/> <property name=\"jdbcUrl\" value=\"jdbc:mysql://localhost:3306/test\"/> <property name=\"user\" value=\"root\"/> <property name=\"password\" value=\"root\"/></bean>
Spring配置数据源
Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代替xml配置文件可以简化配置,提高开发效率。
原始注解
<!--注解的组件扫描--> <context:component-scan base-package=\"com.itheima\"></context:component-scan>
使用注解进行开发时,需要在applicationContext.xml中配置组件扫描,作用是指定哪个包及其子包下的Bean需要进行扫描以便识别使用注解配置的类、字段和方法。
//@Component(\"userDao\")@Repository(\"userDao\")public class UserDaoImpl implements UserDao {@Overridepublic void save() {System.out.println(\"save running... ...\");} }
使用@Compont或@Repository标识UserDaoImpl需要Spring进行实例化。
//@Component(\"userService\")@Service(\"userService\")public class UserServiceImpl implements UserService {/*@Autowired@Qualifier(\"userDao\")*/@Resource(name=\"userDao\")private UserDao userDao;@Overridepublic void save() {userDao.save();} }
使用@Compont或@Service标识UserServiceImpl需要Spring进行实例化使用@Autowired或者@Autowired+@Qulifier或者@Resource进行userDao的注入
@Repository(\"userDao\")public class UserDaoImpl implements UserDao {@Value(\"注入普通数据\")private String str;@Value(\"${jdbc.driver}\")private String driver;@Overridepublic void save() {System.out.println(str);System.out.println(driver);System.out.println(\"save running... ...\");} }
使用@Value进行字符串注入
//@Scope(\"prototype\")@Scope(\"singleton\")public class UserDaoImpl implements UserDao {//此处省略代码}
使用@Scope标注Bean的范围
@PostConstructpublic void init(){System.out.println(\"初始化方法....\");}@PreDestroypublic void destroy(){System.out.println(\"销毁方法.....\");}
使用@PostConstruct标注初始化方法,使用@PreDestroy标注销毁方法
非自定义的Bean的配置:<bean>加载properties文件的配置:<context:property-placeholder>组件扫描的配置:<context:component-scan>引入其他文件:<import>
@Configuration@ComponentScan(\"com.itheima\")@Import({DataSourceConfiguration.class})public class SpringConfiguration { }
@PropertySource(\"classpath:jdbc.properties\")public class DataSourceConfiguration {@Value(\"${jdbc.driver}\")private String driver;@Value(\"${jdbc.url}\")private String url;@Value(\"${jdbc.username}\")private String username;@Value(\"${jdbc.password}\")private String password;
@Bean(name=\"dataSource\")public DataSource getDataSource() throws PropertyVetoException {ComboPooledDataSource dataSource = new ComboPooledDataSource();dataSource.setDriverClass(driver);dataSource.setJdbcUrl(url);dataSource.setUser(username);dataSource.setPassword(password);return dataSource; }
新注解
Spring注解开发
每个测试都有以下俩个方法ApplicationContext ac = new ClassPathXmlApplicationContext(\"bean.xml\");IAccountService as = ac.getBean(\"accountService\
让SpringJunit负责创建Spring容器,但是需要将配置文件的名称告诉它将需要进行测试Bean直接在测试类中进行注入
解决思路
一<!--此处需要注意的是,spring5 及以上版本要求 junit 的版本必须是 4.12 及以上--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.2.RELEASE</version></dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope></dependency>
二@RunWith(SpringJUnit4ClassRunner.class)public class SpringJunitTest {}
三@RunWith(SpringJUnit4ClassRunner.class)//加载spring核心配置文件//@ContextConfiguration(value = {\"classpath:applicationContext.xml\"})//加载spring核心配置类@ContextConfiguration(classes = {SpringConfiguration.class})public class SpringJunitTest {}
四@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(classes = {SpringConfiguration.class})public class SpringJunitTest {@Autowiredprivate UserService userService; }
五@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(classes = {SpringConfiguration.class})public class SpringJunitTest {@Autowiredprivate UserService userService;@Testpublic void testUserService(){userService.save();} }
① 导入spring集成Junit的坐标② 使用@Runwith注解替换原来的运行期③ 使用@ContextConfiguration指定配置文件或配置类④ 使用@Autowired注入需要测试的对象⑤ 创建测试方法进行测试
原始Junit测试Spring的问题
Spring整合JUnit
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.0.5.RELEASE</version></dependency>
导入Spring集成web的坐标
<!--全局参数--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param><!--Spring的监听器--> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
配置ContextLoaderListener监听器
ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);Object obj = applicationContext.getBean(\"id\");
通过工具获得应用上下文对象
Spring与web环境集成
用户界面
角色列表的展示和添加操作
用户列表的展示和添加操作
删除用户操作
Spring练习环境搭建
AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
在程序运行期间,在不修改源码的情况下对方法进行功能增强
减少重复代码,提高开发效率,并且便于维护
AOP 的作用及其优势
AOP 的底层是通过 Spring 提供的的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。
AOP的底层实现
① 目标类接口public interface TargetInterface {public void method();} ② 目标类public class Target implements TargetInterface {@Overridepublic void method() {System.out.println(\"Target running....\
JDK 代理 : 基于接口的动态代理技术
① 目标类public class Target {public void method() {System.out.println(\"Target running....\
常用的动态代理技术
AOP 的动态代理技术
AOP相关术语
AOP 相关概念
编写核心业务代码(目标类的目标方法) 编写切面类,切面类中有通知(增强功能方法) 在配置文件中,配置织入关系,即将哪些通知与哪些连接点进行结合
1. 需要编写的内容
Spring 框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。
2. AOP 技术实现的内容
在 spring 中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。
3. AOP 底层使用哪种代理方式
AOP
① 导入 AOP 相关坐标<!--导入spring的context坐标,context依赖aop--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.5.RELEASE</version></dependency><!-- aspectj的织入 --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.13</version></dependency>
② 创建目标接口和目标类(内部有切点)public interface TargetInterface {public void method();}public class Target implements TargetInterface {@Overridepublic void method() {System.out.println(\"Target running....\");} }
③ 创建切面类(内部有增强方法)public class MyAspect {//前置增强方法public void before(){System.out.println(\"前置代码增强.....\");} }
④ 将目标类和切面类的对象创建权交给 spring<!--配置目标类--> <bean id=\"target\" class=\"com.itheima.aop.Target\"></bean><!--配置切面类--> <bean id=\"myAspect\" class=\"com.itheima.aop.MyAspect\"></bean>
配置切点表达式和前置增强的织入关系<aop:config><!--引用myAspect的Bean为切面对象--> <aop:aspect ref=\"myAspect\"><!--配置Target的method方法执行时要进行myAspect的before方法前置增强--> <aop:before method=\"before\" pointcut=\"execution(public void com.itheima.aop.Target.method())\"></aop:before></aop:aspect></aop:config>
⑥ 测试代码@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(\"classpath:applicationContext.xml\")public class AopTest { @Autowiredprivate TargetInterface target;@Testpublic void test1(){target.method();} }
execution(public void com.itheima.aop.Target.method())execution(void com.itheima.aop.Target.*(..))execution(* com.itheima.aop.*.*(..))execution(* com.itheima.aop..*.*(..))execution(* *..*.*(..))
execution([修饰符] 返回值类型 包名.类名.方法名(参数)) 访问修饰符可以省略 返回值类型、包名、类名、方法名可以使用星号* 代表任意 包名与类名之间一个点 . 代表当前包下的类,两个点 .. 表示当前包及其子包下的类 参数列表可以使用两个点 .. 表示任意个数,任意类型的参数列表
1. 切点表达式的写法
2.通知的类型
当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用 pointcut-ref 属性代替 pointcut 属性来引用抽取后的切点表达式。
3.切点表达式的抽取
XML 配置 AOP 详解
基于 XML 的 AOP 开发
public interface TargetInterface {public void method();}public class Target implements TargetInterface {@Overridepublic void method() {System.out.println(\"Target running....\");} }
① 创建目标接口和目标类(内部有切点)
public class MyAspect {//前置增强方法public void before(){System.out.println(\"前置代码增强.....\");} }
② 创建切面类(内部有增强方法)
@Component(\"target\")public class Target implements TargetInterface {@Overridepublic void method() {System.out.println(\"Target running....\");} }@Component(\"myAspect\")public class MyAspect {public void before(){System.out.println(\"前置代码增强.....\");} }
③ 将目标类和切面类的对象创建权交给 spring
@Component(\"myAspect\")@Aspectpublic class MyAspect {@Before(\"execution(* com.itheima.aop.*.*(..))\")public void before(){System.out.println(\"前置代码增强.....\");} }
④ 在切面类中使用注解配置织入关系
⑤ 在配置文件中开启组件扫描和 AOP 的自动代理
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(\"classpath:applicationContext.xml\")public class AopTest { @Autowired private TargetInterface target; @Test public void test1(){ target.method(); } }
⑥测试
注解通知的类型
@@Component(\"myAspect\")@Aspectpublic class MyAspect {@Before(\"MyAspect.myPoint()\")public void before(){System.out.println(\"前置代码增强.....\");}@Pointcut(\"execution(* com.itheima.aop.*.*(..))\")public void myPoint(){}}
切点表达式的抽取
基于注解的 AOP 开发
PlatformTransactionManager————>spring 的事务管理器
事务隔离级别
事务传播行为
TransactionDefinition是事务的定义信息对象
TransactionStatus事务具体的运行状态
编程式事务控制相关对象
① 引入tx命名空间
② 配置事务增强
③ 配置事务 AOP 织入
切点方法的事务参数的配置
基于 XML 的声明式事务控制
注解配置声明式事务控制解析
基于注解的声明式事务控制
声明式事务控制
Spring
SpringMVC 是一种基于 Java 的实现 MVC 设计模型的请求驱动类型的轻量级 Web 框架,属于SpringFrameWork 的后续产品,已经融合在 Spring Web Flow 中。
SpringMVC概述
<!--Spring坐标--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.5.RELEASE</version></dependency><!--SpringMVC坐标--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.0.5.RELEASE</version></dependency><!--Servlet坐标--> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version></dependency><!--Jsp坐标--> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.0</version></dependency>
导入Servlet和Jsp的坐标
<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> <load-on-startup>1</load-on-startup></servlet> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>/</url-pattern></servlet-mapping>
在web.xml配置SpringMVC的核心控制器
public class QuickController {public String quickMethod(){ System.out.println(\"quickMethod running.....\");return \"index\"; } }
创建Controller和业务层方法
<html> <body><h2>Hello SpringMVC!</h2></body></html>
创建视图jsp
@Controllerpublic class QuickController {@RequestMapping(\"/quick\")public String quickMethod(){System.out.println(\"quickMethod running.....\");return \"index\"; } }
配置注解
<beans xmlns=\"http://www.springframework.org/schema/beans\"xmlns:mvc=\"http://www.springframework.org/schema/mvc\"xmlns:context=\"http://www.springframework.org/schema/context\"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"xsi:schemaLocation=\"http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd\"><!--配置注解扫描--> <context:component-scan base-package=\"com.itheima\"/></beans>
创建spring-mvc.xml
http://localhost:8080/itheima_springmvc1/quick
访问测试地址
流程图示
SpringMVC快速入门
SpringMVC的简介
① 用户发送请求至前端控制器DispatcherServlet。 ② DispatcherServlet收到请求调用HandlerMapping处理器映射器。③ 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。④ DispatcherServlet调用HandlerAdapter处理器适配器。⑤ HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。 ⑥ Controller执行完成返回ModelAndView。 ⑦ HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。 ⑧ DispatcherServlet将ModelAndView传给ViewReslover视图解析器。⑨ ViewReslover解析后返回具体View。 ⑩ DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。DispatcherServlet响应用户。
SpringMVC的执行流程
1. 前端控制器:DispatcherServlet用户请求到达前端控制器,它就相当于 MVC 模式中的 C,DispatcherServlet 是整个流程控制的中心,由它调用其它组件处理用户的请求,DispatcherServlet 的存在降低了组件之间的耦合性。2. 处理器映射器:HandlerMappingHandlerMapping 负责根据用户请求找到 Handler 即处理器,SpringMVC 提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。3. 处理器适配器:HandlerAdapter通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。4. 处理器:Handler它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到 Handler。由Handler 对具体的用户请求进行处理。5. 视图解析器:View ResolverView Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名,即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。6. 视图:ViewSpringMVC 框架提供了很多的 View 视图类型的支持,包括:jstlView、freemarkerView、pdfView等。最常用的视图就是 jsp。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面
组件解析
1. mvc命名空间引入命名空间:xmlns:context=\"http://www.springframework.org/schema/context\"xmlns:mvc=\"http://www.springframework.org/schema/mvc\"约束地址:http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd2. 组件扫描SpringMVC基于Spring容器,所以在进行SpringMVC操作时,需要将Controller存储到Spring容器中,如果使用@Controller注解标注的话,就需要使用<context:component-scan base\u0002package=“com.itheima.controller\"/>进行组件扫描。
@RequestMapping作用:用于建立请求 URL 和处理请求方法之间的对应关系位置: 类上,请求URL 的第一级访问目录。此处不写的话,就相当于应用的根目录 方法上,请求 URL 的第二级访问目录,与类上的使用@ReqquestMapping标注的一级目录一起组成访问虚拟路径属性: value:用于指定请求的URL。它和path属性的作用是一样的 method:用于指定请求的方式 params:用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数的key和value必须和配置的一模一样例如: params = {\"accountName\"},表示请求参数必须有accountName params = {\"moeny!100\"},表示请求参数中money不能是100
注解解析
<!--配置内部资源视图解析器--> <bean class=\"org.springframework.web.servlet.view.InternalResourceViewResolver\"> <property name=\"prefix\" value=\"/WEB-INF/views/\"></property> <property name=\"suffix\" value=\".jsp\"></property></bean>
1. 视图解析器SpringMVC有默认组件配置,默认组件都是DispatcherServlet.properties配置文件中配置的,该配置文件地址org/springframework/web/servlet/DispatcherServlet.properties,该文件中配置了默认的视图解析器,如下:org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver翻看该解析器源码,可以看到该解析器的默认设置,如下:REDIRECT_URL_PREFIX = \"redirect:\" --重定向前缀FORWARD_URL_PREFIX = \"forward:\" --转发前缀(默认值)prefix = \"\"; --视图名称前缀suffix = \"\"; --视图名称后缀
XML配置解析
SpringMVC的组件分析
@RequestMapping(\"/quick2\")public ModelAndView quickMethod2(){ModelAndView modelAndView = new ModelAndView();modelAndView.setViewName(\"redirect:index.jsp\");return modelAndView;}@RequestMapping(\"/quick3\")public ModelAndView quickMethod3(){ModelAndView modelAndView = new ModelAndView();modelAndView.setViewName(\"forward:/WEB-INF/views/index.jsp\");return modelAndView;}
返回ModelAndView对象
1) 页面跳转 直接返回字符串 通过ModelAndView对象返回2) 回写数据 直接返回字符串 返回对象或集合
数据响应方式
② 通过ModelAndView的addObject()方法设置@RequestMapping(\"/quick3\")public ModelAndView quickMethod3(){ModelAndView modelAndView = new ModelAndView();modelAndView.setViewName(\"forward:/WEB-INF/views/index.jsp\");modelAndView.addObject(\"name\
3向request域存储数据
① 通过SpringMVC框架注入的response对象,使用response.getWriter().print(“hello world”) 回写数据,此时不需要视图跳转,业务方法返回值为void。@RequestMapping(\"/quick4\")public void quickMethod4(HttpServletResponse response) throws IOException {response.getWriter().print(\"hello world\");}
② 将需要回写的字符串直接返回,但此时需要通过@ResponseBody注解告知SpringMVC框架,方法返回的字符串不是跳转是直接在http响应体中返回。@RequestMapping(\"/quick5\")@ResponseBodypublic String quickMethod5() throws IOException {return \"hello springMVC!!!\"; }
在异步项目中,客户端与服务器端往往要进行json格式字符串交互,此时我们可以手动拼接json字符串返回。@RequestMapping(\"/quick6\")@ResponseBodypublic String quickMethod6() throws IOException {return \"{\\\"name\\\":\\\"zhangsan\\\
@RequestMapping(\"/quick7\")@ResponseBodypublic String quickMethod7() throws IOException {User user = new User();user.setUsername(\"zhangsan\");user.setAge(18);ObjectMapper objectMapper = new ObjectMapper();String s = objectMapper.writeValueAsString(user);return s;}
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0</version></dependency><dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.0</version></dependency>
<!--jackson--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.0</version></dependency>
@RequestMapping(\"/quick8\")@ResponseBodypublic User quickMethod8() throws IOException {User user = new User();user.setUsername(\"zhangsan\");user.setAge(18);return user;}
返回对象或集合<bean class=\"org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter\"> <property name=\"messageConverters\"> <list><bean class=\"org.springframework.http.converter.json.MappingJackson2HttpMessageConverter\"></bean></list></property></bean>
使用<mvc:annotation-driven>默认底层就会集成jackson进行对象或集合的json格式字符串的转换
<!--mvc的注解驱动--> <mvc:annotation-driven/>
回写数据
SpringMVC数据响应
基本类型参数POJO类型参数数组类型参数集合类型参数
可获取的类型
@RequestMapping(\"/quick9\
Controller中的业务方法的参数名称要与请求参数的name一致,参数值会自动映射匹配http://localhost:8080/itheima_springmvc1/quick9?username=zhangsan&age=12
public class User {private String username;private int age;getter/setter…}@RequestMapping(\"/quick10\")@ResponseBodypublic void quickMethod10(User user) throws IOException {System.out.println(user);}
Controller中的业务方法的POJO参数的属性名与请求参数的name一致,参数值会自动映射匹配。http://localhost:8080/itheima_springmvc1/quick9?username=zhangsan&age=12
http://localhost:8080/itheima_springmvc1/quick11?strs=111&strs=222&strs=333@RequestMapping(\"/quick11\")@ResponseBodypublic void quickMethod11(String[] strs) throws IOException {System.out.println(Arrays.asList(strs));}
获得数组类型
<form action=\"${pageContext.request.contextPath}/quick12\" method=\"post\"> <input type=\"text\" name=\"userList[0].username\"><br> <input type=\"text\" name=\"userList[0].age\"><br> <input type=\"text\" name=\"userList[1].username\"><br> <input type=\"text\" name=\"userList[1].age\"><br> <input type=\"submit\" value=\"提交\"><br></form>@RequestMapping(\"/quick12\")@ResponseBodypublic void quickMethod12(Vo vo) throws IOException {System.out.println(vo.getUserList());}
<script>//模拟数据var userList = new Array();userList.push({username: \"zhangsan\
@RequestMapping(\"/quick13\")@ResponseBodypublic void quickMethod13(@RequestBody List<User> userList) throws IOException {System.out.println(userList);}
当使用ajax提交时,可以指定contentType为json形式,那么在方法参数位置使用@RequestBody可以直接接收集合数据而无需使用POJO进行包装。
获得集合类型参数
<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></filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern></filter-mapping>
请求数据乱码问题
@RequestMapping(\"/quick14\")@ResponseBodypublic void quickMethod14(@RequestParam(\"name\") String username) throws IOException {System.out.println(username);}
value:与请求参数名称required:此在指定的请求参数是否必须包括,默认是true,提交时如果没有此参数则报错defaultValue:当没有指定请求参数时,则使用指定的默认值赋值@RequestMapping(\"/quick14\")@ResponseBodypublic void quickMethod14(@RequestParam(value=\"name\
<form action=\"${pageContext.request.contextPath}/quick14\" method=\"post\"> <input type=\"text\" name=\"name\"><br> <input type=\"submit\" value=\"提交\"><br></form>
参数绑定注解@requestParam
Restful是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。主要用于客户端和服务器交互类的软件,基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存机制等。
http://localhost:8080/itheima_springmvc1/quick19/zhangsan@RequestMapping(\"/quick19/{name}\")@ResponseBodypublic void quickMethod19(@PathVariable(value = \"name\
Restful风格的请求是使用“url+请求方式”表示一次请求目的的,HTTP 协议里面四个表示操作方式的动词如下: GET:用于获取资源 POST:用于新建资源 PUT:用于更新资源 DELETE:用于删除资源
获得Restful风格的参数
自定义类型转换器的开发步骤:① 定义转换器类实现Converter接口
<bean id=\"converterService\" class=\"org.springframework.context.support.ConversionServiceFactoryBean\"> <property name=\"converters\"> <list><bean class=\"com.itheima.converter.DateConverter\"/></list></property></bean>
② 在配置文件中声明转换器
<mvc:annotation-driven conversion-service=\"converterService\"/>
③ 在<annotation-driven>中引用转换器
自定义类型转换器
@RequestMapping(\"/quick16\
1. @RequestHeader使用@RequestHeader可以获得请求头信息,相当于web阶段学习的request.getHeader(name)@RequestHeader注解的属性如下: value:请求头的名称 required:是否必须携带此请求头@RequestMapping(\"/quick17\")@ResponseBodypublic void quickMethod17(@RequestHeader(value = \"User-Agent\
2. @CookieValue使用@CookieValue可以获得指定Cookie的值@CookieValue注解的属性如下: value:指定cookie的名称 required:是否必须携带此cookie@RequestMapping(\"/quick18\")@ResponseBodypublic void quickMethod18(@CookieValue(value = \"JSESSIONID\
SpringMVC支持使用原始ServletAPI对象作为控制器方法的参数进行注入,常用的对象如下: HttpServletRequest HttpServletResponse HttpSession
获得Servlet相关API
1. 文件上传客户端三要素 表单项type=“file” 表单的提交方式是post 表单的enctype属性是多部分表单形式,及enctype=“multipart/form-data”<form action=\"${pageContext.request.contextPath}/quick20\" method=\"post\" enctype=\"multipart/form-data\">名称:<input type=\"text\" name=\"name\"><br>文件:<input type=\"file\" name=\"file\"><br> <input type=\"submit\" value=\"提交\"><br></form>
<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.2.2</version></dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.4</version></dependency>
<bean id=\"multipartResolver\" class=\"org.springframework.web.multipart.commons.CommonsMultipartResolver\"><!--上传文件总大小--> <property name=\"maxUploadSize\" value=\"5242800\"/><!--上传单个文件的大小--> <property name=\"maxUploadSizePerFile\" value=\"5242800\"/><!--上传文件的编码类型--> <property name=\"defaultEncoding\" value=\"UTF-8\"/></bean>
@RequestMapping(\"/quick20\
① 导入fileupload和io坐标② 配置文件上传解析器③ 编写文件上传代码
单文件上传步骤
<h1>多文件上传测试</h1> <form action=\"${pageContext.request.contextPath}/quick21\" method=\"post\" enctype=\"multipart/form-data\">名称:<input type=\"text\" name=\"name\"><br>文件1:<input type=\"file\" name=\"uploadFiles\"><br>文件2:<input type=\"file\" name=\"uploadFiles\"><br>文件3:<input type=\"file\" name=\"uploadFiles\"><br> <input type=\"submit\" value=\"提交\"><br></form>
@RequestMapping(\"/quick21\
多文件上传实现
文件上传
SpringMVC获得请求数据
它是spring框架中提供的一个对象,是对原始繁琐的Jdbc API对象的简单封装。spring框架为我们提供了很多的操作模板类。例如:操作关系型数据的JdbcTemplate和HibernateTemplate,操作nosql数据库的RedisTemplate,操作消息队列的JmsTemplate等等。
<!--导入spring的jdbc坐标--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.0.5.RELEASE</version></dependency><!--导入spring的tx坐标--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.0.5.RELEASE</version></dependency>
//1、创建数据源对象ComboPooledDataSource dataSource = new ComboPooledDataSource();dataSource.setDriverClass(\"com.mysql.jdbc.Driver\");dataSource.setJdbcUrl(\"jdbc:mysql://localhost:3306/test\");dataSource.setUser(\"root\");dataSource.setPassword(\"root\");//2、创建JdbcTemplate对象JdbcTemplate jdbcTemplate = new JdbcTemplate();//3、设置数据源给JdbcTemplatejdbcTemplate.setDataSource(dataSource);//4、执行操作jdbcTemplate.update(\
① 导入spring-jdbc和spring-tx坐标② 创建数据库表和实体③ 创建JdbcTemplate对象④ 执行数据库操作
Spring JdbcTemplate基本使用
Spring MVC 的拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理
拦截器和过滤器的区别
<!--配置拦截器--> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path=\"/**\"/><bean class=\"com.itheima.interceptor.MyHandlerInterceptor1\"/></mvc:interceptor></mvc:interceptors>
@RequestMapping(\"/quick23\
① 创建拦截器类实现HandlerInterceptor接口② 配置拦截器③ 测试拦截器的拦截效果
拦截器方法说明
SpringMVC拦截器
预期异常
运行时异常RuntimeException
异常处理的思路
使用Spring MVC提供的简单异常处理器SimpleMappingExceptionResolver
① 创建异常处理器类实现HandlerExceptionResolver
<bean id=\"exceptionResolver\" class=\"com.itheima.exception.MyExceptionResolver\"/>
② 配置异常处理器
<%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %><html> <head><title>Title</title></head> <body>这是一个最终异常的显示页面</body></html>
③ 编写异常页面
@RequestMapping(\"/quick22\
④ 测试异常跳转
自定义异常处理步骤
实现Spring的异常处理接口HandlerExceptionResolver 自定义自己的异常处理器
异常处理两种方式
配置简单异常处理器SimpleMappingExceptionResolver 自定义异常处理器
异常处理方式
① 创建异常处理器类实现HandlerExceptionResolver② 配置异常处理器③ 编写异常页面④ 测试异常跳转
知识要点
SpringMVC异常处理机制
SpringMVC
mybatis 是一个优秀的基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注sql语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。 mybatis通过xml或注解的方式将要执行的各种 statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句。 最后mybatis框架执行sql并将结果映射为java对象并返回。采用ORM思想解决了实体和数据库映射的问题,对jdbc 进行了封装,屏蔽了jdbc api 底层访问细节,使我们不用与jdbc api打交道,就可以完成对数据库的持久化操作。
MyBatis的简介
① 添加MyBatis的坐标
② 创建user数据表
public class User {private int id;private String username;private String password;//省略get个set方法}
③ 编写User实体类
<?xml version=\"1.0\" encoding=\"UTF-8\" ?><!DOCTYPE mapperPUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\"> <mapper namespace=\"userMapper\"> <select id=\"findAll\" resultType=\"com.itheima.domain.User\">select * from User</select></mapper>
④ 编写映射文件UserMapper.xml
⑤ 编写核心文件SqlMapConfig.xml
//加载核心配置文件InputStream resourceAsStream = Resources.getResourceAsStream(\"SqlMapConfig.xml\");//获得sqlSession工厂对象SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);//获得sqlSession对象SqlSession sqlSession = sqlSessionFactory.openSession();//执行sql语句List<User> userList = sqlSession.selectList(\"userMapper.findAll\");//打印结果System.out.println(userList);//释放资源sqlSession.close();
⑥ 编写测试类
MyBatis的快速入门
MyBatis的映射文件概述
InputStream resourceAsStream = Resources.getResourceAsStream(\"SqlMapConfig.xml\");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession();int insert = sqlSession.insert(\"userMapper.add\
<mapper namespace=\"userMapper\"> <insert id=\"add\" parameterType=\"com.itheima.domain.User\
InputStream resourceAsStream = Resources.getResourceAsStream(\"SqlMapConfig.xml\");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession();int update = sqlSession.update(\"userMapper.update\
<mapper namespace=\"userMapper\"> <update id=\"update\" parameterType=\"com.itheima.domain.User\
InputStream resourceAsStream = Resources.getResourceAsStream(\"SqlMapConfig.xml\");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession();int delete = sqlSession.delete(\"userMapper.delete\
<mapper namespace=\"userMapper\"> <delete id=\"delete\" parameterType=\"java.lang.Integer\">delete from user where id=#{id}</delete></mapper>
1. 编写UserMapper映射文件
MyBatis的增删改查操作
MyBatis核心配置文件层级关系
environments标签
该标签的作用是加载映射的,加载方式有如下几种:• 使用相对于类路径的资源引用,例如:<mapper resource=\"org/mybatis/builder/AuthorMapper.xml\"/>• 使用完全限定资源定位符(URL),例如:<mapper url=\"file:///var/mappers/AuthorMapper.xml\"/>• 使用映射器接口实现类的完全限定类名,例如:<mapper class=\"org.mybatis.builder.AuthorMapper\"/>• 将包内的映射器接口实现全部注册为映射器,例如:<package name=\"org.mybatis.builder\"/>
mapper标签
Properties标签
typeAliases标签
MyBatis常用配置解析
MyBatis的核心配置文件概述
常用API:SqlSessionFactory build(InputStream inputStream)通过加载mybatis的核心文件的输入流的形式构建一个SqlSessionFactory对象
String resource = \"org/mybatis/builder/mybatis-config.xml\";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();SqlSessionFactory factory = builder.build(inputStream);
SqlSession工厂构建器SqlSessionFactoryBuilder
SqlSession工厂对象SqlSessionFactory
SqlSession会话对象
MyBatis的相应API
public interface UserDao {List<User> findAll() throws IOException; }
编写UserDao接口
public class UserDaoImpl implements UserDao {public List<User> findAll() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream(\"SqlMapConfig.xml\");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession();List<User> userList = sqlSession.selectList(\"userMapper.findAll\");sqlSession.close();return userList; } }
编写UserDaoImpl实现
@Testpublic void testTraditionDao() throws IOException {UserDao userDao = new UserDaoImpl();List<User> all = userDao.findAll();System.out.println(all);}
测试传统方式
传统开发方式
采用 Mybatis 的代理开发方式实现 DAO 层的开发,这种方式是我们后面进入企业的主流。Mapper 接口开发方法只需要程序员编写Mapper 接口(相当于Dao 接口),由Mybatis 框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。Mapper 接口开发需要遵循以下规范:1、 Mapper.xml文件中的namespace与mapper接口的全限定名相同2、 Mapper接口方法名和Mapper.xml中定义的每个statement的id相同3、 Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同4、 Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
编写UserMapper接口
@Testpublic void testProxyDao() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream(\"SqlMapConfig.xml\");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession();//获得MyBatis框架生成的UserMapper接口的实现类UserMapper userMapper = sqlSession.getMapper(UserMapper.class);User user = userMapper.findById(1);System.out.println(user);sqlSession.close();}
测试代理方式
代理开发方式
Mybatis的DAO层
Mybatis 的映射文件中,前面我们的 SQL 都是比较简单的,有些时候业务逻辑复杂时,我们的 SQL是动态变化的,此时在前面的学习中我们的 SQL 就不能满足要求了。
我们根据实体类的不同取值,使用不同的 SQL语句来进行查询。比如在 id如果不为空时可以根据id查询,如果username 不同空时还要加入用户名作为条件。这种情况在我们的多条件组合查询中经常会碰到
… … …//获得MyBatis框架生成的UserMapper接口的实现类UserMapper userMapper = sqlSession.getMapper(UserMapper.class);User condition = new User();condition.setId(1);condition.setUsername(\"lucy\");User user = userMapper.findByCondition(condition);… … …
当查询条件id和username都存在时,控制台打印的sql语句如下:
当只有id时
动态 SQL 之<if>
动态 SQL 之<foreach>
动态sql语句
Sql 中可将重复的 sql 提取出来,使用时用 include 引用即可,最终达到 sql 重用的目的
SQL片段抽取
① 定义转换类继承类BaseTypeHandler<T>
② 覆盖4个未实现的方法,其中setNonNullParameter为java程序设置数据到数据库的回调方法,getNullableResult为查询时 mysql的字符串类型转换成 java的Type类型的方法
③ 在MyBatis核心配置文件中进行注册
④ 测试转换是否正确
typeHandlers标签
<!-- 分页助手 --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>3.7.5</version></dependency> <dependency> <groupId>com.github.jsqlparser</groupId> <artifactId>jsqlparser</artifactId> <version>0.9.1</version></dependency>
① 导入通用PageHelper的坐标
<!-- 注意:分页助手的插件 配置在通用馆mapper之前 --> <plugin interceptor=\"com.github.pagehelper.PageHelper\"><!-- 指定方言 --> <property name=\"dialect\" value=\"mysql\"/></plugin>
② 在mybatis核心配置文件中配置PageHelper插件
③ 测试分页数据获取
//其他分页的数据PageInfo<User> pageInfo = new PageInfo<User>(select);System.out.println(\"总条数:\"+pageInfo.getTotal());System.out.println(\"总页数:\"+pageInfo.getPages());System.out.println(\"当前页:\"+pageInfo.getPageNum());System.out.println(\"每页显示长度:\"+pageInfo.getPageSize());System.out.println(\"是否第一页:\"+pageInfo.isIsFirstPage());System.out.println(\"是否最后一页:\"+pageInfo.isIsLastPage());
分页其它相关参数
plugins标签
MyBatis核心配置文件深入
public interface UserMapper {List<User> findAll();}
配置UserMapper.xml<mapper namespace=\"com.itheima.mapper.UserMapper\"> <resultMap id=\"userMap\" type=\"com.itheima.domain.User\"> <result column=\"id\" property=\"id\"></result> <result column=\"username\" property=\"username\"></result> <result column=\"password\" property=\"password\"></result> <result column=\"birthday\" property=\"birthday\"></result> <collection property=\"orderList\" ofType=\"com.itheima.domain.Order\"> <result column=\"oid\" property=\"id\"></result> <result column=\"ordertime\" property=\"ordertime\"></result> <result column=\"total\" property=\"total\"></result></collection></resultMap> <select id=\"findAll\" resultMap=\"userMap\
MyBatis的多表操作
@Insert:实现新增@Update:实现更新@Delete:实现删除@Select:实现查询@Result:实现结果集封装@Results:可以与@Result 一起使用,封装多个结果集@One:实现一对一结果集封装@Many:实现一对多结果集封装
MyBatis常用注解
修改MyBatis的核心配置文件,我们使用了注解替代的映射文件,所以我们只需要加载使用了注解的Mapper接口即可<mappers><!--扫描使用注解的类--> <mapper class=\"com.itheima.mapper.UserMapper\"></mapper></mappers>
或者指定扫描包含映射关系的接口所在的包也可以<mappers><!--扫描使用注解的类所在的包--> <package name=\"com.itheima.mapper\"></package></mappers>
MyBatis的增删改查
MyBatis的注解实现复杂映射开发
MyBatis注解开发
MyBatis
创建Maven工程
导入Maven坐标
编写实体类
编写Mapper接口
编写Service接口
编写Service接口实现
编写Controller
编写添加页面
编写列表页面
编写相应配置文件
原始方式整合
整合思路
将SqlSessionFactory配置到Spring容器中
<!--配置Mapper扫描--><bean class=\"org.mybatis.spring.mapper.MapperScannerConfigurer\"><property name=\"basePackage\" value=\"com.itheima.mapper\"/></bean>
扫描Mapper,让Spring容器产生Mapper实现类
<!--配置声明式事务控制--><bean id=\"transacionManager\" class=\"org.springframework.jdbc.datasource.DataSourceTransactionManager\"><property name=\"dataSource\" ref=\"dataSource\"/></bean><tx:advice id=\"txAdvice\" transaction-manager=\"transacionManager\"><tx:attributes><tx:method name=\"*\"/></tx:attributes></tx:advice><aop:config><aop:pointcut id=\"txPointcut\" expression=\"execution(* com.itheima.service.impl.*.*(..))\"/><aop:advisor advice-ref=\"txAdvice\" pointcut-ref=\"txPointcut\"/></aop:config>
配置声明式事务控制
@Service(\"accountService\")public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountMapper accountMapper;public void save(Account account) {accountMapper.save(account);}public List<Account> findAll() {return accountMapper.findAll();} }
修改Service实现类代码
Spring整合MyBatis
SSM整合
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
Mybatis-Plus是由baomidou(苞米豆)组织开发并且开源的,目前该组织大概有30人左右
author
Mybatis-Plus介绍
创建数据库以及表
<?xml version=\"1.0\" encoding=\"UTF-8\"?> <project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"> <modelVersion>4.0.0</modelVersion> <groupId>cn.itcast.mp</groupId> <artifactId>itcast-mybatis-plus</artifactId> <version>1.0-SNAPSHOT</version> <modules> <module>itcast-mybatis-plus-simple</module> </modules> <packaging>pom</packaging> <dependencies> <!-- mybatis-plus插件依赖 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus</artifactId> <version>3.1.1</version> </dependency> <!-- MySql --><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!-- 连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.11</version> </dependency> <!--简化bean代码的工具包--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> <version>1.18.4</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.6.4</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> </project>
导入依赖
创建工程
Quick start
创建子Module
log4j.properties
Mybatis实现查询User
第一步,将UserMapper继承BaseMapper,将拥有了BaseMapper中的所有方法:
第二步,使用MP中的MybatisSqlSessionFactoryBuilder进程构建:
运行报错解决
Mybatis+MP实现查询User
Mybatis + MP
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/mp? useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL =false jdbc.username=root jdbc.password=root
编写jdbc.properties
编写applicationContext.xml
编写User对象以及UserMapper接口
实现查询User
Spring + Mybatis + MP
新建工程
<?xml version=\"1.0\" encoding=\"UTF-8\"?> <project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.4.RELEASE</version> </parent> <groupId>cn.itcast.mp</groupId> <artifactId>itcast-mp-springboot</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <exclusions> <exclusion><groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--简化代码的工具包--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!--mybatis-plus的springboot支持--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.1.1</version> </dependency> <!--mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
log4j.properties:
spring.application.name = itcast-mp-springboot spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mp? useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL =false spring.datasource.username=root spring.datasource.password=root
编写application.properties
编写pojo
编写mapper
启动类mapper扫描
使用SpringBoot将进一步的简化MP的整合,需要注意的是,由于使用SpringBoot需要继承parent,所以需要重新创建工程,并不是创建子Module。
SpringBoot + Mybatis + MP
解释:在MP中通过@TableField注解可以指定字段的一些属性,常常解决的问题有2个:1、对象中的属性名和字段名不一致的问题(非驼峰)2、对象中的属性字段在表中不存在的问题
@TableField
通过QueryWrapper
通过UpdateWrapper
更新操作
deleteById
deleteByMap
@Test public void testDeleteByMap() { User user = new User(); user.setAge(20); user.setName(\"张三\"); //将实体对象进行包装,包装为操作条件 QueryWrapper<User> wrapper = new QueryWrapper<>(user); int result = this.userMapper.delete(wrapper); System.out.println(\"result = \" + result); }
delete
deleteBatchIds根据id集合批量删除
删除操作
selectById
selectBatchIds
selectOne
selectCount
selectList
@Configuration @MapperScan(\"cn.itcast.mp.mapper\") //设置mapper接口的扫描包 public class MybatisPlusConfig { /*** 分页插件 */ @Bean public PaginationInterceptor paginationInterceptor() {return new PaginationInterceptor(); }}
selectPage
查询操作
通用CRUD
在MP中,ISqlInjector负责SQL的注入工作,它是一个接口,AbstractSqlInjector是它的实现类
SQL注入的原理
configLocation
mapperLocations
typeAliasesPackage
类型: boolean默认值: true
是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN(下划线命名) 到经典 Java 属性名 aColumn(驼峰命名) 的类似映射。
此属性在 MyBatis 中原默认值为 false,在 MyBatis-Plus 中,此属性也将用于生成最终的 SQL 的 select body如果您的数据库命名符合规则无需使用 @TableField 注解指定数据库字段名
#关闭自动驼峰映射,该参数不能和mybatis-plus.config-location同时存在 mybatis-plus.configuration.map-underscore-to-camel-case=false
mapUnderscoreToCamelCase
全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存,默认为 true。
mybatis-plus.configuration.cache-enabled=false 1
cacheEnabled
进阶配置
类型: com.baomidou.mybatisplus.annotation.IdType默认值: ID_WORKER
全局默认主键类型,设置后,即可省略实体对象中的@TableId(type = IdType.AUTO)配置
idType
类型: String默认值: null
tablePrefix
DB 策略配置
allEq
基本比较操作
likeLIKE '%值%'例: like(\"name\
模糊查询
orderBy
排序
逻辑查询
在MP查询中,默认查询所有的字段,如果有需要也可以通过select方法进行指定字段
select
条件构造器
ActiveRecord也属于ORM(对象关系映射)层,由Rails最早提出,遵循标准的ORM模型:表映射到记录,记录映射到对象,字段映射到对象属性。配合遵循的命名和配置惯例,能够很大程度的快速实现模型的操作,而且简洁易懂
每一个数据库表对应创建一个类,类的每一个对象实例对应于数据库中表的一行记录;通常表的每个字段在类中都有相应的Field;ActiveRecord同时负责把自己持久化,在ActiveRecord中封装了对数据库的访问,即CURD;;ActiveRecord是一种领域模型(Domain Model),封装了部分业务逻辑;
主要思想
ActiveRecord(简称AR)一直广受动态语言( PHP 、 Ruby 等)的喜爱
@Data @NoArgsConstructor @AllArgsConstructor public class User extends Model<User> {
在MP中,开启AR非常简单,只需要将实体对象继承Model即可
根据主键查询
新增数据
根据条件查询
ActiveRecord
1. 拦截执行器的方法2. 拦截参数的处理3. 拦截结果集的处理4. 拦截Sql语法构建的处理
/*** 自定义拦截器 */ @Bean public MyInterceptor myInterceptor(){ return new MyInterceptor(); }
注入到spring容器中
或者通过xml配置,mybatis-config.xml
mybatis的插件机制
在MP中提供了对SQL执行的分析的插件,可用作阻断全表更新、删除的操作,注意:该插件仅适用于开发环境,不适用于生产环境。
当执行全表更新时,会抛出异常,这样有效防止了一些误操作
springboot配置
执行分析插件
性能分析拦截器,用于输出每条 SQL 语句及其执行时间,可以设置最大执行时间,超过时间会抛出异常
性能分析插件
意图:当要更新一条记录的时候,希望这条记录没有被别人更新乐观锁实现方式:取出记录时,获取当前version更新时,带上这个version执行更新时, set version = newVersion where version = oldVersion如果version不对,就更新失败
主要适用场景
spring xml:<bean class=\"com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor\"/>spring boot:@Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); }
插件配置
注解实体字段
特别说明
乐观锁插件
Mybatis-Plus的插件
其他的Mapper都可以继承该Mapper,这样实现了统一的扩展。
编写MyBaseMapper
如果直接继承AbstractSqlInjector的话,原有的BaseMapper中的方法将失效,所以我们选择继承DefaultSqlInjector进行扩展
编写MySqlInjector
编写FindAll
注册到Spring容器
Sql 注入器实现自定义全局操作
添加@TableField注解
编写MyMetaObjectHandler
自动填充功能
开发系统时,有时候在实现功能时,删除操作需要实现逻辑删除,所谓逻辑删除就是将数据标记为删除,而并非真正的物理删除(非DELETE操作),查询时需要携带状态条件,确保被标记的数据不被查询到。这样做的目的就是避免数据被真正的删除。
修改表结构
逻辑删除
定义枚举
修改实体
通用枚举
AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、MapperXML、Service、Controller 等各个模块的代码,极大的提升了开发效率
pom.xml
代码生成器
MybatisX 是一款基于 IDEA 的快速开发插件,为效率而生。安装方法:打开 IDEA,进入 File -> Settings -> Plugins -> Browse Repositories,输入 mybatisx 搜索并安装
Java 与 XML 调回跳转Mapper 方法自动生成 XML
MybatisX 快速开发插件
MyBatis Plus
SpringBoot是由Pivotal团队提供的全新框架,其设计目的是用来简化Spring应用的初始搭建以及开发过程
创建过程
基于Idea创建SpringBoot工程 基于官网创建SpringBoot工程 基于阿里云创建SpringBoot工程 手工创建Maven工程修改为SpringBoot工程
创建SpringBoot工程的四种方式
Setting → File Types → Ignored Files and Folders 输入要隐藏的文件名,支持*号通配符 回车确认添加
Idea中隐藏指定文件或指定类型文件
起步依赖(简化依赖配置)自动配置(简化常用工程相关配置)辅助功能(内置服务器,……)
springboot程序优点相较于spring
1. 开发SpringBoot程序要继承spring-boot-starter-parent2. spring-boot-starter-parent中定义了若干个依赖管理3. 继承parent模块可以避免多个依赖使用相同技术时出现依赖版本冲突4. 继承parent的形式也可以采用引入依赖的形式实现效果
parent
SpringBoot中常见项目名称,定义了当前项目使用的所有依赖坐标,以达到减少依赖配置的目的
starter
引导类
内置tomcat
入门案例解析
quickstart
1. 在工作空间中复制对应工程,并修改工程名称2. 删除与Idea相关配置文件,仅保留src目录与pom.xml文件3. 修改pom.xml文件中的artifactId与新工程/模块名相同4. 删除name标签(可选)5. 保留备份工程供后期使用
原则 保留工程基础结构 抹掉原始工程痕迹
复制工程
文件加载顺序
yaml语法规则
使用@Value读取单个数据,属性名引用方式:${一级属性名.二级属性名……}
yaml数据读取
1. 使用@ConfigurationProperties注解绑定配置信息到封装类中2. 封装类需要定义为Spring管理的bean,否则无法进行属性注入
yaml
自动提升功能消失解决方案
属性配置
基础配置
1. 测试类如果存在于引导类所在包或子包中无需指定引导类2. 测试类如果不存在于引导类所在的包或子包中需要通过classes属性指定引导类
1. 导入测试对应的starter2. 测试类使用@SpringBootTest修饰3. 使用自动装配的形式添加要测试的对象
整合JUnit
驱动类过时,提醒更换为com.mysql.cj.jdbc.Driver
核心配置:数据库连接相关信息(连什么?连谁?什么权限)映射配置:SQL映射(XML/注解)
整合MyBatis
整合MyBatis-Plus
整合Druid
整合第三方技术
常用注解@Data
实体类开发————使用Lombok快速制作实体类
导入MyBatisPlus与Druid对应的starter
配置数据源与MyBatisPlus对应的基础配置(id生成策略使用数据库自增策略)
@Mapperpublic interface BookDao extends BaseMapper<Book> {}
继承BaseMapper并指定泛型继承BaseMapper并指定泛型
开启日志
分页操作是在MyBatisPlus的常规操作基础上增强得到,内部是动态的拼写SQL语句,因此需要增强对应的功能,使用MyBatisPlus拦截器实现
条件查询功能
分页功能
Dao开发————整合MyBatisPlus,制作数据层测试类
定义接口
定义实现类
快速开发方案
Service开发————基于MyBatisPlus进行增量开发,制作业务层测试类
Controller开发————基于Restful开发,使用PostMan测试接口功能
Controller开发————前后端开发协议制作
页面开发————基于VUE+ElementUI制作,前后端联调,页面数据处理,页面消息处理
列表、新增、修改、删除、分页、查询
项目异常处理
按条件查询————页面功能调整、Controller修正功能、Service修正功能
基于SpringBoot的SSMP整合案例
①:对SpringBoot项目打包(执行Maven构建指令package)mvn package
②:运行项目(执行启动指令)java –jar springboot.jar
jar支持命令行启动需要依赖maven插件支持,请确认打包时是否具有SpringBoot对应的maven插件
普通工程
基于spring-boot-maven-plugin打包的工程
jar包描述文件(MANIFEST.MF)
# 查询端口netstat -ano# 查询指定端口netstat -ano |findstr \"端口号\"# 根据进程PID查询进程名称tasklist |findstr \"进程PID号\"# 根据PID杀死任务taskkill /F /PID \"进程PID号\"# 根据进程名称杀死任务taskkill -f -t -im \"进程名称\"
命令行启动常见问题及解决方案
• 程序打包与运行(Windows版)
基于Linux(CenterOS7)安装JDK,且版本不低于打包时使用的JDK版本安装包保存在/usr/local/自定义目录中或$HOME下其他操作参照Windows版进行
• 程序运行(Linux版)
打包与运行
看https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config
属性加载优先顺序
开发环境
• 临时属性设置
1级: file :config/application.yml 【最高】 2级: file :application.yml3级:classpath:config/application.yml4级:classpath:application.yml 【最低】
SpringBoot中4级配置文件
1级与2级留做系统打包后设置通用属性,1级常用于运维经理进行线上整体项目部署方案调控3级与4级用于系统开发阶段设置通用属性,3级常用于项目经理进行整体项目属性调控
作用:
• 配置文件分类
通过启动参数加载配置文件(无需书写配置文件扩展名)
通过启动参数加载指定文件路径下的配置文件
通过启动参数加载指定文件路径下的配置文件时可以加载多个配置
单服务器项目:使用自定义配置文件需求较低多服务器项目:使用自定义配置文件需求较高,将所有配置放置在一个目录中,统一管理基于SpringCloud技术,所有的服务器将不再设置配置文件,而是通过配置中心进行设定,动态加载配置信息
• 自定义配置文件
配置高级
• 多环境开发(YAML版)
1. 主启动配置文件application.ymlspring:profiles:active: dev2. 环境分类配置文件application-pro.ymlserver:port: 803. 环境分类配置文件application-dev.ymlserver:port: 814. 环境分类配置文件application-test.ymlserver:port: 82
主配置文件中设置公共配置(全局)环境分类配置文件中常用于设置冲突属性(局部)
多环境开发配置文件书写技巧(一)
主启动配置文件application.propertiesspring.profiles.active=pro 环境分类配置文件application-pro.propertiesserver.port=80 环境分类配置文件application-dev.propertiesserver.port=81 环境分类配置文件application-test.propertiesserver.port=82
根据功能对配置文件中的信息进行拆分,并制作成独立的配置文件,命名规则如下 application-devDB.yml application-devRedis.yml application-devMVC.yml
多环境开发使用group属性设置配置文件分组,便于线上维护管理
多环境开发独立配置文件书写技巧(二)
• 多环境开发(Properties版)
Maven中设置多环境属性
SpringBoot中引用Maven属性
1. 当Maven与SpringBoot同时对多环境进行控制时,以Mavn为主,SpringBoot使用@..@占位符读取Maven对应的配置属性值2. 基于SpringBoot读取Maven配置属性的前提下,如果在Idea下测试工程时pom.xml每次更新需要手动compile方可生效
Maven与SpringBoot多环境兼容
• 多环境开发控制
多环境开发
1 编程期调试代码2 运营期记录信息 记录日常运营重要信息(峰值流量、平均响应时长……) 记录应用报错信息(错误堆栈) 记录运维过程数据(扩容、宕机、报警……)
添加日志操作记录
TRACE:运行堆栈信息,使用率低DEBUG:程序员调试代码使用INFO:记录运维过程数据WARN:记录运维过程报警数据ERROR:记录错误堆栈信息FATAL:灾难信息,合并计入ERROR
设置日志输出级别
设置日志组,控制指定包对应的日志输出级别,也可以直接控制指定包对应的日志输出级别
使用lombok提供的注解@Slf4j简化开发,减少日志对象的声明操作
优化日志对象创建代码
• 日志基础
设置日志输出格式
• 日志输出格式控制
设置日志文件
日志文件详细配置
• 日志文件
日志
运维实用
开启开发者工具
激活热部署:Ctrl + F9
重启(Restart):自定义开发代码,包含类、页面、配置文件等,加载位置restart类加载器重载(ReLoad):jar包,加载位置base类加载器
手动启动热部署
激活方式:Idea失去焦点5秒后启动热部署
自动启动热部署
/META-INF/maven/META-INF/resources/resources/static/public/templates
默认不触发重启的目录列表
img src=\
热部署范围配置
禁用热部署
关闭热部署
热部署
使用@ConfigurationProperties为第三方bean绑定属性
可以将使用@ConfigurationProperties注解对应的类加入Spring容器
@EnableConfigurationProperties与@Component不能同时使用
@EnableConfigurationProperties
@ConfigurationProperties
@ConfigurationProperties绑定属性支持属性名宽松绑定
宽松绑定不支持注解@Value引用单个属性的方式
绑定前缀名命名规范:仅能使用纯小写字母、数字、下划线作为合法的字符
宽松绑定/松散绑定
SpringBoot支持JDK8提供的时间与空间计量单位
JDK8支持的时间与空间计量单位
常用计量单位绑定
开启数据校验有助于系统安全性,J2EE规范中JSR303规范定义了一组有关数据校验相关的API
添加JSR303规范坐标与Hibernate校验框架对应坐标
对bean开启校验功能
设置校验规则
开启Bean数据校验
数据校验
在启动测试环境时可以通过properties参数设置测试环境专用的属性
优势:比多环境开发中的测试环境影响范围更小,仅对当前测试类有效
在启动测试环境时可以通过args参数设置测试环境专用的传入参数
加载测试专用属性
使用@Import注解加载当前测试类专用的配置
加载测试专用配置
模拟端口
虚拟请求测试
虚拟请求状态匹配
虚拟请求响应体匹配
虚拟请求响应体(json)匹配
虚拟请求响应头匹配
Web环境模拟测试
为测试用例添加事务,SpringBoot会对测试用例对应的事务提交操作进行回滚
如果想在测试用例中提交事务,可以通过@Rollback注解设置
数据层测试回滚
测试用例数据通常采用随机值进行测试,使用SpringBoot提供的随机数为其赋值
测试用例数据设定
格式一
格式二
数据源配置格式
HikariCP:默认内置数据源对象Tomcat提供DataSource HikariCP不可用的情况下,且在web环境中,将使用tomcat服务器配置的数据源对象Commons DBCP Hikari不可用,tomcat数据源也不可用,将使用dbcp数据源
SpringBoot提供了3种内嵌的数据源对象供开发者选择
通用配置无法设置具体的数据源配置信息,仅提供基本的连接相关配置,如需配置,在下一级配置中设置具体设定
数据源配置
jdbc-template配置
内置持久化解决方案——JdbcTemplate
数据层解决方案
导入坐标
设置访问数据源
H2数据库控制台仅用于开发阶段,线上项目请务必关闭控制台功能
SpringBoot可以根据url地址自动识别数据库种类,在保障驱动类存在的情况下,可以省略配置
H2
SpringBoot提供了3种内嵌数据库供开发者选择,提高开发测试效率H2HSQLDerby
内嵌数据库
Redis是一款key-value存储结构的内存级NoSQL数据库支持多种数据存储格式支持持久化支持集群
导入SpringBoot整合Redis坐标
配置Redis(采用默认配置)
RedisTemplate提供操作各种数据存储类型的接口API
客户端:RedisTemplate
客户端:RedisTemplate以对象作为key和value,内部对数据进行序列化
配置属性
lettcus与jedis区别
客户端选择:jedis
Redis
MongoDB是一个开源、高性能、无模式的文档型数据库。NoSQL数据库产品中的一种,是最像关系型数据库的非关系型数据库
解压缩后设置数据目录
mongod --dbpath=..\\data\\db
服务端启动
mongo --host=127.0.0.1 --port=27017
客户端启动
window安装MongoDB问题解决
可视化客户端——Robo 3T
db.集合名称.insert/save/insertOne(文档)
新增
db.集合名称.remove(条件)
db.集合名称.update(条件,{操作种类:{文档}})
查询全部:db.集合.find();
查第一条:db.集合.findOne()
查询指定数量文档:db.集合.find().limit(10) //查10条文档
跳过指定数量文档:db.集合.find().skip(20) //跳过20条文档
统计:db.集合.count()
排序:db.集合.sort({age:1}) //按age升序排序
客户端读写
配置客户端
导入Mongodb驱动
Solr
RedisMongoESSolr
NoSQL
定义:缓存是一种介于数据永久存储介质与数据应用之间的数据临时存储介质
缓存是一种介于数据永久存储介质与数据应用之间的数据临时存储介质使用缓存可以有效的减少低速数据读取过程的次数(例如磁盘IO),提高系统性能缓存不仅可以用于提高永久性存储介质的数据读取效率,还可以提供临时的数据存储空间
启用缓存
设置数据进入缓存
启用缓存设置进入缓存的数据设置读取缓存的数据
Generic
JCache
缓存供应商变更:Ehcache
Ehcache
Hazelcast
Infinispan
Couchbase
Caffeine
Simple
memcached
SpringBoot提供的缓存技术除了提供默认的缓存方案,还可以对其他缓存技术进行整合,统一接口,方便缓存技术的开发与管理
输入手机号获取验证码,组织文档以短信形式发送给用户(页面模拟)输入手机号和验证码验证结果
提供controller,传入手机号,业务层通过手机号计算出独有的6位验证码数据,存入缓存后返回此数据提供controller,传入手机号与验证码,业务层通过手机号从缓存中读取验证码与输入验证码进行比对,返回比对结果
开启缓存
业务层接口
业务层设置获取验证码操作,并存储缓存,手机号为key,验证码为value
业务层设置校验验证码操作,校验码通过缓存读取,返回校验结果
缓存供应商变更Ehcache
缓存变更——redis
memcached客户端选择Memcached Client for Java:最早期客户端,稳定可靠,用户群广SpyMemcached:效率更高Xmemcached:并发处理更好SpringBoot未提供对memcached的整合,需要使用硬编码方式实现客户端初始化管理
下载memcached地址:https://www.runoob.com/memcached/window-install-memcached.html
memcache
jetCache对SpringCache进行了封装,在原有功能基础上实现了多级缓存、缓存统计、自动刷新、异步调用、数据报表等功能jetCache设定了本地缓存与远程缓存的多级缓存解决方案本地缓存(local)LinkedHashMap Caffeine远程缓存(remote)Redis Tair
jetcache
配置使用j2cache(application.yml)
j2cache是一个缓存整合框架,可以提供缓存的整合方案,使各种缓存搭配使用,自身不提供缓存功能基于 ehcache + redis 进行整合
j2cache
缓存使用案例——手机验证码
缓存
相关概念工作(Job):用于定义具体执行的工作工作明细(JobDetail):用于描述定时工作相关的信息触发器(Trigger):用于描述触发工作的规则,通常使用cron表达式定义调度规则调度器(Scheduler):描述了工作明细与触发器的对应关系
整合
SpringBoot整合Quartz
定时任务是企业级应用中的常见操作年度报表缓存统计报告… …市面上流行的定时任务技术QuartzSpring Task
任务
SMTP(Simple Mail Transfer Protocol):简单邮件传输协议,用于发送电子邮件的传输协议
POP3(Post Office Protocol - Version 3):用于接收电子邮件的标准协议
IMAP(Internet Mail Access Protocol):互联网消息协议,是POP3的替代协议
SpringBoot整合JavaMail
邮件
同步消息
异步消息
JMS(Java Message Service):一个规范,等同于JDBC规范,提供了与消息服务相关的API接口
peer-2-peer:点对点模型,消息发送到一个队列中,队列保存消息。队列的消息只能被一个消费者消费,或超时
publish-subscribe:发布订阅模型,消息可以被多个消费者消费,生产者和消费者完全独立,不需要感知对方的存在
JMS消息模型
TextMessageMapMessageBytesMessageStreamMessageObjectMessageMessage
JMS消息种类
JMS实现:ActiveMQ、Redis、HornetMQ、RabbitMQ、RocketMQ(没有完全遵守JMS规范)
JMS实现
AMQP(advanced message queuing protocol):一种协议(高级消息队列协议,也是消息代理规范),规范了网络交换的数据格式,兼容JMS
优点:具有跨平台性,服务器供应商,生产者,消费者可以使用不同的语言来实现
direct exchangefanout exchangetopic exchangeheaders exchangesystem exchange
AMQP消息模型
AMQP消息种类:byte[]
AMQP实现:RabbitMQ、StormMQ、RocketMQ
AMQP
MQTT(Message Queueing Telemetry Transport)消息队列遥测传输,专为小设备设计,是物联网(IOT)生态系统中主要成分之一
MQTT
企业级应用中广泛使用的三种异步消息传递技术
Kafka,一种高吞吐量的分布式发布订阅消息系统,提供实时消息功能。
Kafka
消息发送方 生产者消息接收方 消费者
下载地址:https://activemq.apache.org/components/classic/download/安装:解压缩
启动服务activemq.bat访问服务器http://127.0.0.1:8161/服务端口:61616,管理后台端口:8161用户名&密码:admin
ActiveMQ
RabbitMQ基于Erlang语言编写,需要安装Erlang
Erlang下载地址:https://www.erlang.org/downloads安装:一键傻瓜式安装,安装完毕需要重启,需要依赖Windows组件环境变量配置ERLANG_HOMEPATH
启动服务rabbitmq-service.bat start关闭服务rabbitmq-service.bat stop查看服务状态rabbitmqctl status
服务管理可视化(插件形式)查看已安装的插件列表rabbitmq-plugins.bat list
开启服务管理插件rabbitmq-plugins.bat enable rabbitmq_management
访问服务器http://localhost:15672
服务端口:5672,管理后台端口:15672用户名&密码:guest
下载地址:https://rocketmq.apache.org/
安装:解压缩默认服务端口:9876
ROCKETMQ_HOME
PATH
NAMESRV_ADDR (建议): 127.0.0.1:9876
环境变量配置
命名服务器与broker
mqnamesrv
启动命名服务器
mqbroker
启动broker
tools org.apache.rocketmq.example.quickstart.Producer
服务器功能测试:生产者
tools org.apache.rocketmq.example.quickstart.Consumer
服务器功能测试:消费者
RocketMQ
下载地址:https://kafka.apache.org/downloadswindows
系统下3.0.0版本存在BUG,建议使用2.X版本安装:解压缩
默认端口:2181
启动zookeeperzookeeper-server-start.bat ..\\..\\config\\zookeeper.properties
默认端口:9092
启动kafkakafka-server-start.bat ..\\..\\config\\server.properties
kafka-topics.bat --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic itheima
创建topic
kafka-topics.bat --zookeeper 127.0.0.1:2181 --list
查看topic
kafka-topics.bat --delete --zookeeper localhost:2181 --topic itheima
删除topic
kafka-console-producer.bat --broker-list localhost:9092 --topic itheima
生产者功能测试
kafka-console-consumer.bat --bootstrap-server localhost:9092 --topic itheima --from-beginning
消费者功能测试
消息案例——订单短信通知
消息
监控服务状态是否宕机
监控服务运行指标(内存、虚拟机、线程、请求等)
监控日志
管理服务(服务下线)
显示监控信息的服务器:用于获取服务信息,并显示对应的信息运行的服务:启动时主动上报,告知监控服务器自己需要受到监控
监控的实施方式
监控的意义
注意版本一致
Spring Boot Admin,开源社区项目,用于管理和监控SpringBoot应用程序。 客户端注册到服务端后,通过HTTP请求方式,服务端定期从客户端获取对应的信息,并通过UI界面展示对应信息。
可视化监控平台
Actuator提供了SpringBoot生产就绪功能,通过端点的配置与访问,获取端点信息
端点描述了一组监控信息,SpringBoot提供了多个内置端点,也可以根据需要自定义端点信息
访问当前应用所有端点信息:/actuator
访问端点详细信息:/actuator/端点名称
Web程序专用端点
监控原理
为info端点添加自定义指标
为Health端点添加自定义指标
为Metrics端点添加自定义指标
自定义端点
自定义监控指标
开发实用
一,XML方式声明bean
使用@Component及其衍生注解@Controller 、@Service、@Repository定义bean
使用@Bean定义第三方bean,并将所在类定义为配置类或Bean
注解方式声明配置类
二、XML+注解方式声明bean
初始化实现FactoryBean接口的类,实现对bean加载到容器之前的批处理操作
扩展1
加载配置类并加载配置文件(系统迁移)
扩展2
使用proxyBeanMethods=true可以保障调用此方法得到的对象是从容器中获取的而不是重新创建的
扩展3
被导入的bean无需使用注解声明为bean
使用@Import注解导入要注入的bean对应的字节码
bean的加载方式(四)
bean的加载方式——扩展4
使用上下文对象在容器初始化完毕后注入bean
bean的加载方式(五)
导入实现了ImportSelector接口的类,实现对导入源的编程式处理
bean的加载方式(六)
导入实现了ImportBeanDefinitionRegistrar接口的类,通过BeanDefinition的注册器注册实名bean,实现对容器中bean的裁定,例如对现有bean的覆盖,进而达成不修改源代码的情况下更换实现的效果
bean的加载方式(七)
导入实现了BeanDefinitionRegistryPostProcessor接口的类,通过BeanDefinition的注册器注册实名bean,实现对容器中bean的最终裁定
bean的加载方式(八)
bean加载方式
bean的加载控制指根据特定情况对bean进行选择性加载以达到适用于项目的目标
使用@Conditional注解的派生注解设置各种组合条件控制bean的加载
匹配指定类
未匹配指定类
匹配指定类型的bean
匹配指定名称的bean
匹配指定环境
根据任意条件确认是否加载bean
bean的加载方式5-8可以控制
bean加载控制
将业务功能bean运行需要的资源抽取成独立的属性类(******Properties),设置读取配置文件信息
配置文件中使用固定格式为属性类注入数据
定义业务功能bean,通常使用@Import导入,解耦强制加载bean
使用@EnableConfigurationProperties注解设定使用属性类时加载bean
bean依赖属性配置
自动配置原理
自定义自动配置(META-INF/spring.factories)
控制SpringBoot内置自动配置类加载
变更自动配置:去除tomcat自动配置(条件激活),添加jetty自动配置(条件激活)
变更自动配置
自动配置
每次访问网站行为均进行统计
后台每10秒输出一次监控信息(格式:IP+访问次数)
1. 数据记录位置:Map / Redis
① 步骤一:降低难度,主动调用,仅统计单一操作访问次数(例如查询)
② 步骤二:开发拦截器
2. 功能触发位置:每次web请求(拦截器)
输出频度,默认10秒
数据特征:累计数据 / 阶段数据,默认累计数据
输出格式:详细模式 / 极简模式
3. 业务参数(配置项)
4. 校验环境,设置加载条件
自动配置类
模拟调用
开启定时任务功能
设置定时任务
业务功能开发
记录系统访客独立IP访问次数
统计独立IP访问次数
定义属性类,加载对应属性
设置加载Properties类为bean
根据配置切换设置
极简模式报表模板
配置信息
自定义bean名称
放弃配置属性创建bean方式,改为手工控制
使用#{beanName.attrName}读取bean的属性
自定义拦截器
设置核心配置类,加载拦截器
自定义starter
导入配置处理器坐标
进行自定义提示功能开发
辅助功能开发
读取环境属性(Environment)
系统配置(spring.factories)
参数(Arguments、application.properties)
1. 初始化各种属性,加载成对象
2. 创建Spring容器对象ApplicationContext,加载各种配置
3. 在容器创建前,通过监听器机制,应对不同阶段加载数据、更新数据的需求
springboot启动流程
4. 容器初始化过程中追加各种功能,例如统计时间、输出日志等
容器类型选择
1. 在应用运行但未进行任何处理时,将发送 ApplicationStartingEvent。
2. 当Environment被使用,且上下文创建之前,将发送 ApplicationEnvironmentPreparedEvent。
3. 在开始刷新之前,bean定义被加载之后发送 ApplicationPreparedEvent。
4. 在上下文刷新之后且所有的应用和命令行运行器被调用之前发送 ApplicationStartedEvent。
5. 在应用程序和命令行运行器被调用之后,将发出 ApplicationReadyEvent,用于通知应用已经准备处理请求。
6. 启动时发生异常,将发送 ApplicationFailedEvent。
监听器
核心原理
原理篇
SpringBoot
Maven项目模块构建
powerdesigner构架数据库
ElementUI使用
基础环境搭建
检查项
功能开发
Redis使用
传智健康项目
框架

收藏

收藏
0
条评论
回复
删除
下一页
PO_df534d
职业:本科
作者其他创作:
学习总结
1930
2024-01-10
CentralTopic分享
37
为了保护您和被举报人的权利,请您慎重填写举报原因,我们会认真核实调查。