文章目录
- 四. tomcat服务器(web项目代码的容器)
- CS和BS的异同点
- 新建项目-部署-运行-访问
- 新建web项目,在tomcat部署
- 运行和访问
- 在idea新建java web项目
- web项目的配置
- lib文件夹和artifact的关系
- 部署
- 进一步说明
- 什么叫web项目
- 四:JDBC和DAO
- java se + 数据库项目实战
- 一些概念
- 项目架构
- 项目需求
- 五. servelet
- 一个简单的例子
- xxxServlet类
- xxxServlet类实现第一个功能-获取来自客户端的数据
- http request(http请求)
- 获取数据
- 重写继承的哪一个方法以及实例化哪一个xxxServlet类
- xxxServlet类的设计
- 对请求-响应执行顺序的理解
- xxxServlet类实现第二个功能-调用DAO完成对数据库的操作
- xxxServlet类实现第三个功能-在控制台打印相应的操作成功
- 处理http request中的中文乱码
- Servlet的继承树和service方法
- 继承树
- service方法
- servlet的生命周期
- servlet的初始化时机
- tomcat容器与servlet
- http协议
- 会话-session-http是无状态的
- http是无状态的
- 会话跟踪
- session保存作用域
- 服务器端转发和客户端重定向
- 服务器端转发
- 客户端重定向
- thymeleaf
- 为什么使用thymeleaf
- 配置thymeleaf的步骤
- 如何实现大量统一格式的数据在html页面的展示
- 回顾
- servlet保存作用域
- 作用域解释
- HttpServletRequest容器
- HttpSession容器
- ServletContext容器
- 绝对路径和相对路径
- 区分超链接和超文本引用
正式进入后端
四. tomcat服务器(web项目代码的容器)
CS和BS的异同点
CS:client-server架构模式
充分利用客户端机器的资源,减轻服务器的负荷(将一部分安全要求不高额计算或者存储任务放在客户端,减轻服务器压力以及减轻网络负荷)
但是,客户端需要单独安装,也需要升级维护(成本较高,以前的升级很麻烦,打补丁包,可能有多个)
BS:browser-server架构模式
不需要单独安装客户端
但是所有的计算、存储任务都在服务器吨,服务器负荷重。同时,计算结果要通过网络传输给用户,因此浏览器和服务器会进行频繁的网络通信,网络负荷重
新建项目-部署-运行-访问
tomcat是服务器端的一个webcontainer,免费,体积小,性能还行
可以在tomcat中安装项目,这个过程又叫做“部署”deploy,也就是把写好的程序拷贝到相应的项目目录下
项目名称又叫做context root
bin:可执行文件目录
conf:配置文件目录
lib:tomcat使用java和C开发,依赖
logs:日志
webapps:部署项目的空间,早期做法,现在可以自定义,workspace
work:项目的目录
配置环境变量
tomcat中的bin/start.bat需要jvm,因此需要告诉tomcat服务器jdk的位置(jre包含在jdk中)。也就是在环境变量中创建JDK_HOME
启动tomcat服务(就像mysql服务器),访问主页
先启动bin/start.bat,然后输入url,http://localhost:8080/
注意不是https,而是http,如果是前者,报错
启动start.bat之后,可以看到一个进程msedge.exe占用了8080端口
使用netstat -ano|findstr 8080命令可以查看
新建web项目,在tomcat部署
在webapps文件夹下,新建文件夹baidu,作为web项目目录
在baidu目录下新建文件夹WEB-INF,目录名全部大写
一个web项目基本结构就是这样
运行和访问
在baidu目录下导入项目文件
然后重启start.bat,也就是tomcat服务,再在浏览器输入http://localhost:8080/baidu/demo09.html就可以看到
这是很激动人心的时刻,就是说我们从客户端访问到了服务器的资源
曾经的我们打开.html文件是在本地,打开页面之后,其地址是
现在资源的地址变成了http://localhost:8080/baidu/demo09.html
localhost:8080通过ip地址和端口号定位到某台计算机上的tomcat服务器
baidu/demo09.html通过路径定位到服务器上的项目
舒服了,这才是互联网
在idea新建java web项目
(1)在idea中配置tomcat服务器。就是首先得让idea找到tomcat
(2)添加框架支持。自动将我们的java module变成web application。帮我们创建了web项目的文件目录
相比于前面我们直接在tomcat安装路径下创建的目录还是有区别
tomcat/webapps/baidu目录下默认会有一个WEB-INF文件夹,我们的项目和WEB-INF在同一级目录
(3)编辑web工程的结构(edit configuration)
也就是加上本地或者远程的tomcat服务器
左起第二个框,是select run/debug configuration,这个地方要选对,如果是java se程序,那么就是程序名;如果是java web项目,就是你配置的服务器名点击下拉菜单,可以编辑当前选中的configuration
然后就可以进行部署等相关设置
(4)部署web项目
也是在edit configuration中完成部署
web项目的配置
有几个比较重要的按键:
(1)右键module,add framework support, 添加框架支持,比如说让一个java se module变成一个java web module
(2)file->project structure,对整个项目的设置进行调整
挺恶心的,全是英文facets:本意是方面,部分。可以用于添加框架支持。比如说将一个java se module变成一个java web module
web项目还需要部署到tomcat服务器中。更准确地说,一个项目要能够部署到服务器中,就必须按照规范先变成一个web项目(创建标准格式的目录),然后再部署到服务器。
之前我们是在tomcat安装目录下的webapps目录下去创建了一个项目文件夹baidu,然后在baidu目录下创建了一个WEB-INF文件夹,接着将项目源码放在和WEB-INF同一级目录下。完成以后,先启动tomcat服务器,然后就可以通过url=http://localhost:8888/baidu/hello.html在浏览器访问到相应的资源
在idea中,web项目的目录结构都不一样了。并不是将源代码部署到服务器,而是将一个部署包部署到tomcat。这个部署包叫做artifact,本意是指人工制品。
artifacts:web项目部署包。
lib文件夹和artifact的关系
lib是我们自定义的依赖,artifact是部署包,artifact中包含了引用lib的信息
先有artifact,如果之后有了新的依赖,必须手动更新artifact。要么删掉重建,要么fix
为了写DAO,或者引入数据库连接池,我们都写过依赖。不管是idea还是eclipse,都由我们自己创建了一个叫做lib的文件夹,然后将相应的驱动直接复制到这个lib文件夹下
接着就有区别了:
对于eclipse,右键驱动,选择build path
对于idea,右键lib文件夹,选择add as library,直接将这个lib文件夹变成依赖
对于idea,module之前是不共享依赖的。因此需要对每一个module添加这个依赖
还是project stucture,选择module,选中module,找右边的dependencies来添加依赖
部署
准备好web项目,依赖,部署包之后,就准备开始部署到tomcat服务器了
这个时候就可以edit configuration了
先添加tomcat服务器
再将artifact部署包部署,也就是添加artifact
下面有个application context,他的值就是一个路径,这个路径会被添加到server选项卡的URL的值得后面URL的值默认是http://localhost:8080,默认访问的顺序是
这是在tomcat服务器中的默认设置,在config/web.xml中设置好的
假设 application context=/pro,那么URL=http://localhost:8080/pro
进一步说明
什么叫web项目
第一印象里,web项目是说项目里边必须包含web相关的固定格式的目录。在project structure里,我们可以看到web项目的module里边包含web
具体来说,有了web意味着什么呢??有什么变化???
位置1是WEB-INF中web.xml文件的地址,叫做deployment description
位置2是web文件夹的地址,叫做web resource directory
位置3是src文件夹的地址,叫做source roots
这样看下来,不就是把web项目重要的三类资源的起始目录给记录下来了吗
四:JDBC和DAO
虽然前面已经学习过这两部分内容,但是我们需要在java web的背景下再回顾
JDBC:实现通过java控制数据库
DAO:管理代码的一种方式。将操作数据库的原子操作封装在BaseDAO中
然后针对每一张数据表设计对应的映射类,接口,DAO
在引入java web以前,我们学习到的java se的知识已经可以支撑应用软件的基本开发
SSM:spring, springmvc, mabatis
java se + 数据库项目实战
一些概念
之前,我们是将数据表的映射类放在了java bean里边,但是我们对java bean的元素有相当严格的界定。现在是将映射类放在pojo里边
POJO是Plain OrdinaryJava Object的缩写,实质上可以理解为简单的实体类,顾名思义POJO类的作用是方便程序员使用数据库中的数据表,对于广大的程序员,可以很方便的将POJO类当做对象来进行使用,当然也是可以方便的调用其get,set方法。POJO类也给我们在struts框架中的配置带来了很大的方便
JavaBean则比 POJO复杂很多, Java Bean 是可复用的组件,对 Java Bean 并没有严格的规范,理论上讲,任何一个 Java 类都可以是一个 Bean。但是,javabean还是有一些必要的结构:
(1)通常情况下,由于 Java Bean 是被容器所创建(如 Tomcat) 的,所以 Java Bean 应具有一个无参的构造器
(2)实现 Serializable 接口用于实现 Bean 的持久性
(3)不能被跨进程访问
一般在web应用程序中建立一个数据库的映射对象时,我们只能称它为POJO。POJO(Plain Old Java Object)这个名字用来强调它是一个普通java对象,而不是一个特殊的对象,其主要用来指代那些没有遵从特定的Java对象模型、约定或框架(如EJB)的Java对象。理想地讲,一个POJO是一个不受任何限制的Java对象(除了Java语言规范)
项目架构
BaseDAO类:通用的增删改查操作
xxxDAO接口:针对某一张数据表的原子操作,使用继承与BaseDAO的方法实现
xxxDAOImpl类:重写xxxDAO接口中的所有方法
然后在具体的业务模块,我们会设计大量的业务方法,这些业务方法由xxxDAO接口定义的原子操作来实现
总结:业务方法由xxxDAOImpl类实现类中的原子操作实现,xxxDAOImpl实现类中的原子操作由BaseDAO中的原子操作实现
项目需求
有这样一个场景:在下单的时候,订单表中显示谁在什么时间买了什么东西
而具体买了什么在另外一张表 订单详情表 呈现:
这样一来就产生了一个新的需求:需要获取订单表的id来访问订单详情表的某一条记录。而BaseDAO中通用的增删改这一原子操作是不返回id的
需求:在增加数据完成后返回自增列的值
之前我都忽略了一点,conn.preparedStatemetn是有返回值的,返回int
一个新知识,conn.preparedStatemetn的重载方法返回一个ResultSet实例,但是方法的参数要增加
对于删和改没有上面这一需求,只针对增这一操作。因此,我们需要解析字符串sql,看是不是以insert开头。调用下面的方法进行解析sql.trim().toUpperCase().startsWith("INSERT")
trim方法,去掉字符串首尾空格;toUpperCase方法,将字符串全转为大写;startsWith方法,从首字母开始匹配,看是否能匹配到目标字符串
五. servelet
在后端编程中,我们需要一些逻辑结构,比如servlet(server applet,服务器连接器,applet的本意是小程序),JSP
HTML:页面的结构
CSS: 页面的美化
JS:页面的行为
JQuery:封装了JS
VUE:封装了JS(简单)
React:封装了JS
TOMCAT:服务器
xml:可以自定义标签,用来写配置文件
JSON:服务器向浏览器传数据以JSON的格式,轻量级,更易解析
servlet:用于和浏览器交互,tomcat中最重要的组件获取请求,处理请求,响应请求
filter:过滤器,比如说购物车点击购买,会先看是否登录等等
listener:监听器,监听用户的操作
HTTP:浏览器和服务器交互需要遵循的协议
JSP:java服务器页面,响应请求显示的页面,动态页面
EL,JSTL:用于提高JSP的开发效率
会话控制:服务器无法区分请求的来源,通过会话控制区分请求来自于哪个服务器
Ajax:实现异步请求。比如说网站注册,输入完用户名,后面就会判断是否通过,此时我们并没有主动点击注册
一个简单的例子
经过前面对于tomcat服务器的学习,我们知道,当启动tomcat服务器且服务器中有相应的资源时,我们可以通过浏览器输入url(统一资源定位符)打开相应的文件
比如说有这样一个.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="add" method="post">
名称:<input type="text" name="fname"/><br/>
价格:<input type="text" name="price"/><br/>
库存:<input type="text" name="fcount"/><br/>
备注:<input type="text" name="remark"/><br/>
<input type="submit" value="添加" />
</form>
</body>
</html>
(1)通过url,向服务器发起请求
(2)服务器响应请求,返回响应的数据
(3)当我们点击添加按钮,客户端又向服务器发起请求.同时传给服务器的还包括此次进行的操作action,填写好的数据
(4)服务器需要有组件来获取请求,处理请求,响应请求。这个组件就是servlet。从实际代码角度看,我们需要专门设计一个类,用以处理action。比如action = add,那就设计一个叫做AddServlet的类(添加服务器端小程序)
这个类的作用是:
获取来自客户端的数据
调用DAO完成对数据库的操作
在控制台打印相应的操作成功
(5)这个组件可以调用DAO,向数据库服务器中添加数据
xxxServlet类
一个普通的xxxServlet类是无法完成以下功能的:
获取来自客户端的数据
调用DAO完成对数据库的操作
在控制台打印相应的操作成功
服务器厂商,比如说tomcat,会提供一个父类HttpServlet,自定义的xxxServlet类去继承这个父类。这个父类的声明为:public abstract class HttpServlet extends javax.servlet.GenericServlet
,是一个抽象父类
实际上服务器厂商会提供JSP和servlet等许多结构的相关API,都在安装路径的lib文件夹中,我们在使用的时候需要手动导入需要的依赖(dependency)
xxxServlet类实现第一个功能-获取来自客户端的数据
http request(http请求)
万事万物皆对象,http请求也被封装为对象,封装到HttpServletRequest类,这个类来自javax.servlet.http,由服务器厂商提供
获取数据
以前学习计算机网络的时候,知道http报文中携带了数据,他们按照固定的格式排列,服务器提供的API中的抽象类HttpServletRequest提供了getParameter方法来解析http报文
传入getParameter方法的参数就来自于.html文件中的name属性的值
重写继承的哪一个方法以及实例化哪一个xxxServlet类
在上面的.html文件中,我们是这样声明表单的<form action="add" method="post">
重写哪一个方法:
由method属性的值决定。服务器提供的API中的抽象类HttpServletRequest提供了doXxx方法。如果method=“post”,那么就重写doPost方法
实例化哪一个xxxServlet类:
在WEB-INF文件夹下,有一个web.xml文件,在里边建立action属性的值与xxxServlet的映射关系。比如说,当获取到action的值为add时,就实例化AddServlet类
<web-app>
<servlet>
<servlet-name>AddServlet</servlet-name>
<servlet-class>com.atguigu.servlets.AddServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AddServlet</servlet-name>
<url-pattern>/add</url-pattern>
</servlet-mapping>
<!--
1. 用户发请求,action=add
2. 项目中,web.xml中找到url-pattern = /add -> 第12行
3. 找第11行的servlet-name = AddServlet
4. 找和servlet-mapping中servlet-name一致的servlet , 找到第7行
5. 找第8行的servlet-class -> com.atguigu.servlets.AddServlet
6. 用户发送的是post请求(method=post) , 因此 tomcat会执行AddServlet中的doPost方法
-->
</web-app>
xxxServlet类的设计
/**
* @ClassName:AddServlet
* @Package:com.atguigu.servlets
* @Description:用以获取请求,处理请求,响应请求
*
* @Author:jjcale
* @Version:v1.0
*/
public class AddServlet extends HttpServlet {
/**
* 客户端向服务器端上传数据的一种方法叫post,method=post
* 重写这个doPost()方法
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//客户端返回的数据包括属性name的值,这个值有好几个,fname,price,fcount,remark
String fname = request.getParameter("fname");
String priceStr = request.getParameter("price");
Integer price = Integer.parseInt(priceStr);
String fcountStr = request.getParameter("fcount");
Integer fcount = Integer.parseInt(fcountStr);
String remark = request.getParameter("remark");
System.out.println("fname is " + fname + " price is " + price + " fcount is " + fcount + " rema" +
"rk is " + remark);
}
}
对请求-响应执行顺序的理解
<!--
1. 用户发请求,action=add
2. 项目中,web.xml中找到url-pattern = /add -> 第12行
3. 找第11行的servlet-name = AddServlet
4. 找和servlet-mapping中servlet-name一致的servlet , 找到第7行
5. 找第8行的servlet-class -> com.atguigu.servlets.AddServlet
6. 用户发送的是post请求(method=post) , 因此 tomcat会执行AddServlet中的doPost方法
-->
这个地方直觉上还是挺乱的
我的理解是:
(1)执行web项目,不像是一个java se项目,我在某一个程序里边点击run就行了。反正在idea里边,直接点击run就可以,我在.html, .java都直接运行过
(2)注意,我的localhost既是服务器所在的主机,也是客户端所在的主机
(3)首先,我们的web项目通过配置,部署,它是事先知道tomcat的位置的,localhost:8080
(4)启动项目,就相当于客户端发起了请求,请求访问http://localhost:8080/pro07/add.html这一资源
(5)服务器先启动,连接客户端,然后响应
(6)客户端输入相关的数据,点击添加。action属性的值为add,这又是一个http请求。一点击添加,我们在浏览器的检查的网络里边,可以看到一个叫add的请求
<form action="add" method="post">
<input type="submit" value="添加" />
从这里就能看出后端为什么要懂一些前端,像add,post这两个关键字在后端要用,add用于设计子类AddServlet,post用于重写doPost方法
这个请求的具体信息如下:
(7)此时,AddServlet类就该获取请求(获取数据)。我们在doPost方法打印了获取到的数据
(8)然后处理请求,比如说增加数据库的数据
(9)响应请求,比如说弹窗,操作成功
xxxServlet类实现第二个功能-调用DAO完成对数据库的操作
前面设计的xxxServlet类只是打印了从请求中提取的数据,这些数据实际上都可以写入数据库了
有前述内容可知,我们通过xxxDAO这一结构完成与数据库的交互
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String fname = request.getParameter("fname");
String priceStr = request.getParameter("price");
Integer price = Integer.parseInt(priceStr);
String fcountStr = request.getParameter("fcount");
Integer fcount = Integer.parseInt(fcountStr);
String remark = request.getParameter("remark");
FruitDAO fruitDAO = new FruitDAOImpl();
boolean flag = fruitDAO.addFruit(new Fruit(0 , fname , price , fcount , remark));
System.out.println(flag ? "添加成功!" : "添加失败!");
}
xxxServlet类实现第三个功能-在控制台打印相应的操作成功
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println(flag ? "添加成功!" : "添加失败!");
}
处理http request中的中文乱码
背景:如果在名称一栏输入中文,AddServlet类捕获到的值就会是乱码
这是因为HttpServletRequest这个类的实例默认不支持中文造成的
我们知道method有两种值,一个是post,一个是默认情况的get
tomcat8之前,设置编码:
1)get请求方式:
//get方式目前不需要设置编码(基于tomcat8)
//如果是get请求发送的中文数据,转码稍微有点麻烦(tomcat8之前)
String fname = request.getParameter("fname");
//1.将字符串打散成字节数组
byte[] bytes = fname.getBytes("ISO-8859-1");
//2.将字节数组按照设定的编码重新组装成字符串
fname = new String(bytes,"UTF-8");
2)post请求方式:
request.setCharacterEncoding("UTF-8");
tomcat8开始,设置编码,只需要针对post方式
request.setCharacterEncoding("UTF-8");
Servlet的继承树和service方法
继承树
1. 继承关系
javax.servlet.Servlet接口
javax.servlet.GenericServlet抽象类
javax.servlet.http.HttpServlet抽象子类
XxxServlet子类(对应增删改查)
service方法
当服务器收到http request的时候,自动调用service方法
2. 相关方法
javax.servlet.Servlet接口:
void init(config) - 初始化方法
void service(request,response) - 服务方法
void destory() - 销毁方法
javax.servlet.GenericServlet抽象类:
void service(request,response) - 仍然是抽象的
javax.servlet.http.HttpServlet 抽象子类:
void service(request,response) - 不是抽象的
1. String method = req.getMethod(); 获取请求的方式
2. 各种if判断,根据请求方式不同,决定去调用不同的do方法
if (method.equals("GET")) {
this.doGet(req,resp);
} else if (method.equals("HEAD")) {
this.doHead(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);
} else if (method.equals("PUT")) {
3. 在HttpServlet这个抽象类中,do方法都差不多:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}
}
3.小结:
1) 继承关系: HttpServlet -> GenericServlet -> Servlet
2) Servlet中的核心方法: init() , service() , destroy()
3) 服务方法: 当有请求过来时,service方法会自动响应(其实是tomcat容器调用的)
在HttpServlet中我们会去分析请求的方式:到底是get、post、head还是delete等等
然后再决定调用的是哪个do开头的方法
那么在HttpServlet中这些do方法默认都是405的实现风格-要我们子类去实现对应的方法,否则默认会报405错误
4) 因此,我们在新建Servlet时,我们才会去考虑请求方法,从而决定重写哪个do方法
servlet的生命周期
获取,处理,响应请求
看起来web项目不像java se项目,有非常明显的入口程序。比如说之前那个fruit项目,我们就是在view.java这个程序启动整个系统
而web项目,我在.html可以启动,在AddServlet.java可以启动,这就说明底层控制着整个java web程序的执行顺序,这个底层就是tomcat容器
servlet的生命周期也就和Servlet中的核心方法: init() , service() , destroy()直接相关
1) 生命周期:从出生到死亡的过程就是生命周期。对应Servlet中的三个方法:init(),service(),destroy()
2) 默认情况下:
第一次接收请求时,这个Servlet会进行实例化(调用构造方法)、初始化(调用init())、然后服务(调用service())
从第二次请求开始,每一次都是服务
当容器关闭时,其中的所有的servlet实例会被销毁,调用销毁方法
3) 通过案例我们发现:
- Servlet实例tomcat只会创建一个,所有的请求都是这个实例去响应。
- 默认情况下,第一次请求时,tomcat才会去实例化,初始化,然后再服务.这样的好处是什么? 提高系统的启动速度 。 这样的缺点是什么? 第一次请求时,耗时较长。
- 因此得出结论: 如果需要提高系统的启动速度,当前默认情况就是这样。如果需要提高响应速度,我们应该设置Servlet的初始化时机。
servlet的初始化时机
Servlet的初始化时机:
- 默认是第一次接收请求时,实例化,初始化
- 我们可以通过<load-on-startup>来设置servlet启动的先后顺序,数 字越小,启动越靠前,最小值0
load-on-startup这个标签的值是一个无量纲的数,表示程度
tomcat容器与servlet
Servlet在容器中是:单例的、线程不安全的
- 单例:所有的请求都是同一个实例去响应
- 线程不安全:一个线程需要根据这个实例中的某个成员变量值去做逻辑判断。但是在中间某个时机,另一个线程改变了这个成员变量的值,从而导致第一个线程的执行路径发生了变化
- 我们已经知道了servlet是线程不安全的,给我们的启发是: 尽量的不要在servlet中定义成员变量。如果不得不定义成员变量,那么不要去:①不要去修改成员变量的值 ②不要去根据成员变量的值做一些逻辑判断
注意,并不是servlet总共只有一个实例,而是对于XxxServlet类只有一个实例,如果有AddServlet类,DeleteServlet类,那么他们俩分别只能有一个实例
http协议
实际产生的一个http请求
1) Http 称之为 超文本传输协议
2) Http是无状态的
3) Http请求响应包含两个部分:请求和响应
- 请求:
请求包含三个部分: 1.请求行 ; 2.请求消息头 ; 3.请求主体
1)请求行包含是三个信息: 1. 请求的方式 ; 2.请求的URL ; 3.请求的协议(一般都是HTTP1.1)
2)请求消息头中包含了很多客户端需要告诉服务器的信息,比如:我的浏览器型号、版本、我能接收的内容的类型、我给你发的内容的类型、内容的长度等等
3)请求体,三种情况
get方式,没有请求体,但是有一个queryString,比如?nation=USA&name=jim
post方式,有请求体,form data
json格式,有请求体,request payload
- 响应:
响应也包含三本: 1. 响应行 ; 2.响应头 ; 3.响应体
1)响应行包含三个信息:1.协议 2.响应状态码(200) 3.响应状态(ok)
2)响应头:包含了服务器的信息;服务器发送给浏览器的信息(内容的媒体类型、编码、内容长度等)
3)响应体:响应的实际内容(比如请求add.html页面时,响应的内容就是<html><head><body><form....)
会话-session-http是无状态的
物联网淑慧试用:传输层,会话层,表示层,应用层
万物皆对象,会话也是一个类,HttpSession类
http是无状态的
- HTTP 无状态:服务器无法判断这两次请求是同一个客户端发过来的,还是不同的客户端发过来的
- 无状态带来的现实问题:第一次请求是添加商品到购物车,第二次请求是结账;如果这两次请求服务器无法区分是同一个用户的,那么就会导致混乱
- 通过会话跟踪技术来解决无状态的问题。
会话跟踪
客户端请求,服务器响应,这就像谈话一样,这种场景被定义为一个会话
会话跟踪技术
- 客户端第一次发请求给服务器,服务器获取session,获取不到,则创建新的,然后响应给客户端
- 下次客户端给服务器发请求时,会把sessionID带给服务器,那么服务器就能获取到了,那么服务器就判断这一次请求和上次某次请求是同一个客户端,从而能够区分开客户端
- 常用的API:
request.getSession() -> 获取当前的会话,没有则创建一个新的会话
request.getSession(true) -> 效果和不带参数相同
request.getSession(false) -> 获取当前会话,没有则返回null,不会创建新的
session.getId() -> 获取sessionID
session.isNew() -> 判断当前session是否是新的
session.getMaxInactiveInterval() -> session的非激活间隔时长,默认1800秒
session.setMaxInactiveInterval()
session.invalidate() -> 强制性让会话立即失效
....
session保存作用域
前面说了,会话就是HttpSession类,那么类里边有属性很正常吧
每个客户端实例化了一个HttpSession类,同时呢,也实例化了一个xxxServlet类
- session保存作用域是和具体的某一个session对应的
- 常用的API:
void session.setAttribute(k,v)
Object session.getAttribute(k)
void removeAttribute(k)
服务器端转发和客户端重定向
两种资源跳转的方式
服务器端转发
上面说了,服务器端只会实例化一个servlet,由这个实例来为所有的请求提供服务。如果是这样,从哪里来的服务器端转发呢????
这里说的是两个组件,不是两个servlet;但是处理请求的不都是servlet吗??
服务器内部转发 : request.getRequestDispatcher("...").forward(request,response);
- 一次请求响应的过程,对于客户端而言,内部经过了多少次转发,客户端是不知道的
- 地址栏没有变化
客户端重定向
服务器的组件让客户端立即向指定的组件发起请求
客户端重定向: response.sendRedirect("....");
- 两次请求响应的过程。客户端肯定知道请求URL有变化
- 地址栏有变化
thymeleaf
thyme:百里香,有吉祥如意的意思
一种视图模板技术,可以实现在html中显示数据库中的真实信息,和springboot完美整合
在静态页面.html文件上加载java内存中的数据这一过程被称之为渲染
thymeleaf就是用来帮助进行渲染的一个技术
渲染的英文是render,在底层源码中设计了一个render方法
为什么使用thymeleaf
背景:假设要将数据库中的数据展示在html页面
我们肯定是不希望将取出的记录一条一条的写到静态页面上,而是说,我取出一百条记录,通过一些操作,可以让他直接显示在页面上
配置thymeleaf的步骤
添加thymeleaf的jar包:有一大堆jar包
注意,新建的依赖文件夹,也就是lib,需要右键->add as library
新建一个Servlet类ViewBaseServlet:这个类继承了HttpServlet类
public class ViewBaseServlet extends HttpServlet {
private TemplateEngine templateEngine;
@Override
public void init() throws ServletException {
// 1.获取ServletContext对象
ServletContext servletContext = this.getServletContext();
// 2.创建Thymeleaf解析器对象
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext);
// 3.给解析器对象设置参数
// ①HTML是默认模式,明确设置是为了代码更容易理解
templateResolver.setTemplateMode(TemplateMode.HTML);
// ②设置前缀
String viewPrefix = servletContext.getInitParameter("view-prefix");
templateResolver.setPrefix(viewPrefix);
// ③设置后缀
String viewSuffix = servletContext.getInitParameter("view-suffix");
templateResolver.setSuffix(viewSuffix);
// ④设置缓存过期时间(毫秒)
templateResolver.setCacheTTLMs(60000L);
// ⑤设置是否缓存
templateResolver.setCacheable(true);
// ⑥设置服务器端编码方式
templateResolver.setCharacterEncoding("utf-8");
// 4.创建模板引擎对象
templateEngine = new TemplateEngine();
// 5.给模板引擎对象设置模板解析器
templateEngine.setTemplateResolver(templateResolver);
}
protected void processTemplate(String templateName, HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 1.设置响应体内容类型和字符集
resp.setContentType("text/html;charset=UTF-8");
// 2.创建WebContext对象
WebContext webContext = new WebContext(req, resp, getServletContext());
// 3.处理模板数据
templateEngine.process(templateName, webContext, resp.getWriter());
}
}
在web.xml文件中添加配置,ViewBaseServlet类中的init方法会使用这两个参数
- 配置前缀 view-prefix
- 配置后缀 view-suffix
<!-- 配置上下文参数 -->
<context-param>
<param-name>view-prefix</param-name>
//前缀就是web这个根目录
<param-value>/</param-value>
</context-param>
<context-param>
<param-name>view-suffix</param-name>
//后缀就是.html,相当于说去找这个.html文件
<param-value>.html</param-value>
</context-param>
使得我们的Servlet继承ViewBaseServlet,在这个例子中,我们设计了一个IndexServlet类
//servlet从3.0版本开始支持使用注解完成在xml文件中的注册
@WebServlet("/index")
public class IndexServlet extends ViewBaseServlet {
@Override
public void doGet(HttpServletRequest request , HttpServletResponse response)throws IOException, ServletException {
FruitDAO fruitDAO = new FruitDAOImpl();
List<Fruit> fruitList = fruitDAO.getFruirList();
//保存到session作用域
HttpSession session = request.getSession() ;
session.setAttribute("fruitList",fruitList);
//此处的视图名称是 index
//那么thymeleaf会将这个 逻辑视图名称 对应到 物理视图 名称上去
//逻辑视图名称 : index
//物理视图名称 : view-prefix + 逻辑视图名称 + view-suffix
//所以真实的视图名称是: / index .html
super.processTemplate("index",request,response);
}
}
根据逻辑视图名称 得到 物理视图名称:说白了就是将一个不带后缀的文件名变成一个相对路径,这个路径必须定位到一个文件
//此处的视图名称是 index
//那么thymeleaf会将这个 逻辑视图名称 对应到 物理视图 名称上去
//逻辑视图名称 : index
//物理视图名称 : view-prefix + 逻辑视图名称 + view-suffix
//所以真实的视图名称是: / index .html
super.processTemplate(“index”,request,response);
使用thymeleaf的标签
th:if , th:unless , th:each , th:text
//为了使用thymeleaf,需要添加这么一个表头
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="css/index.css">
</head>
<body>
<div id="div_container">
<div id="div_fruit_list">
<p class="center f30">欢迎使用水果库存后台管理系统</p>
<table id="tbl_fruit">
<tr>
<th class="w20">名称</th>
<th class="w20">单价</th>
<th class="w20">库存</th>
<th>操作</th>
</tr>
<tr th:if="${#lists.isEmpty(session.fruitList)}">
<td colspan="4">对不起,库存为空!</td>
</tr>
<tr th:unless="${#lists.isEmpty(session.fruitList)}" th:each="fruit : ${session.fruitList}">
<td th:text="${fruit.fname}">苹果</td>
<td th:text="${fruit.price}">5</td>
<td th:text="${fruit.fcount}">20</td>
<td><img src="imgs/del.jpg" class="delImg"/></td>
</tr>
</table>
</div>
</div>
</body>
</html>
如何实现大量统一格式的数据在html页面的展示
如果是一般的高级程序语言,我想肯定能脱口而出,就是用循环嘛
在这就懵逼了。html不同于之前遇到的高级程序语言,我们没有看到经典的流程控制相关的语法
但在前面说明使用thymeleaf的背景时,我们提到已经提前获取到sql查询的结果,需要通过一些操作将查询结果一条一条的展示出来
从上面的html代码可以看到,他妈的,居然用的是循环,意料之外又在情理之中
th:each="fruit : ${session.fruitList}"
session.fruitList,我们将装查询结果的容器储存到会话中
fruit,每次从容器中取出一条记录储存到变量fruit
回顾
review:
1. post提交方式下的设置编码,防止中文乱码
request.setCharacterEncoding("utf-8");
get提交方式,tomcat8开始,编码不需要设置
tomcat8之前,get方式设置比较麻烦:
String fname = request.getParameter("fname");
byte[] bytes = fname.getBytes("iso-8859-1");
fname = new String(bytes,"UTF-8");
2. Servlet继承关系以及生命周期
1) Servlet接口 : init() , service() , destroy()
GenericServlet抽象子类: abstract service();
HttpServlet抽象子类:实现了service方法,在service方法内部通过request.getMethod()来判断请求的方式,
然后根据请求的方式去调用内部的do方法。每一个do方法进行了简单实现,主要是如果请求方式不符合,则报405错误。
目的是让我们的Servlet子类去重写对应的方法(如果重写的不对,则使用父类的405错误实现)
2) 生命周期:实例化、初始化、服务、销毁
- Tomcat负责维护Servlet实例的生命周期
- 每个Servlet在Tomcat容器中只有一个实例,它是线程不安全的
- Servlet的启动时机:<load-on-startup>
- Servlet3.0开始支持注解: @WebServlet
3. HTTP协议:
1) 由 Request 和 Response 两部分组成
2) 请求包含了三部分:请求行、请求消息头、请求主体: 普通的get方式请求-query string;post方式- form data ; json格式 - request payload
3) 响应包含了三部分:响应行、响应消息头、响应主体
4. HttpSession
1) HttpSession :表示 会话
2) 为什么需要HttpSession , 原因是因为 Http协议是无状态的
3) Session保存作用域 :一次会话范围都有效 ; void session.setAttribute(k,v) ,Object session.getAttribute(k)
4) 其他的API: session.getId() , session.isNew() , session.getCreationTime() , session.invalidate() 等等
5. 服务器端转发和客户端重定向
1) 服务器端转发 : request.getRequestDispatcher("index.html").forward(request,response);
2) 客户端重定向: response.sendRedirect("index.html");
6. thymeleaf的部分标签
1) 使用步骤: 添加jar , 新建ViewBaseServlet(有两个方法) , 配置两个<context-param> : view-prefix , view-suffix
2) 部分标签: <th:if> , <th:unless> , <th:each> , <th:text>
servlet保存作用域
从我最开始学习会话的时候,我就没明白这个保存作用域是什么意思。从代码层面来看,有一个HttpSession类,这个类的实例可以存储其他类型的实例,感觉上HttpSession类有点像是一个容器
但你要说这是个作用域,听起来就很奇怪,很反直觉
作用域解释
于是,我搜索到了这样一样说法,
几乎所有web应用容器都提供了四种类似Map的结构:application session request page,通过向着这四个对象放入数据,从而实现数据的共享
这些说法真的是越来越抽象,不说人话啊!!!
web容器:就是一个程序。特指服务器程序,比如说tomcat也可以叫tomcat容器
因此,上面那个说法翻译一下就是,tomcat服务器提供了四种类似于哈希表的类:
application:整个应用 对应servlet中ServletContext类
session:会话 对应servlet中HttpSession类
request:一次请求 对应servlet中的HttpServletRequest类
page:当前页面 对应servlet中的PageContext类
pageContext:当前页面有效 (页面跳转后无效)
request:同一次请求有效(请求转发后有效;重定向后无效)
session:同一次会话有效(关闭/切换浏览器后无效 ; 默认30分钟有效期)
appliation:全局有效 (切换浏览器 仍然有效) 服务器开着就有效,切换客户端
结论:所以说,作用域指的是,web容器提供的四个类Map容器类实例的作用范围
HttpServletRequest容器
HttpServletRequest实例的作用范围在一次请求之内,因此,上面的示例中,第二次请求是无法取到数据的
模拟不同请求:可以使用客户端重定向
HttpSession容器
模拟不同会话:
ServletContext容器
模拟不同的客户端:换一台IP地址不同的设备或者直接换一个浏览器
绝对路径和相对路径
href:hyper reference超文本引用
区分超链接和超文本引用
超链接:将网页的选中部分作为跳转到另一个网页的入口,通过点击实现跳转
超文本引用:用超链接的方法,将各种不同空间的文字信息组织在一起的网状文本
从实际使用来看,超链接需要用户主动点击才能跳转,超文本引用写好以后,在运行时会自动引用链接的内容,用户不能主动点击