文章目录

  • 四. 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,如果是前者,报错

java XStreamAlias构建报文 java组装xml报文_tomcat


启动start.bat之后,可以看到一个进程msedge.exe占用了8080端口

使用netstat -ano|findstr 8080命令可以查看

新建web项目,在tomcat部署

在webapps文件夹下,新建文件夹baidu,作为web项目目录

在baidu目录下新建文件夹WEB-INF,目录名全部大写

一个web项目基本结构就是这样

运行和访问

在baidu目录下导入项目文件

java XStreamAlias构建报文 java组装xml报文_tomcat_02


然后重启start.bat,也就是tomcat服务,再在浏览器输入http://localhost:8080/baidu/demo09.html就可以看到

java XStreamAlias构建报文 java组装xml报文_服务器_03


这是很激动人心的时刻,就是说我们从客户端访问到了服务器的资源

曾经的我们打开.html文件是在本地,打开页面之后,其地址是

java XStreamAlias构建报文 java组装xml报文_web项目_04


现在资源的地址变成了http://localhost:8080/baidu/demo09.html

localhost:8080通过ip地址和端口号定位到某台计算机上的tomcat服务器
baidu/demo09.html通过路径定位到服务器上的项目

舒服了,这才是互联网

在idea新建java web项目

(1)在idea中配置tomcat服务器。就是首先得让idea找到tomcat

java XStreamAlias构建报文 java组装xml报文_服务器_05

(2)添加框架支持。自动将我们的java module变成web application。帮我们创建了web项目的文件目录

java XStreamAlias构建报文 java组装xml报文_服务器_06


相比于前面我们直接在tomcat安装路径下创建的目录还是有区别

tomcat/webapps/baidu目录下默认会有一个WEB-INF文件夹,我们的项目和WEB-INF在同一级目录

java XStreamAlias构建报文 java组装xml报文_服务器_07


(3)编辑web工程的结构(edit configuration)

也就是加上本地或者远程的tomcat服务器

java XStreamAlias构建报文 java组装xml报文_tomcat_08


左起第二个框,是select run/debug configuration,这个地方要选对,如果是java se程序,那么就是程序名;如果是java web项目,就是你配置的服务器名点击下拉菜单,可以编辑当前选中的configuration

java XStreamAlias构建报文 java组装xml报文_java_09


然后就可以进行部署等相关设置

java XStreamAlias构建报文 java组装xml报文_tomcat_10

(4)部署web项目
也是在edit configuration中完成部署

web项目的配置

有几个比较重要的按键:

(1)右键module,add framework support, 添加框架支持,比如说让一个java se module变成一个java web module

(2)file->project structure,对整个项目的设置进行调整

java XStreamAlias构建报文 java组装xml报文_服务器_11


挺恶心的,全是英文facets:本意是方面,部分。可以用于添加框架支持。比如说将一个java se module变成一个java web module

java XStreamAlias构建报文 java组装xml报文_java_12


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项目部署包。

java XStreamAlias构建报文 java组装xml报文_服务器_13

lib文件夹和artifact的关系

lib是我们自定义的依赖,artifact是部署包,artifact中包含了引用lib的信息

先有artifact,如果之后有了新的依赖,必须手动更新artifact。要么删掉重建,要么fix

java XStreamAlias构建报文 java组装xml报文_tomcat_14

为了写DAO,或者引入数据库连接池,我们都写过依赖。不管是idea还是eclipse,都由我们自己创建了一个叫做lib的文件夹,然后将相应的驱动直接复制到这个lib文件夹下

接着就有区别了:
对于eclipse,右键驱动,选择build path
对于idea,右键lib文件夹,选择add as library,直接将这个lib文件夹变成依赖

对于idea,module之前是不共享依赖的。因此需要对每一个module添加这个依赖

还是project stucture,选择module,选中module,找右边的dependencies来添加依赖

java XStreamAlias构建报文 java组装xml报文_tomcat_15

部署

准备好web项目,依赖,部署包之后,就准备开始部署到tomcat服务器了

这个时候就可以edit configuration了

先添加tomcat服务器

java XStreamAlias构建报文 java组装xml报文_服务器_16


再将artifact部署包部署,也就是添加artifact

java XStreamAlias构建报文 java组装xml报文_服务器_17


下面有个application context,他的值就是一个路径,这个路径会被添加到server选项卡的URL的值得后面URL的值默认是http://localhost:8080,默认访问的顺序是

java XStreamAlias构建报文 java组装xml报文_tomcat_18


这是在tomcat服务器中的默认设置,在config/web.xml中设置好的

假设 application context=/pro,那么URL=http://localhost:8080/pro

进一步说明

什么叫web项目

第一印象里,web项目是说项目里边必须包含web相关的固定格式的目录。在project structure里,我们可以看到web项目的module里边包含web

java XStreamAlias构建报文 java组装xml报文_web项目_19


具体来说,有了web意味着什么呢??有什么变化???

java XStreamAlias构建报文 java组装xml报文_java_20


位置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 XStreamAlias构建报文 java组装xml报文_web项目_21

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语言规范)

项目架构

java XStreamAlias构建报文 java组装xml报文_tomcat_22


BaseDAO类:通用的增删改查操作

xxxDAO接口:针对某一张数据表的原子操作,使用继承与BaseDAO的方法实现

xxxDAOImpl类:重写xxxDAO接口中的所有方法

然后在具体的业务模块,我们会设计大量的业务方法,这些业务方法由xxxDAO接口定义的原子操作来实现

总结:业务方法由xxxDAOImpl类实现类中的原子操作实现,xxxDAOImpl实现类中的原子操作由BaseDAO中的原子操作实现

项目需求

有这样一个场景:在下单的时候,订单表中显示谁在什么时间买了什么东西

java XStreamAlias构建报文 java组装xml报文_服务器_23


而具体买了什么在另外一张表 订单详情表 呈现:

java XStreamAlias构建报文 java组装xml报文_服务器_24


这样一来就产生了一个新的需求:需要获取订单表的id来访问订单详情表的某一条记录。而BaseDAO中通用的增删改这一原子操作是不返回id的

需求:在增加数据完成后返回自增列的值
之前我都忽略了一点,conn.preparedStatemetn是有返回值的,返回int

一个新知识,conn.preparedStatemetn的重载方法返回一个ResultSet实例,但是方法的参数要增加

java XStreamAlias构建报文 java组装xml报文_tomcat_25


对于删和改没有上面这一需求,只针对增这一操作。因此,我们需要解析字符串sql,看是不是以insert开头。调用下面的方法进行解析sql.trim().toUpperCase().startsWith("INSERT") trim方法,去掉字符串首尾空格;toUpperCase方法,将字符串全转为大写;startsWith方法,从首字母开始匹配,看是否能匹配到目标字符串

五. servelet

在后端编程中,我们需要一些逻辑结构,比如servlet(server applet,服务器连接器,applet的本意是小程序),JSP

java XStreamAlias构建报文 java组装xml报文_服务器_26


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,向数据库服务器中添加数据

java XStreamAlias构建报文 java组装xml报文_服务器_27

xxxServlet类

一个普通的xxxServlet类是无法完成以下功能的:
获取来自客户端的数据
调用DAO完成对数据库的操作
在控制台打印相应的操作成功

服务器厂商,比如说tomcat,会提供一个父类HttpServlet,自定义的xxxServlet类去继承这个父类。这个父类的声明为:public abstract class HttpServlet extends javax.servlet.GenericServlet ,是一个抽象父类

实际上服务器厂商会提供JSP和servlet等许多结构的相关API,都在安装路径的lib文件夹中,我们在使用的时候需要手动导入需要的依赖(dependency)

java XStreamAlias构建报文 java组装xml报文_web项目_28

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方法

java XStreamAlias构建报文 java组装xml报文_tomcat_29


这个请求的具体信息如下:

java XStreamAlias构建报文 java组装xml报文_java_30


(7)此时,AddServlet类就该获取请求(获取数据)。我们在doPost方法打印了获取到的数据

java XStreamAlias构建报文 java组装xml报文_java_31


(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类捕获到的值就会是乱码

java XStreamAlias构建报文 java组装xml报文_服务器_32


java XStreamAlias构建报文 java组装xml报文_java_33


这是因为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

java XStreamAlias构建报文 java组装xml报文_tomcat_34

Servlet在容器中是:单例的、线程不安全的
        - 单例:所有的请求都是同一个实例去响应
        - 线程不安全:一个线程需要根据这个实例中的某个成员变量值去做逻辑判断。但是在中间某个时机,另一个线程改变了这个成员变量的值,从而导致第一个线程的执行路径发生了变化
        - 我们已经知道了servlet是线程不安全的,给我们的启发是: 尽量的不要在servlet中定义成员变量。如果不得不定义成员变量,那么不要去:①不要去修改成员变量的值 ②不要去根据成员变量的值做一些逻辑判断

注意,并不是servlet总共只有一个实例,而是对于XxxServlet类只有一个实例,如果有AddServlet类,DeleteServlet类,那么他们俩分别只能有一个实例

http协议

java XStreamAlias构建报文 java组装xml报文_tomcat_35


实际产生的一个http请求

java XStreamAlias构建报文 java组装xml报文_java_30

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,由这个实例来为所有的请求提供服务。如果是这样,从哪里来的服务器端转发呢????

java XStreamAlias构建报文 java组装xml报文_web项目_37


这里说的是两个组件,不是两个servlet;但是处理请求的不都是servlet吗??

java XStreamAlias构建报文 java组装xml报文_tomcat_38

服务器内部转发 : request.getRequestDispatcher("...").forward(request,response);
      - 一次请求响应的过程,对于客户端而言,内部经过了多少次转发,客户端是不知道的
      - 地址栏没有变化

客户端重定向

服务器的组件让客户端立即向指定的组件发起请求

java XStreamAlias构建报文 java组装xml报文_tomcat_39


java XStreamAlias构建报文 java组装xml报文_tomcat_40

客户端重定向: response.sendRedirect("....");
      - 两次请求响应的过程。客户端肯定知道请求URL有变化
      - 地址栏有变化

thymeleaf

thyme:百里香,有吉祥如意的意思

一种视图模板技术,可以实现在html中显示数据库中的真实信息,和springboot完美整合

java XStreamAlias构建报文 java组装xml报文_tomcat_41


在静态页面.html文件上加载java内存中的数据这一过程被称之为渲染

thymeleaf就是用来帮助进行渲染的一个技术

渲染的英文是render,在底层源码中设计了一个render方法

为什么使用thymeleaf

背景:假设要将数据库中的数据展示在html页面

我们肯定是不希望将取出的记录一条一条的写到静态页面上,而是说,我取出一百条记录,通过一些操作,可以让他直接显示在页面上

配置thymeleaf的步骤

添加thymeleaf的jar包:有一大堆jar包

java XStreamAlias构建报文 java组装xml报文_服务器_42


注意,新建的依赖文件夹,也就是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

java XStreamAlias构建报文 java组装xml报文_java_43


pageContext:当前页面有效 (页面跳转后无效)

request:同一次请求有效(请求转发后有效;重定向后无效)

session:同一次会话有效(关闭/切换浏览器后无效 ; 默认30分钟有效期)

appliation:全局有效 (切换浏览器 仍然有效) 服务器开着就有效,切换客户端

结论:所以说,作用域指的是,web容器提供的四个类Map容器类实例的作用范围

HttpServletRequest容器

java XStreamAlias构建报文 java组装xml报文_tomcat_44


HttpServletRequest实例的作用范围在一次请求之内,因此,上面的示例中,第二次请求是无法取到数据的

模拟不同请求:可以使用客户端重定向

HttpSession容器

模拟不同会话:

ServletContext容器

java XStreamAlias构建报文 java组装xml报文_服务器_45


模拟不同的客户端:换一台IP地址不同的设备或者直接换一个浏览器

绝对路径和相对路径

java XStreamAlias构建报文 java组装xml报文_服务器_46


href:hyper reference超文本引用

区分超链接和超文本引用

超链接:将网页的选中部分作为跳转到另一个网页的入口,通过点击实现跳转
超文本引用:用超链接的方法,将各种不同空间的文字信息组织在一起的网状文本

从实际使用来看,超链接需要用户主动点击才能跳转,超文本引用写好以后,在运行时会自动引用链接的内容,用户不能主动点击