努力经营当下,直至未来明朗!
坚持一定很酷!
【Servlet API概述】
- API其实就是一组类/方法
- Servlet提供的类和方法很多,但是最主要就使用三个:
① HttpServlet: (继承)
② HttpServletRequest:(方法参数)
③ HttpServletResponse:(方法参数)
(HttpServletRequest、HttpServletResponse中的方法都是和http报文格式有关的)
【HttpServlet】
- 核心方法
1)init:创建出HttpServlet实例会调用一次,其作用其实就是用来初始化。一般就是首次访问的时候会被实例化。
2)destroy:不一定能够调用到。因为HttpServlet不再调用其实就是在tomcat关闭时,关闭tomcat有两种方法:
① 直接杀进程:如点击idea中的红色方框、cmd直接点击x、通过任务管理器结束进程等,此时destroy无法被调用。
② 8005端口是用来控制tomcat的,通过该端口给tomcat发送一个关闭操作,此时tomcat就可以正常关闭,也就能够调用destroy了。
(工作中最常用的其实就是直接杀进程方式关闭,所以不太会调用到destroy方法)
3) service:tomcat收到请求之后实际上是先调用service方法,在service中根据方法来调用不同的doXXX方法。(实际开发中其实很少会重写service,重写doXXX方法就够了)
- 【面试题】谈谈Servlet的生命周期?
生命周期:其实就是正确的时间干正确的事儿。
也就是回答:Servlet中有三个主要的方法:init、destroy、service,以及何时被调用:init在servlet被实例化的时候调用一次,destroy是在servlet销毁之前调用一次,service是每次收到请求调用(两次)。
(调用两次service原因:两次service)
【HttpServletRequest】
-
核心方法:
-
写一个测试代码:
(枚举类型是不可以通过下标访问和修改的) -
如果不写content type,此时浏览器就不知道body是啥格式,就会去猜;但是一般不要让浏览器去猜,而是直接setContentType。
body格式如:
tect/plain(纯文本)、text/html、 text/css、 application/js、 application/json、 image/png 等
- 还有一个重要场景就是:使用api获取到请求中的重要参数(query string中的值 以及 body中的值)
对于query string来说,核心方法就是 getqueryString
1)实例:
(在服务器中打印)
(在浏览器中打印)
此时:浏览器没有正确识别出中文,当前不是没有urlencode的缘故(因为服务器这里的打印结果是正确的);但是返回的响应页面浏览器没能正确识别,为了让浏览器能够识别,需要显式在响应上加上content type。
注意:即使在地址栏输入的是encode的内容,但是servlet getParameter会自动的针对urlencode的结果进行decode,不需要我们手动处理
- getParameter获取键值对的时候,如果是键key不存在,得到的就是null;如果是键存在、值不存在,得到的就是” ”;如果key、value都不存在,得到的也是null。
2)POST实例:
如果请求是post,如何获取到body中的参数?
A. 请求的body是x-www-form-urlencode 的 form表单形式:
这里也是使用getParameter来获取的。
但是post请求不是直接在地址栏输入就可以的,需要构造请求:
① 这里可以使用第三方工具postman来构造请求:构造完成后直接点send就可以看到结果
(postman也是http客户端,其地位和浏览器是对等的)
② 还可以搞个html页面,通过form表单发送请求。
(注:html创建是在webapp下,不是WEB-INF下!! new file)
(如果在idea中不好写代码,就切到vscode中写代码)
(一定要注意这里的路径:不是webapp,而是servl,之前在idea tomcat上设定的context path!!)
在提交之后可以在fiddler中看到body信息,也就是post请求构造成功。
(body中被form表单自动urlencode了)
(post请求路径)
(form表单类型)
虽然已经显式地告诉了浏览器响应的编码方式,但是响应页面依旧是乱码:
查看发现服务器中也是乱码:
说明:服务器在针对请求进行解析的时候就已经乱码了。所以:需要显式地告诉servlet应该按照哪种编码来理解请求的body。
所以:给请求也设置编码方式:
- 【请求处设置的utf8是告诉servlet(tomcat)如何解析;响应处设置的utf8是告诉浏览器如何解析】
响应这里设置字符集有两种写法:setContentType 以及 setCharacterEncoding,但是还是建议使用setContentType完整写法;
设置的字符集只是其中一部分,还需要设置格式。
【补充】
“爬虫”:其实就是一个HTTP客户端,是自己写的。能在浏览器中做的一切操作,理论上都是可以用爬虫来实现的。
(90%的爬虫都是违法的。服务器承受的请求压力是有限的,爬虫其实属于无效访问,还白白增大了服务器的压力,容易把别人的服务器搞挂了;另外,可能会涉及别人的隐私信息。(很多网站都是有反爬策略))
【注意】
顺序不能颠倒。务必要保证先设置所有的header,最后再设置body(servlet的坑)。
即:先格式 后写入!!
B. 请求的body是json:(使用 {键值对,} 构成的)
① 需要先读取body中的内容,使用getInputStream来读取流对象,然后再进一步来读取流对象。
② 如何解析json格式?
Servlet没有内置json解析,所以我们就使用第三方库。市面上很多第三方库如fastjson、jackson、gson等,但是我们主要使用的是jackson,因为jackson是Spring御用的json库。
③ 使用中央仓库maven(中央仓库)下载安装jackson:选择databind -> 随便选择一个版本(此时我选2.13.4.1)-> 复制如下图maven中内容 -> 拷贝到pom.xml中(依旧是dependecies中)-> 如果标红,就进行刷新操作
④ 其实使用jackson非常方便,掌握一个类两个方法就行:
ObjectMapper类:
① readValue方法:把JSON格式的数据转为java的对象
②writeValueAsString方法:把java对象转成jjson格式的字符串
【readValue方法:第一个参数可以是字符串,也可以是输入流; 第二个参数是获取类对象(类似于反射),也就是要解析出来的结果的对象的类】
【重点理解】
① 读取输入流,获取到要解析的字符串
② 把字符串按照json格式进行解析,得到一组键值对(Map)
③ 根据类对象创建一个实例
④ 遍历类对象中属性的名字,拿着名字去与上述Map中查询匹配,将匹配到的value赋值到对应对象的属性中去
⑤ 返回这个构造完成的对象。
(要求:json键值对中 键 的名字/类型要和类的 属性 一一对应)
【注】
① 要实现java对象就要有java类,而类中的属性务必是public或者是带有public的getter/setter ,否则json无法访问该对象的属性。
② 另外,该类必须有无参版本的构造方法,如果不写任何构造方法,编译器能够自动生成无参的构造方法。(但是一旦写了有参的构造方法,就一定要自己手动写无参的构造方法)
在postman中构造post请求时,json格式设置方法如下:
在postman中写json格式的时候,务必要保证这里的key是带引号的!正常的json中key都是要带引号的! (但是js里的对象是不带的)
具体参考代码:JsonServlet
【HttpServletResponse】
- 核心方法:
注:HttpServletRequest方法一般是get系列,而HttpServletResponse方法一般是set系列:因为doGet/doPost等这样的方法中的HttpServletResponse对象就是空对象,需要往里面写入数据
- 举个例子:
1) 设置不同的状态码:
(代码:StatusServlet)
这些状态码具体怎么处理(前端页面显示啥)都是由程序员自定义的。
2) 设置响应的header:
通过这个实现页面的自动刷新:header设置refresh属性,值是一个秒数。
在进行刷新时,刷新时间总是会比设定的时间稍多一些,理由:
① 进程调度消耗时间
② 网络传输消耗时间
③ 服务器响应消耗时间
(代码:AutoRefreshServlet)
3)构造重定向响应(3开头系列):
(代码:RedirectServlet 没有referer的)
写例子:表白墙
- 之前写的表白墙,其内容只是在内存中存在(List保存),一旦刷新之后内容就丢失。
- 此时需要让用户留言的数据能够在服务器这儿保存,保证页面即使关闭之后数据也不会丢失!此时就可以保证页面加载数据时是从服务器这儿加载数据的,之前留言的内容不会丢失。
1)准备工作:创建maven项目MessageWall,引入依赖,创建目录结构
2)设计前后端如何交互:前端啥时候给后端发请求,发的请求是啥样的,返回的响应是啥样的。
① 在表白墙中,希望点击提交之后数据能够在服务器中保存(存档);
② 当关闭页面再重新启动/刷新之后,需要从服务器获取到之前保存过的数据并在页面上进行显示(读档)
【客户端和服务器的交互是根据业务需求的】
3)理清请求和响应的细节。
① 发送数据给服务器:
接口约定的方式是无穷的,必须要确定一个唯一方案,这样前后端才可以配合开发。
②从服务器获取到已经保存的数据:
4)开发后端和前端代码
① 前端代码是在webapp目录底下! 然后使用vscode打开并操作
② 注意:将资源都放到webapp下,包括写Servlet,其实编译之后的.class文件也是在webapp底下的;webapp就描述了当前网站的Context Path。
(发送请求:前端代码,前面还加了一个script 引入jquery)
③ 页面进行加载的时候就需要进行“读档”操作:直接写在script标签中的代码都是在页面加载的时候执行的
④ 注:smart tomcat 会把一部分内存中的数据保存下来,后续重启服务器的时候数据就会恢复回来。
【注意:请求、响应过程!】
⑤ web程序一旦出现问题,起手式就是“抓包”,通过抓包确定是前端问题还是后端问题。
5)需要让数据在数据库中持久化保存:jdbc编程
① 在中央仓库搜索mysql:
② 选择5.1.49版本,复制代码并粘贴到pom.xml的dependencies中
③ 设计数据库表结构:几张表,表里有哪些列
本表白墙中只需要一张表message,列:from、to、message(id可以没有)
在cmd中操作:首先 目录要cd到mysql.exe所在的目录,然后输入mysql -u root -p
进入数据库(也可以直接使用mysql client)
此时建表成功(注意关键字要加上``)
④ 此时重新修改后端代码,直接保存在数据库中。
(记忆数据库操作代码)
6)DataSource存在多份,这不太合理,所以使用单例模式对DataSource进行封装。
① 单例模式:只创建一个实例!
② 一般由两种模式:懒汉模式和饿汉模式,一般更倾向于使用懒汉模式。
懒汉模式涉及到一个线程安全问题。
③ 所以考虑:Servlet代码中是否涉及到多线程/线程安全问题?
① Servlet写的是一个服务器,同一时刻可能要处理多个客户端的请求。
② 一旦同时有多个客户端都发来请求,服务器就势必要同时处理多个请求。Tomcat内部正是使用了多线程的方式来处理的。
③ 则:Servlet代码中涉及到多线程/线程安全问题。
表白墙参考代码
参考代码:表白墙
小结
-
HttpServlet
① doXXX:处理哪种http请求会调用的对应的方法
② init/destroy/service:Servlet的生命周期 -
HttpServletRequest:HTTP请求,get系列方法
可以获取到以下信息:
协议名(版本号)、URL、query string、header、query string/body的键值对等 -
HttpServletResponse:HTTP响应,set系列方法
可以设置以下信息:
状态码、各种header、body等 -
表白墙代码:主要注意 mysql +单例模式(线程安全)