Java:50-Servlet核心技术

Servlet核心技术

C/S架构的概念------------------------------
C/S架构(Client/Server,客户端/服务器模式),是一种比较早的软件体系结构,也是生活中很常见的结构
这种结构将需要处理的业务合理地分配到客户端和服务器端,客户端通常负责完成与用户的交互任务,服务器通常负责数据的管理
C/S架构的主要优点如下:
客户端的界面和功能可以很丰富,应用服务器的负荷较轻
响应速度较快
C/S架构的主要缺点如下:适用面窄,用户群固定
维护和升级的成本高,所有的客户端都需要更新版本
B/S架构的概念----------------------------------
B/S架构(Browser/Server,浏览器/服务器模式),是互联网兴起后的软件体系结构
该结构将系统功能实现的主要业务逻辑集中到服务器端,极少数业务逻辑在浏览器实现
浏览器通常负责完成与用户的交互任务,服务器通常负责数据的管理
B/S架构的主要优点如下:
无需安装客户端,只要有浏览器即可
适用面广,用户群不固定。通过权限控制实现多客户访问的目的,交互性较强
维护和升级的成本低,无需更新所有客户端版本
B/S架构的主要缺点如下:
应用服务器的负荷较重
浏览器的界面和功能想要达到客户端的丰富程度需要花费大量的成本
在跨浏览器上不尽如人意,适配比较麻烦
JavaWeb的概念----------------------------------
Web本意为网页的含义,这里表示互联网上供外界访问的资源
互联网上供外界访问的资源主要分为以下两种:
静态资源:主要指Web页面中供人们浏览的数据始终是不变
动态资源:主要指Web页面中供人们浏览的数据由程序产生,不同时间点访问页面看到的内容各不相同
JavaWeb主要指使用Java语言进行动态Web资源开发技术的统称,是解决相关Web互联网领域的技术总和
早期的B/S架构:

在这里插入图片描述

后来的B/S架构:

在这里插入图片描述

可以理解为:客户端和服务端的cs架构,就是下载的客户端里有先存好的页面,然后服务端只要对数据操作即可
而浏览器和服务端的bs架构,就是服务端一直给你页面数据,基本上你刷新一次我就要给你一次
所以我不止需要操作数据,还要操作页面
HTTP协议的概念-----------------------------
HTTP协议(HyperText Transfer Protocol,超文本传输协议)是由W3C(万维网联盟)组织制定的一种应用层协议
是用来规范浏览器与Web服务器之间如何通讯的数据格式,主要涉及浏览器的发请求格式和服务器的响应格式
HTTP协议通常承载于TCP协议之上,而承载于TLS或SSL协议层之上的协议就是常说的HTTPS协议
HTTP默认的端口号为80,HTTPS默认的端口号为443

在这里插入图片描述

HTTP请求格式:
客户端发送一个HTTP请求到服务器的请求消息主要包括:请求行、请求头、空白行和请求体
/*请求行用来说明请求类型和要访问的资源以及所使用的HTTP版本,格式如下:
	请求类型 请求的路径 协议的版本(1.1)
请求头是紧接着请求行(即第一行)之后的部分,用来说明服务器要使用的附加信息,格式(key:value)如下:
	主机 请求长度 请求的浏览器相关信息
空白行就是请求头部的空行,即使后面的请求数据为空则必须有空行
请求体也叫请求数据,可以添加任意的其他数据
举例如下:
POST /task01_demo01/demo1.html HTTP/1.1 	请求行,包括请求类型,请求路径,协议版本
Host: localhost:8088               请求头  主机:主机地址,请求的服务器的地址
Content-Length: 21                  最下面的请求体的长度
Cache-Control: max-age=0			浏览器相关信息,在服务器上修改代码可以进行实施更新
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64)    浏览器相关信息
       //空白行
name=scott&pwd=123456                请求体,即请求数据,不是请求过去的内容,这是添加的数据
*/
HTTP响应格式:
通常情况下服务器接收并处理客户端发过来的请求后会返回一个HTTP的响应消息,主要包括:响应行、响应头、空白行和响应体
/*
响应行用来说明HTTP协议版本号和状态码以及状态消息,格式如下:
	协议的版本(1.0 1.1) 状态码 (200 成功 404 路径错误 500 服务错误) 状态信息
响应头用来说明客户端要使用的一些附加信息,格式(key:value)
空白行就是响应头部的空行,即使后面的请求数据为空则必须有空行
响应体用来服务器返回给客户端的文本信息
举例如下:
HTTP/1.1 200 OK    				响应行   协议版本 状态码 很成功
Content-Type: text/html 			响应体 这里是我要给你的的内容类型,文本的html类型
Content-Length: 588                  我要给你的内容长度
Date: Thu, 08 Sep 2021 12:59:54 GMT   	日期
  //空白行
<html><head><title>示例1</title></head>   	响应体 服务器给你的信息(内容)
<body><h1>这是一个HTML页面</h1></body>		这里省略了很多,只给你html标签里的东西
</html>
*/
若要看具体情况,在浏览器中打开F12(检查,或者开发者工具),找到Network(网络)
注意:不同的浏览器的功能(网络)不同
然后刷新页面或者去其他页面使得地址请求,就知道了具体情况了

在这里插入图片描述

其中在左边的名字那里点击一个东西,可以看出他的相关信息,消息头最上面是请求行和响应行放一起的信息
实际上每个东西都会请求过去,因为这些东西都是服务器响应给你的(如图片等等)
而每个东西都有对应的请求和响应,右边的就是他们的相关信息
Tomcat服务器(重点):
Tomcat本意为公猫的含义,最初是由Sun公司的软件架构师詹姆斯·邓肯·戴维森开发的
后来他帮助将其变为开源项目并由Sun公司贡献给Apache软件基金会
Tomcat 服务器是一个开源的轻量级Web应用服务器,在中小型系统和并发量小的场合下被普遍使用
是开发和调试Servlet、JSP 程序的首选
安装方式:
下载地址:http://tomcat.apache.org/

在这里插入图片描述

在这里插入图片描述

目录结构:
bin 主要存放二进制可执行文件和脚本
conf 主要存放各种配置文件
lib 主要用来存放Tomcat运行需要加载的jar包
logs 主要存放Tomcat在运行过程中产生的日志文件
temp 主要存放Tomcat在运行过程中产生的临时文件
webapps 主要存放应用程序,当Tomcat启动时会去加载该目录下的应用程序
work 主要存放tomcat在运行时的编译后文件,例如JSP编译后的文件
启动和关闭:
启动方式:使用bin目录下的批处理文件startup.bat来启动Tomcat服务器,若出现一个毫秒数说明启动成功
若再次启动,那么再次启动的会自动关闭,不起作用,关闭窗口可以关闭启动的tomcat,也可以使用下面的关闭方式
关闭方式:使用bin目录下的批处理文件shutdown.bat来关闭Tomcat服务器
在这里说明一下,mysql和tomcat的服务启动时,都会有一个默认的端口,mysql我们可以去连接,因为有mysql管道来传输信息
若用浏览器去连接
则需要开启mysql的http协议管道,因为要进行数据交互,因为要进行连接,就需要一个方式,即数据的交互
平常我们浏览器不能访问到mysql
因为mysql还没有设置网页上的http之间的管道交互,就如网络编程类似,需要一个IO流去操作
当设置后,可以通过默认端口进行请求,就可以看到网页版了,有默认地址
而tomcat我们也可以去连接,自带http的管道交互,且默认端口有设置默认地址(设置的,即默认请求该地址)
所以可以请求到,即可以看到tomcat的网页版(若看到了,相当于tomcat基本启动成功),在一个网络下
mysql默认端口3306
tomcat默认端口8080
http默认端口80,当然,进行连接时端口是可以改变的,只是默认端口代表默认开放的端口对于http来说,只是偏向于输出的意思,当然,对方要返回,自然是通过80的
注意:他们只是访问时的请求而已,而不是他们本身占用,这里要明白哦,所以tomca是可以操作80的,因为只有程序占用端口,而不是访问,要不然你浏览器多次的访问80不就不行了吗,而正是因为可行,所以他并没有占用
实际上http只是一个具体说明方式(协议),就如TCP协议和UDP协议类似,用程序进行数据操作
mysql里面包含mysql通讯协议来进行传输,所以代码需要驱动包来进行这样的连接(使得可以通过这个方式来进行数据传输)
协议就是一种方式,如代码方式,可以理解为一个管道的操作,不同管道有不同规定
就如规定xml书写的Schema和DTD类似
不是具体存在的,但端口也是,即要让两者进行传输,则需要都有这样的方式才可进行
如mysql没设置http,那么你发送http得不到响应
注意事项:
启动之前首先安装JDK并配置环境变量JAVA_HOME(通常tomcat需要这个,因为是java写的,所以需要环境)
若希望Tomcat服务器可以在任意路径启动,则需要配置环境变量CATALINA_HOME
注意:这个名称不能改变,虽然Path设置的是全局的命令参数
但使用全局的启动时(对应目录要正确,否则全局找不到对应命令),是需要这个名称的,也就是说
虽然你设置了全局命令参数,但是使用全局命令参数时,若不是这个名称,那么就不会执行
所以启动时会先判断是否是全局启动,若是,如果不是这个名称,那么不执行
若不是全局启动,若有这个名称,那么就操作这个名称所对应的tomcat的启动,若不是这个名称,那么就是对应的tomcat的启动
若是这个名称,但是对应的目录的最终指向,不是对应的存在的tomcat的目录(里面就是tomcat的文件了),那么就会直接退出启动,注意,直接的点击相当于利用命令,但要知道,他们只是针对我们手动的执行,因为我们手动的执行,是按照对应配置的
若是idea的执行,则不会操作这个名称,即idea操作哪个tomcat,那么就是哪个tomcat(二进制是可以实现的,实际上可能是特别的启动,自己看看他的指向就知道了,具体可以百度)
这里就知道为什么java的环境变量需要JAVA_HOME了,虽然你可以改变这个名字
但其他的服务或者软件有些需要这个名字的,若你改变,则可能会出现一些错误
当然,环境变量名的大小写是忽略的(最好不要),因为这样也会导致其他服务或者软件的需要
所以最好用JAVA_HOME这个变量名,防止其他服务或者软件出错
所以在网上找资料时,通常都会教你配置JAVA_HOME这个变量名,而不是其他变量名
启动信息乱码的处理方式:logging.properties文件修改为java.util.logging.ConsoleHandler.encoding = GBK
前面我们说过文件是可以使用\ \,因为目录用的就是\
所以一般操作不是本地的基本用/,而操作本地的基本用\,但/无论是在不是本地的还是本地的都可以用
配置文件配好后,就可以使用startup或者startup.bat开启tomcat了
也可以使用shutdown和shutdown.bat关闭了,其中shutdown可能会与其他的程序冲突,使得他不是关闭,而是一个命令提示
配置文件:
在当你修改端口时,若出错,大概是端口被占用的问题(如改成80端口)
使用netstat -ano列出所有端口情况,若修改80端口,则找到最后为80的端口,查看PID是多少,如 0.0.0.0:80样子的
停止PID对应的服务就可以了(使用taskkill /F /PID 对应PID,来sha si进程),这里用拼音,是怕被河蟹(和谐),如taskkill /F /PID 80
若关闭(sha si)不了(有可能是系统占用)
则去注册表里的(计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\HTTP)
修改Start变为4,重启电脑可以,若实在不行,你就在服务里找到对应PID,进行手动关闭吧
对于注册表,它里面是用来控制软硬件的一些重要数据,可以看成一个数据库,如上面的改变,是对于http协议的改变
如0,1,2,3,4等等,你可以理解为我改变一些东西,使得80端口不被占用(就算是系统占用也会不占用)
最后:最好不要更改,因为80端口可能会有其他程序会要占用
使得有些程序或者tomcat不可启动或者使用(互相占用,一个停止,另外一个占用,然后再停,再占,非常麻烦)
所有还是改回来8080吧(不想改也可以)
server.xml文件是服务器的主配置文件,可以设置端口号、设置域名或IP、默认加载的项目、请求编码等
/*
<Connector port="8888" protocol="HTTP/1.1"    
connectionTimeout="20000"     连接的超时,这里最大20000毫秒,即20秒,一个请求最多的等待时间,超过则报错
redirectPort="8443" />       重定向端口信息,一般出现重定向时,可能会利用该端口的进程处理某些信息
*/
tomcat-users.xml文件用来配置管理Tomcat服务器的用户与权限
/*
<role rolename="manager-gui"/> //权限,具体说明可以百度查询,也可以在tomcat界面中点击第二个Manager App左边来查找说明
<user username="admin" password="123456" roles="manager-gui"/> 
*/
开启服务器后,到网页上进入tomcat默认网址localhost:8080

在这里插入图片描述

右边的:
Server Status:服务器的状态信息
Manager App:应用程序的管理界面
Host Manager:主机的管理
进入都需要用户名和密码
我们的根目录默认是webapps目录,即localhost:8080/就是这个目录下的地址,这是默认的地址
当请求时,会默认访问ROOT文件的index.jsp文件,若index.html和index.jsp他们单独存在时,没有指定的话,都会默认访问他们
若两者都存在,则访问index.html(优先访问)
而当你去指定请求时,不指定文件,如localhost:8080/hello/,就会默认请求hello当前目录下的index.html文件和index.jsp,同样的html优先
上述都是默认的,而指定了文件后,当前会去请求你指定的文件,因为不是默认了
注意:浏览器会默认给没有加" / “的地址加上” / ",如localhost:8080/hello就是localhost:8080/hello/
创建javaweb工程

在这里插入图片描述

其中Application Server是指定服务器,即这里指定tomcat服务器,右边可以新建一个服务器地址,指定Tomcat Server
然后指定服务器目录地址,点击ok

在这里插入图片描述

在中间找到,这个Web Application,web工程,勾选,因为我们要写web程序,然后直接下一步

在这里插入图片描述

src我们知道是写java代码的,而web则是写一些网页资源的,但不识别java代码,即规定了一些资源的存放和运行
而src基本都可以执行,如网页资源等等,基本没有限制
而web下的WEB-INF主要放的是配置文件,当然也可以放网页资源
上面的主要放的资源最好这样放,虽然可以不这样放,但他们这样放容易观察和维护
安装了服务器的电脑也可以叫做服务器,如mysql服务器,tomcat服务器等等,或者两者结合
我们可以配置服务器在idea里的设置
点击Run下的Edit Configurations…,进入服务器的配置,也可在右上角直接点击服务器也可进入配置
这些配置并没有对tomcat的配置文件进行修改,而是复制一些配置文件和一些程序进行操作,修改的是复制的
在idea启动时使用的也是复制的(外面启动是使用tomcat的),即一个服务器配置就有以下内容

在这里插入图片描述

将复制的文件放在这里,所以idea在运行时,即启动服务器时,是使用这些配置文件

在这里插入图片描述

Server里:
右上面是名称,即配置的名称,当需要多个不同配置时,可以改变这个名字
再下面就是可以设置默认浏览器
然后下面就是能访问到这个项目里的路径,默认就是项目里的index.html或者index.jsp文件
实际上8080/后面的是一个简写,即对应当前项目的out的项目路径,在后面的Deployment里有介绍
然后On ‘Update’ action(注意:他的对应的选项出现,需要部署的是war exploded才行,可以自行测试,比如上面图片的选项) 后面就是当你修改资源文件和类文件时
更新服务器会将对应class文件和资源文件进行更新(保存时,就更新)
再下面就是失去焦点后,要干什么,也可以进行更新,且是实时更新,不用重新更新服务器
对于服务器来说,右上角的重新执行是更新的意思,对于类来说,是停止再执行
所以这个最好设置上面的将对应class文件和资源文件进行更新(保存时,就更新),但就是耗内存
然后就可以刷新网页显示新的结果了(启动只是启动服务器,在服务器上运行而已,访问路径还是这个,相当于你重新请求一下)
服务器运行可以理解为提供了http的管道连接,即可以接收http的信息,远程访问
也是访问资源,且有响应时,将数据放入浏览器
文件的访问,在本地上,也是直接访问资源,且将数据放入浏览器
一个是http直接的传输数据,一个是文件的传输数据,但本质都是传输数据的一种方式而已,且有相应的管道传输
而tomcat服务器就有http协议的方式,即有http传输的管道,mysql没有自带,即没有管道
注意HTTP port端口号的设置

在这里插入图片描述

在这里插入图片描述

注意:配置了上面,那么就需要访问时,加上他,才可以访问到对应项目的内容
Deployment里:
这个tomcat配置默认给当前项目部署起来,可以添加对不同项目的部署
再下面就是该项目路径的,用来表示当前项目路径,如图

在这里插入图片描述

部署在这里的项目,都会有一个配置文件,指定对应地址,即一个服务器可以访问这些添加的配置文件的项目,加一个
就多出来一个配置文件,但都在这个服务器配置里,即一个服务器配置,可以有多个不同项目
路径:C:\Users\用户名\ .IntelliJIdea2019.3\system\tomcat\Tomcat_8_5_55_javaweb_3\conf\Catalina\localhost
不同idea版本,位置可能不同
他将下面的表示为访问的最终目录,即简写
所以当你改变下面的这个路径时,前面Server的也会进行改变,使得访问时是后面真正路径下的文件,该文件下
就是web目录下的文件,web和src一样是特定文件,即他们的内容直接放在项目路径下(默认index的jsp和html)
所以前面的默认实际上就是配置文件的设置
那么总体意思就是说,一个服务器的配置可以操作多个项目(但默认地址只有一个,所以基本上要新建服务器配置)
当需要改变时,可以新建一个配置,新建的可以配置不同的
使得不用重新配置,并会将操作的项目进行存放且更新
上面只是配置,并没有进行操作,记得关闭开启过的服务器,否则运行时,会冲突,即运行不了
点击主界面右上角的启动服务器,来启动运行这个项目,即这里默认运行index.jsp
所以idea实际上是一个新的配置文件的作用
注意:上面的是启动时,idea自动使用浏览器来进行请求的,而不是服务器的操作,服务器基本只是用来响应,来存放项目的
Servlet的概念和使用(重点)-----------------------------
Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器
是Java语言编写的服务器端程序,换句话说,Servlet就是运行在服务器上的Java类
Servlet用来完成B/S架构下客户端请求的响应处理,也就是交互式地浏览和生成数据,生成动态Web内容
因为无论是请求还是响应,实际上底层都有代码完成的
对于浏览器来说一个" / “或者” \ “就可以了,但是若写多个连起来的” / “或者” \ “,最后都会默认为一个” / ",一开始的http后面可能是//
Servlet的编程步骤:
Servlet的编程步骤:
建立一个Java Web Application项目并配置Tomcat服务器
自定义类实现Servlet接口或继承 HttpServlet类(推荐) 并重写service方法
将自定义类的信息配置到 web.xml文件并启动项目,配置方式如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--对Servlet的配置-->
    <servlet>
        <!--  起别名,习惯起类名-->
        <servlet-name>HelloServlet</servlet-name>
        <!--指定上述别名所对应的类,即这个别名是给谁起的
        对于配置来说,一般默认路径,是class的项目下的路径
即可以说是整个模块在class里的模块里的路径,即可以叫做根路径
        根路径就是整个项目下的路径
        所以使用com开始一直进行指向
        -->
        <servlet-class>com.lagou.demo02.HelloServlet3</servlet-class>
        <!--可以是多例,只需要名称不同,路径不同即可,只是拦截不同哦-->
    </servlet>

    <!--对Servlet的映射配置-->
    <servlet-mapping> <!--一般多加,是合并的-->
        <servlet-name>HelloServlet</servlet-name>
        <!--对浏览器的访问地址的配置
        在原来的项目路径基础上加上这个地址
        -->
        <url-pattern>/hello</url-pattern> 
          <!--/代表后面都是,不包括起始(并且浏览器操作默认地址),/*代表后面都是,包括起始(即一开始起始就操作),并且浏览器访问也会再次拦截,即操作两次(如果操作了默认路径,也就是/(在idea配置,具体可以看后面的图),会再次的操作,也就是浏览器会执行来两次,第一次是跳转一次,第二次是默认的处理一次),SpringMVC一般也是这样,当然可能会有所改变,但是基本也是如此,有些框架中,可能还存在/**,一个*代表下一个任意目录,但是存在两个**代表都是(即根目录下所有),然而,我们web层中,servlet或者说MVC基本没有这样的,并且大多数都没有(且基本将两个**就看成路径,即在url加上两个*来访问,并且只拦截**了,但起始地址不包括的,并且浏览器默认操作该地址),可能会有,但是小众,或者说在其他操作中存在,比如MVC中的静态拦截操作就存在-->

        <!--加上地址时,会对总体地址进行请求
        加上后,若使用加上的地址后的总地址,中间判断
那么反射就会根据当前name找到对应的name(有约束必须存在这两个且要相等)
        否则报错,就如使用Schema和DTD这样的约束,来约束xml的
        并使用他的servlet-class的值所对应的类的方法
         其实xml也是有代码来获得他们的值的,并判断文件是否有该类的名称,并执行的
         得到名称,可以通过反射,来创建对象从而调用对应方法(如service方法)
反射会判断他是否调用或者父类及其以上是否有调用Servlet接口
         这时控制台就有方法的操作了
         -->

    </servlet-mapping>
    <servlet>
        <servlet-name>Parameter</servlet-name>
        <servlet-class>com.lagou.demo02.ParameterServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Parameter</servlet-name>
        <url-pattern>/Parameter</url-pattern>
    </servlet-mapping>


<servlet>
    <servlet-name>ConfigServlet</servlet-name>
    <servlet-class>com.lagou.demo02.ConfigServlet</servlet-class>
    <init-param>
        <param-name>username</param-name>
        <param-value>admin</param-value>
    </init-param>
    <init-param>
        <param-name>password</param-name>
        <param-value>123456</param-value>
    </init-param>
    <!--这个是初始化的一些参数和对应的值,就类似于表单提交时的name和对应的value
初始化参数可以使用对应的get方法来获得这些值
    方法调用与HttpServletRequest的方法中间的get后面加上Init就差不多了
    -->
</servlet>
    <servlet-mapping>
        <servlet-name>ConfigServlet</servlet-name>
        <url-pattern>/config</url-pattern>

    </servlet-mapping>
    
     <servlet>
        <servlet-name>Context</servlet-name>
        <servlet-class>com.lagou.demo02.ContextServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Context</servlet-name>
        <url-pattern>/Context</url-pattern>
    </servlet-mapping>
    <context-param>
    <param-name>param1</param-name>
    <param-value>value1</param-value>
</context-param>
    <context-param>
        <param-name>param2</param-name>
        <param-value>value2</param-value>
    </context-param>
    <!--对全局的对象的配置,即ServletContext接口的对象配置
    也是通过在get后加上Init的方式获得信息的,name和value也是对应,但他是全局的,即多个Servlet之间都可以用
    -->

</web-app>
前面注释中需要的图:

在这里插入图片描述

默认的资源:
<%--
  Created by IntelliJ IDEA.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
  $END$
  </body>
</html>

后续都是如此,除非你改变
Servlet接口:
javax.servlet.Servlet接口用于定义所有servlet必须实现的方法
package com.lagou.demo02;

import javax.servlet.*;
import java.io.IOException;

public class HelloServlet implements Servlet {
    //void init(ServletConfig config),由servlet容器调用,以向servlet指示servlet正在被放入服务中
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {

    }
    //ServletConfig getServletConfig()返回ServletConfig对象,该对象包含此servlet的初始化和启动参数
    @Override
    public ServletConfig getServletConfig() {
        return null;
    }
    //void service(ServletRequest req,ServletResponse res)由servlet容器调用,以允许servlet响应请求
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws 
        ServletException, IOException {
        System.out.println("接收到了浏览器的请求并做出了响应!");
    }
    //String getServletInfo()返回有关servlet的信息,如作者、版本和版权
    @Override
    public String getServletInfo() {
        return null;
    }
    //void destroy()由servlet容器调用,以向servlet指示该servlet正在退出服务
    @Override
    public void destroy() {

    }
}

GenericServlet类
javax.servlet.GenericServlet类主要用于定义一个通用的、与协议无关的servlet,该类实现了Servlet接口
若编写通用servlet,只需重写service抽象方法即可
package com.lagou.demo02;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

public class HelloServlet2 extends GenericServlet {
    //GenericServlet的方法
    //abstract void service(ServletRequest req,ServletResponse res)由servlet容器调用允许servlet响应请求
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws 
        ServletException, IOException {
        System.out.println("继承GenericServlet类的方式来创建Servlet");
    }
}

HttpServlet类:
javax.servlet.http.HttpServlet类是个抽象类并继承了GenericServlet类,用于创建适用于网站的HTTP Servlet
该类的子类必须至少重写一个方法
package com.lagou.demo02;

import com.sun.net.httpserver.HttpServer;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class HelloServlet3 extends HttpServlet {

    public HelloServlet3() {
        System.out.println("构造方法调用了");
    }
//void service(HttpServletRequest req,HttpServletResponse resp)根据请求决定调用doGet还是doPost方法
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, 
    IOException {
        System.out.println("这是采用继承HttpServlet类的方式创建,以后的开发中推荐该方式!");
        //因为操作多很多
    }
//void destroy()删除实例时释放资源
    @Override
    public void destroy() {
        System.out.println("销毁操作开始喽...");
    }
//void init()进行初始化操作
    @Override
    public void init() throws ServletException {
        System.out.println("初始化操作开始喽...");
    }
    //反射后,先构造,然初始,再打印,最销毁
}

Servlet 的生命周期:

在这里插入图片描述

构造方法只被调用一次,当第一次请求Servlet时调用构造方法来创建Servlet的实例
init方法只被调用一次,当创建好Servlet实例后立即调用该方法实现Servlet的初始化
这里需要注意:服务器自身的底层中,init真正被调用的是这样的:
void init(ServletConfig var1) throws ServletException;
并且他的子类也只有一个直接的处理,所以任何的关于初始化都是这样的处理:
public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }

    public void init() throws ServletException {
    }
这也是为什么执行有参数的和无参数的都可以进行处理,当你覆盖有参数的,那么自然init进行处理(默认情况下,servlet其实默认执行的是有参数的),没有时,由于对应的有参数的会执行无参数的,所以相当于也进行处理初始化了
service方法被多次调用,每当有请求时都会调用service方法来用于请求的响应
destroy方法只被调用一次,当该Servlet实例所在的Web应用被卸载前调用该方法来释放当前占用的资源
如停止服务器,即关闭服务器
package com.lagou.demo02;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(name = "HelloServlet4", urlPatterns = "/hello4")
//平常没有配置信息时,启动服务器,默认使用浏览器调用某个地址,发送请求,然后服务器响应给你
//而有配置信息则有如下情况
//实际上与xml类似,底层也是使用反射,来得到数值,并调用方法,之前我说明了,底层程序会去读xml的信息
//并通过程序进行操作
//使得路径添加和执行方法
//而注解也可以通过反射,来获得注解的信息,使得也可以添加和执行方法
//对于xml,启动服务器,idea中java底层会有去读取xml文件,获得信息
// 若使用加上的地址后的总地址,则会根据该地址对应其他元素的信息,使得后面操作或者进行执行调用方法
//对于注解,就如寻找xml一样,会一次扫描,路径,直到在一个类的找到注解,那么反射就会获得注解的信息
//其中name可以省略
// 只是用来映射的,类似于xml的servlet-name,但这里可以直接通过属性获得添加的地址
//并则创建当前找到注解的这个类的对象(通过地址来的,即有路径名,所以反射可以获得对象)
//与配置一样,从class项目的根路径开始
// 若使用加上的地址后的总地址,则会根据该地址对应该注解其他的属性信息,使得后面操作或者进行执行调用方法
//最后无论是xml还是注解,都会在构造方法后,即创建对象后检查是否实现servlet或者继承一个类中有存在实现的类
//否则则会报错,且刷新服务器,没有作用,因为在启动时,对应的java文件已经进行来读取
//即你的刷新对读取的文件没作用
// 因为不会再次读取,但可以使得,请求的文件进行改变,如html或者jsp等文件(jsp一般不行)
//当然重新部署和重启服务器最后都可以重新读取,即配置文件也会读取,和java文件也会读取,相当于全部重新来过
public class HelloServlet4 extends HttpServlet {
    //void doPost(HttpServletRequest req,HttpServletResponse resp)处理客户端的POST请求
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        System.out.println("Post请求方式...");
    }
//void doGet(HttpServletRequest req,HttpServletResponse resp)处理客户端的GET请求
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        System.out.println("Get请求方式...");
        this.doPost(request, response);
    }

   /* @Override
   该方法不重写的话,其实也会判断GET和POST请求方式,所以可以不写
   是通过this调用的,所以最后会调用该类的doGet和doPost方法
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, 
    IOException {
        System.out.println("原来使用注解可以如此潇洒...");
        String method = req.getMethod();  当使用总地址时,不只会调用这个方法,而且也会获得请求信息,这是请
        求中的操作
        并将信息放入HttpServletRequest类中,使用getMethod方法获得请求的方式
        HttpServletRequest 英文意思:请求对象
        HttpServletResponse 英文意思:响应
        System.out.println("获取到的请求方式为:" + method);
        if ("get".equalsIgnoreCase(method)) { 要大小写不可忽略,基本是GET和POST,而不是get和post
            doGet(req, resp);
        }
        if ("post".equalsIgnoreCase(method)) {
            doPost(req, resp);
        }
    }*/
}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>请求方式的测试</title>
</head>
<body>
<!-- 测试一下超链接的方式是否为Get请求方式 -->
<a href="hello4">测试Get请求</a>
<!-- 使用form表单的方式采用Get请求提交 -->
<!--<form action="hello4" method="get">-->
<form action="hello4" method="post">
    <input type="submit"/>
</form>
<!--提交时,根据当前路径-->
</body>
</html>
POST和GET请求(重点)---------------------------
GET请求:
/*
发出GET请求的主要方式: 
(1)在浏览器输入URL按回车   
(2)点击<a>超链接   
(3)点击submit按钮,提交 <form method="get">表单 这个get大小写忽略,因为只是判断是什么形式的
GET请求特点:  
会将请求数据添加到请求URL地址的后面,只能提交少量的数据、不安全
*/
POST请求:
/*
发出POST请求的方法如下: 
点击submit按钮,提交 <form method="post">表单 这个post大小写忽略,因为只是判断是什么形式的
POST请求的特点:   
请求数据添加到HTTP协议体中,可提交大量数据、安全性好
*/
ServletRequest接口:
基本概念:
javax.servlet.ServletRequest接口主要用于向servlet提供客户端请求信息,可以从中获取到任何请求信息
Servlet容器创建一个ServletRequest对象,并将其作为参数传递给Servlet的service方法
HttpServletRequest接口:
javax.servlet.http.HttpServletRequest接口是ServletRequest接口的子接口,主要用于提供HTTP请求信息的功能
不同于表单数据,在发送HTTP请求时,HTTP请求头直接由浏览器设置
可直接通过HttpServletRequest对象提供的一系列get方法获取请求头数据
ServletResponse接口:
javax.servlet.ServletResponse接口用于定义一个对象来帮助Servlet向客户端发送响应
Servlet容器创建ServletResponse对象,并将其作为参数传递给servlet的service方法
这些类是在服务器操作的,所以有请求信息和响应信息
HttpServletResponse接口:
javax.servlet.http.HttpServletResponse接口继承ServletResponse接口,以便在发送响应时提供特定于HTTP的功能
void sendRedirect(String location),使用指定的重定向位置URL向客户端发送临时重定向响应
package com.lagou.demo02;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import java.util.Map;
import java.util.Random;
import java.util.Set;

public class ParameterServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        // 6.设置请求信息中的编码方式为utf-8来解决乱码问题
        request.setCharacterEncoding("utf-8");
        //对获得的数据进行操作编码格式

        // 1.获取指定参数名称对应的参数值并打印
        //String getParameter(String name)以字符串形式返回请求参数的值,如果该参数不存在,则返回空值
        String name = request.getParameter("name");//若是多个,则取第一个
        //若在这一行设置编码格式,那么上述就是乱码,即可以知道
        //其中的数据是以字节方式存在的
        //所以对于数据,不是本内部的,基本都要进行解码
        //所以上面就进行了解码,若没有设置对应编码,那么解码可能会出现乱码错误
        //java字节码以及放入内存,需要重新部署
        System.out.println("获取到的姓名为:" + name);
        //String[] getParameterValues(String name)
        // 返回一个字符串对象数组,其中包含给定请求参数所具有的所有值,如果该参数不存在,则返回空值
        String[] hobbies = request.getParameterValues("hobby");
        System.out.print("获取到的爱好有:");
        for (String ts : hobbies) {
            System.out.print(ts + " ");
        }
        System.out.println();

        System.out.println("-------------------------------------------------------");
        // 2.获取所有参数的名称
        //Enumeration getParameterNames()
        // 返回包含此请求中包含的参数名称的字符串对象的枚举。如果请求没有参数,则方法返回空枚举
        //即获得所有对应参数哦
        Enumeration<String> parameterNames = request.getParameterNames();
             //枚举,该接口存在的子类对象
        System.out.print("获取到的所有参数名称为:");
        while (parameterNames.hasMoreElements()) {
            System.out.print(parameterNames.nextElement() + " ");
        }
        System.out.println();

        System.out.println("-------------------------------------------------------");

        // 3.获取请求参数名和对应值的第二种方式
        //因为存放的就是键值对,一个参数对应一个或多个值
        //Map<String, String[]>getParameterMap()返回请求参数的键值对,一个键可以对应多个值
        Map<String, String[]> parameterMap = request.getParameterMap();
        // 使用Map集合中所有的键值对组成Set集合
        Set<Map.Entry<String, String[]>> entries = parameterMap.entrySet();

        // 遍历Set集合
        for (Map.Entry<String, String[]> me : entries) {

            System.out.print(me.getKey() + "对应的数值有:");
            for (String ts : me.getValue()) {
                System.out.print(ts + " ");
            }
            System.out.println();
        }

        System.out.println("-------------------------------------------------------");
        // 4.获取客户端请求的其它信息
        //String getRemoteAddr()返回发送请求的客户端或最后一个代理的IP地址
        System.out.println("发送请求的客户端IP地址为:" + request.getRemoteAddr());

        //int getRemotePort()返回发送请求的客户端或最后一个代理的端口号
        System.out.println("发送请求的客户端端口号为:" + request.getRemotePort());
        //String getRequestURI()返回此请求的资源路径信息
        System.out.println("请求资源的路径为:" + request.getRequestURI());
        //StringBuffer getRequestURL()返回此请求的完整路径信息
        System.out.println("请求资源的完整路径为:" + request.getRequestURL());
        //String getMethod()返回发出此请求的HTTP方法的名称,例如GET、POST
        System.out.println("请求方式为:" + request.getMethod());
        //String getQueryString()返回路径后面请求中附带的参数
        System.out.println("请求的附带参数为:" + request.getQueryString()); //POST没有,改成get就可
        //get提交时,会将这些参数显示到路径后面,中间有个?连接,后面用&连接
        // 多个相同的就多个参数连接如
        // http://localhost:8080/task01_dome02/Parameter?name=1&age=&hobby=Java&hobby=C
        //那么附带参数就是
        // name=1&age=&hobby=Java&hobby=C
        //所以也可以使用get后面加参数来进行传输数据,即反过来,变成对应的变量
        //String getServletPath()返回此请求中调用servlet的路径部分
        System.out.println("请求的Servlet路径为:" + request.getServletPath());

        System.out.println("-------------------------------------------------------");
        // 5.向浏览器发出响应数据
        // 获取响应数据的默认编码方式
        //String getCharacterEncoding()获取响应内容的编码方式
        String characterEncoding = response.getCharacterEncoding();
        System.out.println("服务器响应数据的默认编码方式为:" + characterEncoding); // ISO-8859-1
        //字节传输需要编码过程,变为字节,编码编成字节,解码解成字符图,简称解释
        // 设置服务器和浏览器的编码方式以及文本类型
        //void setContentType(Stringtype)
        // 如果尚未提交响应,则设置发送到客户端响应的内容类型
        // 内容类型可以包括字符编码规范,例如text/html;charset=UTF-8
        response.setContentType("text/html;charset=UTF-8");//说明一下,UTF-8忽略大小写
        //且text/html可以不写,但有些特殊情况,需要html的文件格式,所以最好写
        //PrintWriter getWriter()返回可向客户端发送字符文本的PrintWriter对象
        //既然是返回,那么下次就是得到,所以不会进行重新的输入流删除
        PrintWriter writer = response.getWriter();
        //获取对浏览器的输出流,将数据写入body中
        //writer.write("I Received!");
        //writer.write("我接收到了!");
        Random ra = new Random();
        int num = ra.nextInt(100) + 1;
        writer.write("<h1>" + num + "</h1>"); //服务器发送数据到body里面
         //response有自带的空的文档(响应报文里面的响应数据,我称为对应文档)
        //实际上也可说是协议管道,如tcp自带的管道(后面以及后续,若是空的返回,则简称为空文档)
        //没有指定页面的话,就是自带的一个空文档写入浏览器,而上面的就是在该文档输入
        //即就是本来空的body,变成有值了
        //只所以是空文档,是因为一个Servlet对应一个地址,该地址若没有指定资源
        //就默认添加一个空文档(空的响应体),若有资源,也会被输入流进行消除,所以基本上是空文档
        System.out.println("服务器发送数据成功!");
        writer.close();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        this.doPost(request, response);
    }
}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>请求参数的获取和测试</title>
</head>
<body>
<form action="Parameter" method="post">
    姓名:<input type="text" name="name"/><br/>
    年龄:<input type="text" name="age"/><br/>
    爱好:<input type="checkbox" name="hobby" value="Java"/>Java
          <input type="checkbox" name="hobby" value="C"/>C
          <input type="checkbox" name="hobby" value="C++"/>C++<br/>
    <input type="submit" value="提交"/>
</form>
</body>
</html>
Servlet接收中文乱码(重点)-------------------------
接收乱码原因:
浏览器在提交表单时,会对中文参数值进行自动编码
当Tomcat服务器接收到浏览器请求后自动解码,当编码与解码方式不一致时,就会导致乱码(servlet虽然也是一个框架,但是与tomcat结合的,并且本质是tomcat接收数据给servlet的,所以tomcat才是本质的编码设置者)
解决POST接收乱码:
/*
接收之前设置编码方式:  
request.setCharacterEncoding("utf-8")  //他并不能解决get(所以在以后的乱码过滤器中,通常并不能解决get,如后面的67章博客的中文乱码过滤器),因为get和post的获取信息不在一个地方,否则又怎么区分get和post的数据大小的区别呢
提示: 
必须在调用request.getParameter("name")之前设置
*/
解决GET接收乱码:
/*将接收到的中文乱码重新编码: 
	接收到get请求的中文字符串  
    String name = request.getParameter("name");  
    将中文字符重新编码,默认编码为ISO-8859-1  
    String userName = new String(name.getBytes("ISO-8859-1"),"utf-8");
    先通过编码方式编码回来,那么原来的若是utf8来的,那么这里就行,然后再解码成utf8编码的格式
*/
这里是相对底层的编码处理了,无论是jsp还是其他的如mvc框架,最终都是处理这里的编码的,当然,可能还会有隐藏的编码,但是一般我们也很难碰到(框架可能存在,在以后遇到会说明的,比如:68章博客说明的全局编码)
当然,无论是什么编码问题,只需要解码和编码一致即可,一开始的编码格式或者显示(统一编码作为称呼的)由当前写上的数(或者说文本)和当前编码影响的,也更加证明编码一致即可
ServletConfig接口(熟悉):
基本概念
javax.servlet.ServletConfig接口用于描述Servlet本身的相关配置信息,在初始化期间用于将信息传递给Servlet配置对象
即只针对一个,相当于在反射中又读取了xml出现的读取信息,即< init-param >
可以使用这个接口来获得对象,该信息在反射时赋值了
并当成参数,进行初始化,使得只作用于反射的类,差不多是当前类
可以看到他的配置在servlet里面
配置方式:
<servlet>
    <servlet-name>ConfigServlet</servlet-name>
    <servlet-class>com.lagou.demo02.ConfigServlet</servlet-class>
    <init-param>
        <param-name>username</param-name>
        <param-value>admin</param-value>
    </init-param>
    <init-param>
        <param-name>password</param-name>
        <param-value>123456</param-value>
    </init-param>
    <!--这个是初始化的一些参数和对应的值,就类似于表单提交时的name和对应的value
初始化参数可以使用对应的get方法来获得这些值
    方法调用与HttpServletRequest的方法中间的get后面加上init就差不多了
    -->
</servlet>
    <servlet-mapping>
        <servlet-name>ConfigServlet</servlet-name>
        <url-pattern>/config</url-pattern>

    </servlet-mapping>

package com.lagou.demo02;

import javax.servlet.*;
import java.io.IOException;
import java.util.Enumeration;

public class ConfigServlet implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        //初始化,对象后,判断Servlet后,然后初始化,这时基本就没有错误了
        System.out.println("初始化操作执行了...");
        //String getServletName()返回Servlet的别名
        System.out.println("Servlet的别名是:" + servletConfig.getServletName());
        // ConfigServlet,这是当指向总地址时的对应name

        System.out.println("-----------------------------------------------");
        // 获取配置文件中的初始化参数信息
        //String getInitParameter(String name)返回包含初始化参数值的字符串,如果该参数不存在,则返回null
        String userName = servletConfig.getInitParameter("userName");
        System.out.println("获取到的初始化用户名为:" + userName);
        // 获取所有配置参数的名称
        //Enumeration getInitParameterNames()
        // 将servlet的初始化参数的名称作为字符串对象的枚举返回,如果servlet没有初始化参数,则返回空枚举
        //枚举,该接口存在的子类对象
        Enumeration<String> initParameterNames = servletConfig.getInitParameterNames();


        while (initParameterNames.hasMoreElements()) {
            System.out.println("初始化参数名为:" + initParameterNames.nextElement());
        }
        //注意:由于是配置文件的获得,其中xml有这name不能相同的约束
        //那么一个name只能有一个值了,因为value只能对应一个
        //与表单类似,所以没有对应的Values方法来获得全部的一样的值,即只有getInitParameter来获得值
        //如HttpServletRequest接口的引用request的方法
        // String[] hobbies = request.getParameterValues("hobby");

        System.out.println("-----------------------------------------------");
        // 获取ServletContext接口的引用
        //ServletContext getServletContext()返回对调用方正在其中执行的ServletContext的引用
        ServletContext servletContext = servletConfig.getServletContext();

        System.out.println("获取到的ServletContext引用为:" + servletContext);//自动调用toString
        //没重写,就是包名@哈希码值(这里是没重写的)
        
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws 
        ServletException, IOException {

    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}

ServletContext接口(熟悉):
基本概念
javax.servlet.ServletContext接口主要用于定义一组方法,Servlet使用这些方法与它的Servlet容器通信
服务器容器在启动时会为每个项目创建唯一的一个ServletContext对象,用于实现多个Servlet之间的信息共享和通信
在Servlet中通过this.getServletContext()方法可以获得ServletContext对象
由于一个服务器里可以有多个项目,那么服务器自动的会对这些项目创建对象一个全局对象,本身自带的类型(底层)
使得让多个通过反射的类之间的通信,其中创建后,会读取xml配置信息,没有对应的,不会读取
可以看到他的配置不在servlet里面
package com.lagou.demo02;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;

public class ContextServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        // 1.配置参数的获取
        ServletContext servletContext = getServletConfig().getServletContext();
        //HttpServlet的重写方法中,有个getServletConfig()方法可以得到ServletConfig
        //且HttpServlet实现了这个方法
        //当然,getServletContext()这个方法也有,即也可以直接getServletContext()方法
        //上面两种可以省略this,因为会默认加上this,但实际是只有在相同的时候才会操作this,而方法基本操作不到
        //于是,就可以使用ServletConfig来调用getServletContext()方法来获得这个ServletContext引用
        //里面是获得全局对象,就如私有静态,即常量,需要公有方法获得
        //Enumeration getInitParameterNames()
        // 将servlet的初始化参数的名称作为字符串对象的枚举返回,如果servlet没有初始化参数,则返回空枚举
        Enumeration<String> initParameterNames = servletContext.getInitParameterNames();
        while (initParameterNames.hasMoreElements()) {
            String s = initParameterNames.nextElement();
            //String getInitParameter(String name)返回包含初始化参数值的字符串
            //如果该参数不存在,则返回null
            System.out.println( s + "对应的值为:" + servletContext.getInitParameter(s));
//看配置文件发现,他的servlet里没有设置init-param
// 所以获取不到其他的servlet的值,这就证明了,ServletConfig是只作用于servlet对应的类
            //而Context-param没有指定类名,也可获得,也证明了他是全局的
        }

        System.out.println("----------------------------------------------------------");
        // 2.相关路径的获取
        // 本质上就是获取工程路径    /工程名
        //String getContextPath()返回与此上下文关联的主路径
        String contextPath = servletContext.getContextPath();
        System.out.println("获取上下文关联的路径信息为:" + contextPath); // /task01_demo02

        // / 在服务器被解析为: http://ip地址:端口号/工程名  获取实际路径信息
        // 获取到的是部署工程路径信息   对应  当前工程中的web目录
        //String getRealPath(String path)返回包含给定虚拟路径的实际路径的字符串
        String realPath = servletContext.getRealPath("/");//实际路径,即里面,但还没指定
        // C:\Users\Marz\IdeaProjects\javaweb\out\artifacts\task01_demo02_war_exploded\
        //如果在服务器中(通常说的服务器是Linux),那么这个路径会按照服务器的文件系统来决定,而文件系统自然也会改变io流,所以只有这里对应了,就算是其他的服务器,只要都是操作同一个io流(都支持的话),那么就没有问题,而java基本都支持,所以不用考虑路径是否正确了,基本是正确的
        System.out.println("获取到的实际路径信息为:" + realPath);
        //实际上class文件与配置文件同路径,class文件直接就是src的内容
        //InputStream getResourceAsStream(Stringpath)将位于指定路径的资源作为InputStream对象返回
        //这个方法可以让其他文件的内容进行读取,而可以不用xml的文件了
        System.out.println("----------------------------------------------------------");
        // 3.设置和获取属性信息
        //void setAttribute(Stringname, Object object)将指定的属性名和属性值绑定到当前对象
        servletContext.setAttribute("key", "value");
        //ServletContext接口的对象,有键值对的存储,key为键,value为值

        Object key = servletContext.getAttribute("key");
        System.out.println("根据参数指定的属性名获取到的属性值为:" + key); // value
        //void removeAttribute(Stringname)删除指定的属性名信息
        servletContext.removeAttribute("key");
        //Object getAttribute(Stringname)根据执行的属性名获取属性值
        key = servletContext.getAttribute("key");
        System.out.println("根据参数指定的属性名获取到的属性值为:" + key); // null
        //基本上后面就是跟Attribute,前面是get,set,remove,调用者ServletContext的引用对象
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        this.doPost(request, response);
    }
}

servlet 英文意思:小服务程序,注解对应class文件
Servlet+JDBC应用(重点)-----------------------
在Servlet中可以使用JDBC技术访问数据库,常见功能如下:
查询DB数据,然后生成显示页面,例如:列表显示功能
接收请求参数,然后对DB操作,例如:注册、登录、修改密码等功能
为了方便重用和便于维护等目的,经常会采用DAO(Data Access Object)模式对数据库操作进行独立封装

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <servlet>
        <servlet-name>RegisterServlet</servlet-name>
        <servlet-class>com.lagou.demo01.servlet.RegisterServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>RegisterServlet</servlet-name>
        <url-pattern>/register</url-pattern>
    </servlet-mapping>
</web-app>
package com.lagou.demo01.servlet;

import com.lagou.demo01.dao.UserDao;
import com.lagou.demo01.model.User;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class RegisterServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        // 1.获取请求对象中保存的用户名和密码信息
        String userName = request.getParameter("userName");
        System.out.println("获取到的用户名为:" + userName);
        String password = request.getParameter("password");
        System.out.println("获取到的密码为:" + password);
        // 2.将接受到的用户名和密码信息打包成用户对象交给DAO层进行处理
        User user = new User(userName, password);
        UserDao userDao = new UserDao();
        int res = userDao.createUser(user);
        // 3.将处理结果响应到浏览器
        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();
        if (1 == res) {
            System.out.println("注册成功!");
            writer.write("<h1>注册成功!</h1>");
        } else {
            writer.write("<h1>注册失败!</h1>");
        }
        writer.close();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        this.doPost(request, response);
    }
}

DAO工厂(工厂模式) 工厂类:封装了对象的创建细节,为调用者提供符合要求的对象
package com.lagou.demo01.model;

public class User {
    private int id;
    private String userName;
    private String password;

    public User() {
    }

    public User(String userName, String password) {
        this.userName = userName;
        this.password = password;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "userName='" + userName + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

package com.lagou.demo01.util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class DbUtil {
    private static String jdbcName;   // 用于描述驱动信息
    private static String dbUrl;      // 用于描述URL信息
    private static String dbUserName; // 用户描述用户名信息
    private static String dbPassword; // 用户描述密码信息

    // 进行静态成员的初始化操作
    static {
        jdbcName = "com.mysql.jdbc.Driver"; //mysql驱动包
        dbUrl = "jdbc:mysql://localhost:3306/db_web";
        dbUserName = "root";
        dbPassword = "123456";
        try {
            Class.forName(jdbcName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取连接
     * @return
     * @throws SQLException
     */
    public static Connection getConnection() throws SQLException {
        Connection con = DriverManager.getConnection(dbUrl, dbUserName, dbPassword);
        return con;
    }

    /**
     * 关闭连接
     * @param con
     * @throws SQLException
     */
    public static void closeConnection(Connection con, PreparedStatement psts) throws SQLException {
        if (null != con) {
            con.close();
        }
        if (null != psts) {
            psts.close();
        }
    }
}

在项目右键,找到Open Module Settings打开项目的模块设置,可以直接定位,不用再打开总模块设置了
package com.lagou.demo01.test;

import com.lagou.demo01.util.DbUtil;

import java.sql.Connection;
import java.sql.SQLException;

public class DbUtilTest {

    public static void main(String[] args) {

        Connection connection = null;
        try {
            connection = DbUtil.getConnection();
            System.out.println("连接数据库成功!");
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            /*try {
                DbUtil.closeConnection(connection);
            } catch (SQLException e) {
                e.printStackTrace();
            }*/
        }
    }
}

其实若有相同的类,那么这两个类一定是不同包,用静态时,会提示包的路径,即其实静态是根据包来找类的
package com.lagou.demo01.dao;

import com.lagou.demo01.model.User;
import com.lagou.demo01.util.DbUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class UserDao {

    public int createUser(User user) {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        try {
            // 1.获取连接
            connection = DbUtil.getConnection();
            // 2.准备sql语句
            String sql = "insert into t_user values(null, ?, ?)";
            // 3.获取PreparedStatement类型的引用
            preparedStatement = connection.prepareStatement(sql);
            // 4.向问号所占的位置设置数据
            preparedStatement.setString(1, user.getUserName());
            preparedStatement.setString(2, user.getPassword());
            // 5.执行sql语句
            int row = preparedStatement.executeUpdate();
            return row; // 执行成功
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 6.关闭资源
            try {
                DbUtil.closeConnection(connection, preparedStatement);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return 0; // 执行失败
        //若再finally里return,就按照finally的return来返回
    }
}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>实现简单的注册功能</title>
</head>
<body>
<form action="register" method="post">
    用户名:<input type="text" name="userName"/><br/>&nbsp;&nbsp;&nbsp;&nbsp;码:<input type="text" name="password"/><br/>
    <input type="submit" value="注册"/>
</form>
</body>
</html>
实际上根据这个案例,可以知道,Servlet中的Http对应的接口是能操作请求信息和响应信息的接口
反射中获取能操作这些请求信息和响应信息的接口及其对象
而Servlet是用来处理这些操作的
重定向和转发(重点):
重定向的概念:
首先客户浏览器发送http请求,当web服务器接受后发送302状态码响应及对应新的location给客户浏览器
客户浏览器发现是302响应(302基本是请求重定向的状态码),则自动再发送一个新的http请求,请求url是新的location地址(一般是重定向给的)
服务器根据此请求寻找资源并发送给客户
重定向的实现:
实现重定向需要借助javax.servlet.http.HttpServletResponse接口中的以下方法:
void sendRedirect(String location),使用指定的重定向位置URL向客户端发送临时重定向响应
//当你发送请求时,运行到这个代码,最后他会给你参数的地址放到响应信息里,并将响应信息的状态码变为302
//当看到302时,就会重新请求,且请求地址是传过来的参数地址,也就是的location值
//在检查里的网路中可以看到location的值
//改变从服务器初始路径下改变,即工程路径下改变
//所以当浏览器请求时,让你原来加的地址变成了这个新的location值了,就将改变的地址重新请求,这就是重定向
//若参数地址是完整的地址,那么就不会再工程路径下改变了,而是整个URL路径进行改变
//304状态码,服务端已经执行了GET,但文件未变化
//200状态码,成功处理了请求,即响应正常
注意:在java代码里,虽然请求地址信息放到了响应体中,但最后只有执行完对应Servlet程序后,才会发送响应体
并且当你设置了重定向地址后,后面不可再写重定向代码,否则报错
重定向的原理:

在这里插入图片描述

重定向的特点:
重定向之后,浏览器地址栏的URL会发生改变
重定向过程中会将前面Request对象销毁,然后创建一个新的Request对象,即原来的请求对象被销毁(原来的请求信息没了)
重定向的URL可以是其它项目工程
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <servlet>
        <servlet-name>RedirectServlet</servlet-name>
        <servlet-class>com.lagou.demo02.servlet.RedirectServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>RedirectServlet</servlet-name>
        <url-pattern>/redirectServlet</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>ForwardServlet</servlet-name>
        <servlet-class>com.lagou.demo02.servlet.ForwardServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ForwardServlet</servlet-name>
        <url-pattern>/forwardServlet</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>TargetServlet</servlet-name>
        <servlet-class>com.lagou.demo02.servlet.TargetServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>TargetServlet</servlet-name>
        <url-pattern>/targetServlet</url-pattern>
    </servlet-mapping>



</web-app>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>重定向后的页面</title>
</head>
<body>
<h1>服务器重新指定位置后的页面</h1>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>重定向的测试</title>
</head>
<body>
<form action="redirectServlet" method="post">
    <input type="submit" value="重定向"/>
</form>
</body>
</html>
package com.lagou.demo02.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class RedirectServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        System.out.println("接收到了浏览器的请求...");
        // 重定向,也就是给浏览器发送一个新的位置
        //response.sendRedirect("target.html"); 加/代表端口开始
        response.sendRedirect("https://www.baidu.com/index.php?tn=monline_3_dg");

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        this.doPost(request, response);
    }
}

转发的概述-------------------------------------
转发的概念:
个Web组件(Servlet/JSP)将未完成的处理通过容器转交给另外一个Web组件继续处理
转发的各个组件会共享Request和Response对象
可以理解为当前的Servlet给另外一个Servlet
转发与重定向的区别就是:
转发是浏览器请求后,Web组件将帮你再次请求一次
也可以说是将路径给反射出来的对象,不用浏览器来给了,相当于重新请求
即给另外一个Web组件处理,即跳过去了
与重定向一样,只有执行完对应Servlet才会进行响应,转发到其他Servlet里,且不可再次转发,否则报错
重定向是Web组件要你重新请求一次
注意:实际上HttpRequest和HttpResponse只是中间操作,存放对应数据
即request对象是服务器对浏览器请求的封装,而response是服务器对服务器响应的封装,即信息是由他们的对象接收的
当程序执行完后,发送对应的对象信息
即可以说是请求信息给值(地址)给HttpRequest,加数据(HttpResponse)给响应信息
对中间后台程序内存或者响应信息进行操作而已,若没有写,也不会对路径造成影响
该显示的显示(即没有对其对象进行操作,那么就是原来对应的信息)
可以看出他们是对信息的扩展
而对于重定向和转发,实际上最后都是操作路径,但重定向若在转发前面
在转发时,会被重定向阻塞,最后重定向的响应信息就会保存错误信息,交给页面,然后浏览器就会显示,这是响应信息
且会使得重定向没有操作,使得页面没有进行跳转(没有302状态码了,即没有跳转,还是路径的响应信息)
若转发在重定向前面,那么由于转发是交给其他Web组件(即Servlet),相当于隔离的操作,即他会显示页面,且它会阻塞重定向
使得报错而结束程序,最后再操作新的Servlet
最后使用写入数据时,每个Servlet都会有对应的一个空文档,因为请求的路径就是请求的一个文件,若没有对应文件
则是添加一个该文件的空的文档(读取就会创建,IO流的读取会创建没有对应地址的文件)
且一个路径对应一个Servlet,所以才说每个Servlet都会有对应的一个空文档
若有资源,也会被输入流进行消除,所以基本上是空文档,当Servlet被销毁时,创建的文档也会删除
最后:转发的请求响应也是原来路径的请求响应,即原来路径的地址还是原来的,不是服务器转发的,而重定向则会改变原来的
可以理解为,转发不变原路径,重向改变原路径,浏览器请求时可以观察到
转发的实现:
package com.lagou.demo02.servlet;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class ForwardServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        System.out.println("接收到了浏览器的请求...");
        // 向request对象中设置属性信息
        //void setAttribute(String name,Object o)在此请求中存储属性值
        //绑定数据到Request对象
        request.setAttribute("key1", "value1");
        // 转发,也就是让Web组件将任务转交给另外一个Web组件
        //RequestDispatcher getRequestDispatcher(String path)
        // 返回一个RequestDispatcher对象,该对象充当位于给定路径上的资源的包装器
        //RequestDispatcher requestDispatcher = request.getRequestDispatcher("/targetServlet");
                //当路径和资源名称相同时,优先考虑路径,因为他先拦截的
        //获取转发器对象
        RequestDispatcher requestDispatcher = request.getRequestDispatcher("https://www.baidu.com/");
        //参数放的是新的地址,一般默认工程路径开始(加/的话),否则实际上是替换最后的路径,是因为是内部服务器的转发,不是浏览器的重新请求
        //即只能是本服务器存在的路径,或者配置路径,而重定向的完整路径会被请求,因为location变成了完整路径
        //与location有关,单独路径,会使用获得的配置文件默认工程路径(发送)
        //而完整路径(不发送),就不会使用了
        //其实直接的文件名也是路径的
        //如a文件和a.html文件在文件里都是都是路径
        //都是文本,放到浏览器里,会自动加上对应标签,有了不自动添加,而html只是一个规范而已,实际都是文件
        //由于转发是服务器将新的请求再次交给自己,所以需要请求信息
        //即HttpServletRequest,先将路径请求路径弄好
        //即request.getRequestDispatcher()方法路径资源信息
        //然后再提交给自己服务器,使得重新请求,是服务器去请求
        //而不是浏览器,也可以不说是请求,只是将该路径给其他由xml反射出来的对象,相当于重新请求
        //void forward(ServletRequest request, ServletResponse response)
        // 将请求从一个servlet转发到服务器上的另一个资源(Servlet、JSP文件或HTML文件)
        //转发操作
        requestDispatcher.forward(request, response);
        //不止再次请求,还会将当前的请求操作信息和响应操作信息也会发送过去
        // 注意:这些只是存放对应信息的,即可以通过他们获得请求信息和添加响应操作
        // 对原来的请求信息和响应无影响,可以将他们设置为null来验证
        // 且转发时,这些信息并不会覆盖,转发后的Web组件的对应存放信息
        //而是会扩展信息,即相同的属性,不去扩展
        //不同属性,则扩展,如转发后,对应请求头的路径信息是转发后的路径,原来的路径被扩展


    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        this.doPost(request, response);
    }
}

package com.lagou.demo02.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class TargetServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        System.out.println("转发过来了...");
        // 获取request对象中的属性值判断是否共享
        //Object getAttribute(String name)将指定属性值作为对象返回,若给定名称属性不存在,则返回空值
        Object key1 = request.getAttribute("key1");
        //只有HttpServletRequest有该方法设置,HttpServletResponse没有
        System.out.println("获取到的属性值为:" + key1); // value1
        // 通过打印流向页面写入转发成功的信息
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().write("<h1>转发成功!</h1>");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        this.doPost(request, response);
    }
}

转发的特点:
转发之后浏览器地址栏的URL不会发生改变
转发过程中共享Request对象和Response对象
转发的URL不可以是其它项目工程
重定向和转发的比较:

在这里插入图片描述

重定向:外部
转发:内部
Servlet线程安全(重点)------------------------------
服务器在收到请求之后,会启动一个线程来进行相应的请求处理
默认情况下,服务器为每个Servlet只创建一个对象实例
当多个请求访问同一个Servlet时,会有多个线程访问同一个Servlet对象,此时就可能发生线程安全问题(这就需要考虑公共资源的影响了,特别的如静态,或者公共map集合等等)
多线程并发逻辑,需要使用synchronized对代码加锁处理,但尽量避免使用
package com.lagou.demo02.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet(name = "ThreadServlet", urlPatterns = "/thread")
public class ThreadServlet extends HttpServlet {

    //private String name; // 准备一个成员变量,作为共享资源

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        //synchronized (this) {
            // 1.获取request对象中名字为name的参数数值并赋值给成员变量name
            String name = request.getParameter("name");
            System.out.println("获取到的name数值为:" + name);
        //可以在路径后面加上对应的信息来发送,使得这里获得数据
            // 2.睡眠5秒钟
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 3.使用打印流将成员变量name的数值发送给浏览器
            PrintWriter writer = response.getWriter();
            writer.write("<h1>" + name + "</h1>");
            writer.close();
        //}
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        this.doPost(request, response);
    }
}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Servlet线程安全的测试</title>
</head>
<body>
<iframe width="600px" height="100px" src="thread?name=zhangfei"></iframe><br/>
<iframe width="600px" height="100px" src="thread?name=guanyu"></iframe><br/>
<iframe width="600px" height="100px" src="thread?name=liubei"></iframe><br/>
    <!--
iframe标签可以将一部分显示页面,与frame一样,但他是比例显示,而这里是指定大小显示
若不指定,则使用默认的宽度和高度
-->
</body>
</html>
重定向和转发的地址可以加斜杠也可不加,反正会默认加,即多余的斜杠会变成一个,一般代表中间的
无论是重定向还是转发,当前程序都会执行,其中Servlet执行完,就会响应,对应相同的Response就不可响应了
状态管理(重点)-----------------------
Web程序基于HTTP协议通信,而HTTP协议是"无状态"的协议,一旦服务器响应完客户的请求之后
就断开连接,而同一个客户的下一次请求又会重新建立网络连接
服务器程序有时是需要判断是否为同一个客户发出的请求,比如客户的多次选购商品
因此,有必要跟踪同一个客户发出的一系列请求
把浏览器与服务器之间多次交互作为一个整体,将多次交互所涉及的数据保存下来,即状态管理
多次交互的数据状态可以在客户端保存,也可以在服务器端保存,状态管理主要分为以下两类:
客户端管理:将状态保存在客户端。基于Cookie技术实现
服务器管理:将状态保存在服务器端。基于Session技术实现
Cookie技术(重点):
基本概念:
Cookie本意为"饼干"的含义,在这里表示客户端以"名-值"形式进行保存的一种技术
浏览器向服务器发送请求时,服务器将数据以Set-Cookie消息头的方式响应给浏览器
然后浏览器会将这些数据以文本文件的方式保存起来
当浏览器再次访问服务器时,会将这些数据以Cookie消息头的方式发送给服务器
package com.lagou.demo03.servlet;

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;

@WebServlet(name = "CookieServlet", urlPatterns = "/cookie") 
//在这里注解和xml中必须要有斜杠"/",其他的斜杠不可
//因为有些是判断,有些是默认加上/(有/的话不会加上),这里没有默认加上/,所以需要手动加
//且能是其他资源,如cookie.jsp,但通常是直接的单词访问,即cookie
//因为这里写资源的话,会使得本来真正的资源访问不了,即xml和这里优先级高
//优先级,配置文件>注解>真正的资源,其中当配置文件中没找到时,就直接找资源文件
重定向和转发没有要求,直接的还是其他资源都可以,但重定向不能加斜杠,会认为最开始路径,而转发可以,是添加路径(一般默认是工程路径开始)
public class CookieServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        // 1.测试一下浏览器的请求是否到达
        System.out.println("看看有没有执行到这里哦!");
        // 2.创建Cookie对象并添加到响应信息中
        //使用javax.servlet.http.Cookie类的构造方法实现Cookie的创建
        //Cookie(String name, String value)根据参数指定数值构造对象
        Cookie cookie = new Cookie("name", "zhangfei");
        //使用javax.servlet.http.HttpServletResponse接口的成员方法实现Cookie的添加
        //void addCookie(Cookie cookie)添加参数指定的对象到响应
        response.addCookie(cookie);
        //由于要发送,所以需要放到响应信息
        System.out.println("创建Cookie成功!");
        //一开始,若你的浏览器开启Cookie的话,Cookie里是没有内容的,只有服务器发送过来时才有内容
        //有些服务器默认发送JSESSIONID名称和对应的值,这个对应的值是会随着服务器启动而变化
        //这是Session的属性和对应的值,后面可以知道作用
        //当然不同的服务器的name的值有可能不同,即JSESSIONID有可能不同
        //Cookie是来保存一种键值对的数据的存在
        //当服务器创建Cookie时,将Cookie信息放在操作响应信息的response里,然后在交给响应信息
        //之后浏览器有多余的Cookie内容了
        //默认关闭浏览器后对Cookie内容进行清除,关闭页面不会
        //也可以去缓存清除Cookie,则去缓存中清除Cookie,那么Cookie的内容就会没有,所以注意缓存(注意刷新)
        //注意:Cookie的唯一标识是name,相同的name就会进行修改
        //之后浏览器再次请求时,该Cookie信息就会发送过去
        //这里可以知道,除了我们不好改变的请求信息和响应信息外
        //我们也可以自己创建信息来进行互相使用,这是可以操作的信息
        //而不是有点固定的请求信息和响应信息
        //由于Cookie是服务器给浏览器的,那么这个数据差不多是一种标识
        //由于这个操作信息会存在对应页面,所以当你请求多次时,服务器可以根据多次相同的数据,而进行对应的操作
         //如查找选择,当你第一次查找时,会出现不同的选择,这时若你选择一个
        //那么服务器就会将你选择的信息变成Cookie,响应给浏览器
        //其中Cookie记录了当前(通常是当前)响应浏览器的路径(域名的路径)和域名,与url对应(虽然通常我们需要访问后才可以看到,只要浏览器得到过且没有删除,实际上就算不访问,他还是存在的)
        //端口一般不算(不会记录,除了个别的,即特殊的,具体可以百度),即可以随便改变
        //其中路径一般是请求的整体路径的下个路径,如/a/b/c,那么就是/a/b,代码可以设置路径,看后面操作
        //进行请求时,对应域名的路径的Cookie就会发送到对应服务器
        //如果域名和路径不匹配(端口不算,所以端口不匹配也行,只需要域名和路径匹配即可,除了个别的,即特殊的,具体可以百度)
        //那么是没有该cookie的,自然也就没有发送,查看Cookie时
        //实际上是查看这个域名以及他路径下的Cookie,可以将路径进行修改(当然域名也可以修改,只是通常不会)
        //所以这里以修改路径为主要说明
        //若修改不是/a/b或者其子路径(下一个路径)
        //那么你就会发现,消失了,因为不在这个路径及其以下了
        //这样的设置,是为了让在一定范围内的操作的调查选择显示
        //即当你再次指定这个路径下的查找路径时
        //这时,将这个有你原来选择的信息,发送到对应服务器的Web组件,他进行判断
        //优先选择你选择过的内容显示给你看,这就是Cookie的具体作用
        //因为都是在一个路径下的操作,基本上是工程路径(项目路径)下的操作,因为他是最低的,在后面就是端口了
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        this.doPost(request, response);
    }
}

package com.lagou.demo03.servlet;

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;

@WebServlet(name = "CookieServlet2", urlPatterns = "/cookie2")
public class CookieServlet2 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        // 1.获取客户端发来的Cookie信息并打印出来
        //使用javax.servlet.http.HttpServletRequest接口的成员方法实现Cookie对象的获取
        //Cookie[] getCookies()返回此请求中包含的所有Cookie对象
        Cookie[] cookies = request.getCookies();
        System.out.println("获取到的Cookie信息有:");
        for (Cookie tc : cookies) {
            //使用javax.servlet.http.Cookie类的构造方法实现Cookie对象中属性的获取和修改
            //String getName()返回此Cookie对象中的名字
            // String getValue()返回此Cookie对象的数值
           
            System.out.println(tc.getName() + "对应的值为:" + tc.getValue());
        }

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        this.doPost(request, response);
    }
}

package com.lagou.demo03.servlet;

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;

@WebServlet(name = "CookieServlet3", urlPatterns = "/cookie3")
public class CookieServlet3 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        // 1.获取客户端发来的Cookie信息并打印出来
        Cookie[] cookies = request.getCookies();
        for (Cookie tc : cookies) {
            // 2.当获取到的Cookie对象的名字为name时,将对应的数值修改为guanyu并添加到响应信息中
            if ("name".equalsIgnoreCase(tc.getName())) {
                 // void setValue(String newValue)设置Cookie的数值
                tc.setValue("guanyu");
                response.addCookie(tc);
                break;
            }
        }
        System.out.println("修改Cookie信息成功!");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        this.doPost(request, response);
    }
}

Cookie的生命周期:
默认情况下,浏览器会将Cookie信息保存在内存中,只要浏览器关闭,Cookie信息就会消失
如果希望关闭浏览器后Cookie信息仍有效,可以通过Cookie类的成员方法实现
package com.lagou.demo03.servlet;

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;

@WebServlet(name = "CookieServlet4", urlPatterns = "/cookie4")
public class CookieServlet4 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        // 1.创建Cookie信息
        Cookie cookie = new Cookie("name", "liubei");
        // 2.获取Cookie信息的默认使用期限
        //int getMaxAge()返回cookie的最长使用期限(以秒为单位)
        int maxAge = cookie.getMaxAge();
        System.out.println("该Cookie的默认使用期限是:" + maxAge);
        // 3.修改Cookie信息的使用期限
        // 正数表示在指定的秒数后失效   负数表示浏览器关闭后失效   0表示马上失效
        //cookie.setMaxAge(0);
        //void setMaxAge(int expiry)设置cookie的最长保留时间(秒)
        cookie.setMaxAge(0);
        // 4.添加到响应信息中
        response.addCookie(cookie);
        //添加的cookie里有你设置的期限,这里可以看出,因为默认为-1,所以基本上就是,关闭浏览器后失效
        //而当设置0时传过来的Set-Cookie中有Max-Age=0
        // 当Set-Cookie加载完后,浏览器看到这个Max-Age=0
        // 那么就会立即删除传过来的Cookie数据,即没有多余的Cookie数据
        //虽然响应头里这个数据还在,但那是先加载完的,并没有与删除绑定,所以可以看到,但实际上没有对应数据了
        //且这个显示是大于等于0的显示,负数不显示
        //其中由于浏览器是会检查这个设置的值的,每当你响应一次,就会检查一次
        //即当你重新部署时,就会根据你设置的期限来操作,如0就立即删除传过来的Cookie数据(注意是传过来的)
        //其余不删,且是先修改后再删除
        System.out.println("设置Cookie的生命周期成功!");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        this.doPost(request, response);
    }
}

使用了上述期限,必须要满足对应条件,操作做,如我10*60分钟失效,那么就是你关闭浏览器他也会在那里
这时看Cookie时,后面有个过期时间(具体是那一个,可以百度,当然,可以看到会话二字的那么他就是在过期时间属性的地方),浏览器会检查这个时间,而进行操作,过了这个时间,那么就删除
而负数的就是会话时间,即关闭浏览器删除,而0则看不到,因为立即删除了
但是浏览器主动的清理缓存Cookie的话,还是会删除的(无视过期时间)
Cookie的路径问题:
浏览器在访问服务器时,会比较Cookie的路径与请求路径是否匹配,只有匹配的Cookie才会发送给服务器
Cookie的默认路径等于添加这个Cookie信息时的组件路径
例如:/项目名/目录/add.do请求添加了一个Cookie信息,则该Cookie的路径是 /项目名/目录
访问的请求地址必须符合Cookie的路径或者其子路径时,浏览器才会发送Cookie信息(注意:也包括域名,除非通过某些设置)
package com.lagou.demo03.servlet;

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;

@WebServlet(name = "CookieServlet5", urlPatterns = "/cookie5")
public class CookieServlet5 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        // 1.创建Cookie对象并指定数值
        //Cookie cookie = new Cookie("name", "zhaoyun");
        Cookie cookie = new Cookie("name", "huangzhong");
        // 3.修改Cookie的路径信息
        //void setPath(String uri)设置cookie的路径信息
         //将默认的整体路径的下个路径进行修改
        cookie.setPath(request.getContextPath() + "/hello");
       //request.getContextPath()方法获得当前工程路径

        // 2.添加到响应信息中
        response.addCookie(cookie);
        System.out.println("设置Cookie路径成功!");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        this.doPost(request, response);
    }
}

实际上Cookie会在对应的请求信息和响应信息中,即不管怎么操纵,就是请求和响应之间的操作
Cookie的特点:
Cookie技术不适合存储所有数据,程序员只用于存储少量、非敏感信息,原因如下:
将状态数据保存在浏览器端,不安全
保存数据量有限制,大约4KB左右
只能保存字符串信息
可以通过浏览器设置为禁止使用,因为有些网站必须要Cookie才可进行操作
即必须要有Cookie数据给他的服务器,这是他们页面的设置
这个禁止,可以理解为就是禁止发送Cookie,和接收Cookie
Session技术(重点)-------------------------------
基本概念:
Session本意为"会话"的含义,是用来维护一个客户端和服务器关联的一种技术
浏览器访问服务器时,服务器会为每一个浏览器(因为Cookie是浏览器里的)都在服务器端的内存中分配一个空间
用于创建一个Session对象
该对象有一个id属性且该值唯一,我们称为SessionId,并且服务器会将这个SessionId以Cookie方式发送给浏览器存储
只发送一次,除非造成Session销毁的操作,那时就会再次发送
id就是上面的JSESSIONID(名称)对应的值,即值唯一,除非重新启动服务器,就是销毁原来的Session内存
那么这个值会改变,但名称不会变,不同服务器可能名称不同
或者删除对应的Cookie的值,因为没有发送对应SessionId的Cookie给服务器
那么就会销毁原来的Session内存
那么这个值会改变,但名称不会变,不同服务器可能名称不同
浏览器再次访问服务器时会将SessionId发送给服务器,服务器可以依据SessionId查找相对应的Session对象
package com.lagou.demo03.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet(name = "SessionServlet", urlPatterns = "/session")
public class SessionServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        // 1.调用getSession方法获取或者创建Session对象
        //使用javax.servlet.http.HttpServletRequest接口的成员方法实现Session的获取
        //HttpSession getSession()返回此请求关联的当前Session,若此请求没有则创建一个
        HttpSession session = request.getSession();
        // 2.判断该Session对象是否为新建的对象
        //使用javax.servlet.http.HttpSession接口的成员方法实现判断和获取
        //boolean isNew()判断是否为新创建的Session
        System.out.println(session.isNew()? "新创建的Session对象": "已有的Session对象");
        // 3.获取编号并打印
        //String getId()获取Session的编号
        String id = session.getId();
        System.out.println("获取到的Session编号为:" + id);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        this.doPost(request, response);
    }
}

package com.lagou.demo03.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet(name = "SessionServlet2", urlPatterns = "/session2")
public class SessionServlet2 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        HttpSession session = request.getSession();
        //使用javax.servlet.http.HttpSession接口的成员方法实现属性的管理
        // 1.设置属性名和属性值
        //void setAttribute(String name, Object value)使用指定的名称将对象绑定到此会话
        session.setAttribute("name", "machao");
        // 2.获取指定属性名对应的属性值
        //Object getAttribute(String name)
        //返回在此会话中用指定名称绑定的对象,如果没有对象在该名称下绑定,则返回空值
        System.out.println("获取到的属性值为:" + session.getAttribute("name")); // machao
        // 3.删除指定的属性名
        //void removeAttribute(String name)从此会话中删除与指定名称绑定的对象
        session.removeAttribute("name");
        // 4.获取指定属性名对应的属性值
        System.out.println("获取到的属性值为:" + session.getAttribute("name")); // null
        
        //上面的Session的方法虽然与HttpServletRequest的方法类似
        //但Session是服务器的,即全局的,其他Servlet也可使用
        //而HttpServletRequest需要转发来使得其他Servlet可以使用
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        this.doPost(request, response);
    }
}

Session的生命周期:
为了节省服务器内存空间资源,服务器会将空闲时间过长的Session对象自动清除掉,服务器默认的超时限制一般是30分钟
package com.lagou.demo03.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet(name = "SessionServlet3", urlPatterns = "/session3")
public class SessionServlet3 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        // 1.获取Session对象
        HttpSession session = request.getSession();
        // 2.获取对象的默认失效时间并打印
        //使用javax.servlet.http.HttpSession接口的成员方法实现失效实现的获取和设置
        //int getMaxInactiveInterval()获取失效时间
        int maxInactiveInterval = session.getMaxInactiveInterval();

        System.out.println("获取到的失效时间为:" + maxInactiveInterval); // 1800
        //默认1800秒,即30*60,30分钟
        // 3.修改实现时间后重新获取并打印
        //void setMaxInactiveInterval(int interval)设置失效时间
        session.setMaxInactiveInterval(1200);
        maxInactiveInterval = session.getMaxInactiveInterval();
        System.out.println("获取到的失效时间为:" + maxInactiveInterval); // 1200
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws 
        ServletException, IOException {
        this.doPost(request, response);
    }
}

可以配置web.xml文件修改失效时间:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <session-config>
        <session-timeout>10</session-timeout>
        //会将默认改为600秒,即10*60,10分钟
    </session-config>
    <!--
    对应的Session通过读取xml文件来得到对应时间,即读取对应元素来查找值,没找到
    自然返回null,即不读取(会判断是否是null)
    在这里不能写两个一样的,因为底层读取时只能读一个,若读取两个或者以上,则会报错,使得服务器启动不了

web程序中,在启动前,一般都会读取xml来进行操作的

    -->

</web-app>
Session的特点:
数据比较安全
能够保存的数据类型丰富,而Cookie只能保存字符串
能够保存更多的数据,而Cookie大约保存4KB
数据保存在服务器端会占用服务器的内存空间,如果存储信息过多、用户量过大,会严重影响服务器的性能
Cookie与Session,一个是通行证,一个是记录者,前者数据走动,但不存,后者不走动,但存
最后要说明的是:
我们知道一般情况下,你可能认为请求后,回退是回退,而不会再次的请求,但是这里就要颠覆了,实际上post再回退时,会重新操作当前的访问(回退的),注意:是资源的访问,也就是说,get是操作缓存的,而post不是,与页面无关哦,是资源哦,这是缓存的问题,具体可以百度,比如:https://ask.csdn.net/questions/7518088,对应的get和post的区别:https://mp.weixin.qq.com/s?__biz=MzI3NzIzMzg3Mw==&mid=100000054&idx=1&sn=71f6c214f3833d9ca20b9f7dcd9d33e4#rd
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值