💕"Echo"💕
作者:Mylvzi
文章主要内容:网站开发–详解Servlet
一.基本介绍
tomcat是Java中开发服务器的重要的一个工具,任何开发的服务器都要部署在tomcat之上,可以说tomcat是所有服务器的底座
,为了更好的操作http,tomcat对原生的api进行了封装,封装为Servlet*,通过servlet就可以非常方便的完成和http协议的祥光操作
关于Servlet的学习,主要学习以下三个类即可:
- HttpServlet
- HttpRequestServlet
- HttpResponseServlet
掌握好这三个类,就能很好的使用Servlet
二.HttpServlet
HttpServlet是Servlet中最核心的一个类,我们创建的用于进行交互的类都必须要继承于HttpServlet
HttpServlet中常用方法
- init()方法会在一个Servlet对象被创建好之后,自动的调用这个方法,完成初始化操作
- destroy()方法会在Servlet对象被销毁之前进行调用,完成一些释放资源的工作
- service()方法实际上并不会直接使用到,而是被doGet(),doPost()给替代
init(),destroy(),service()这三个方法在实际的开发中很少用到,都是tomcat自动的在合适的时机帮助我们调用的,这三个方法经常会出现到一个经典的面试题中:
经典面试题:Servlet的生命周期
Servlet的生命周期是指Servlet实例被创建出来到被销毁的过程,具体来说可以分为以下几点:
- Servlet实例的创建:当客户端发送请求,服务器就要创建出一个Servlet实例来处理请求,通过构造方法创建出一个Servlet实例
- 初始化:在实例被创建好之后,会自动调用init()方法,来完成一些初始化操作,比如:读取配置文件,数据库的连接,一些资源的初始化
- 处理请求:当有客户端的http请求发送到服务器时,Servlet会先调用service()方法,判断请求的具体类型(根据请求的方法),service()方法就像是火车的中转站一样,看你的目的是什么,再给你对应的路线.这里也是一样,如果请求是GET方法,就会交给代码中的doGet()方法处理,如果是POST请求,就会交给doPost()方法
- 实例的销毁:当我们不再使用Servlet实例时(客户端不再有请求发送过来/人为关闭),先调用destroy()方法,做一些清理工作,比如数据库的断开,资源释放等
这个过程类似于生命周期中的起始、成长、运行和结束阶段,每个阶段都有其特定的任务和目的。
总的来说,我们所写的代码都要继承于HttpServlet这个类,重写里面的方法,并将重写的方法插入到tomcat已有的框架
之中,我们只需关注数据处理部分,其余操作都是tomcat自动帮助我们完成
二.HttpServletRequest
1.介绍
HttpServletRequest表示一个Http请求类,用于接受从客户端传输过来的http请求
HttpServletRequest中的很多属性和请求头的相关属性相同,理解好请求头中的相关属性有助于学习HttpServletRequest类
一个Http请求由四部分组成:
- 首行 = 方法 + URL + 版本号
- 请求头:由键值对组成
- 空行
- body
2.HttpServletRequest常用方法介绍
1.获取首行信息和header中的数据**
@WebServlet("/request1")
public class RequestParameter extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 尝试获取请求报文中的详细信息StringBuffer stringBuffer = new StringBuffer();// 这个是线程安全的// 1.获取首行信息stringBuffer.append(req.getMethod());stringBuffer.append("<br>");// <br>是html中的换行标签stringBuffer.append(req.getRequestURI());stringBuffer.append("<br>");stringBuffer.append(req.getProtocol());stringBuffer.append("<br>");// 2.获取请求头信息Enumeration<String> enumeration = req.getHeaderNames();while (enumeration.hasMoreElements()){// 利用迭代器访问每一个头部keyString key = enumeration.nextElement();String val = req.getHeader(key);// 获取key的值stringBuffer.append(key + ":" + val + "<br>");}resp.getWriter().write(stringBuffer.toString());}
}
获取结果:
总结:
对于请求报文来说,获取首行中的信息(版本号/方法/URL),可以直接通过指定的get
方法来获取,获取请求头(header)中内容,可以利用迭代器
,将请求头中的所有键值对都存储到一个集合之中,再利用getHeader方法,指定参数,来获取对应的value值
2.获取请求报文中的query string**
query string,译为查询字符串,往往和用户的关键信息相关(登录密码),在日常的开发中,我们也经常利用到query string来完成一些业务上的逻辑,所以获取请求报文中的query string是一个很频繁且重要的操作
query string在请求报文中的位置是不固定的,具体位置取决于请求报文中的方法(method),如果方法是Get,query string就位于URL之中,如果方法是Post,query string就位于body之中
注意:实际上URL中是包含query string的,但是在servlet的getURL类似的方法中返回的是一个不携带query string的URL(可能是因为查询字符串这个操作过于频繁,就单独拿出来进行包装了)
1.方法为Get
说明:前后端交互的一个关键点在于前端和后端要对传输内容的格式都了然于胸,后端要知道前端是如何组织数据进行传输的
比如,规定好客户端发送的请求报文中的query string的格式是类似于:
username=zhangsan&password=123456
那么服务器这边就可以规定好的格式进行解析
代码演示:
@WebServlet("/request2")
public class GetRequestParameter extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 获取用户传输过来的query string// 假设用户传输过来的query string的格式是形如 username='lisi'&password='123'这样的形式// servlet会自动将query string中的所有内容存储到一个Map结构之中(键值对都存储到其中// 后端程序员只需通过getParameter()方法来获取具体的值即可// 注意你构造的query string内部不需要有引号!!!// 可以根据这个特性来完成很多操作 比如判断用户名是否正确// 在传输中文的时候最好传输urlencode之后的内容String username = req.getParameter("username");String password = req.getParameter("password");System.out.println("username=" + username);System.out.println("password=" + password);}
}
当在浏览器中输入:“http://127.0.0.1:8080/testsevlet/request2?username=zh&password=123”
在终端上显示:
如果不添加query string,显示的结果就是null
总结:
对于Get方法的请求,query string位于首行中,直接使用getParameter方法,并制定参数,就能获取到对应的值
2.方法为Post
此时query string位于body之中,而body的传输是有不同的格式的,对于不同格式的body,要采用不同的方法来进行解析,获取query string,常用的body格式有两种:
- form表单
- json
1.form表单格式
body通过form表单来进行数据的组织,内部是键值对,Content-Type:application/x-www-form-urlencoded
获取body中的内容和上面获取query string的方法一致
通过postman发送一个post请求
服务器的代码不变,打印结果为:
总结:如果body的格式是form表单,虽然此时query string位于body之中,但是也可以直接使用**getParameter()**方法,并指定参数来获取要查询的内容
2.json格式
如果body是通过json格式来进行组织,原生的Servlet是不支持解析json格式的数据的,此时需要进入第三方库jackson
来帮助进行json格式数据解析(jackson也是spring官方推荐的用于解析JSON格式数据的第三方库)
准备:引入jackson依赖
在maven中找到jackson,选择合适的版本进行路径的复制,并在项目中的pom.xml中引入
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind --><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.15.0</version></dependency>
引入好依赖之后就可以使用jackson中的api解析json格式的数据
具体的操作依赖于一个类和两个核心方法:
ObjectMapper(对象映射器),是JSON数据与Java对象转换的核心枢纽,在交互时,通过JSON字符串进行网络传输,处理逻辑,使用Java对象
站在服务器的角度,我们收到的数据是JSON字符串,需要将其转化为Java对象(read放哪发),来完成一些业务上的逻辑,完成后,还需要将处理好的Java对象转化为JSON字符串(write方法)
1.创建一个类 用于接收JSON字符串
// 创建一个类 用于接收JSON字符串
class Request {public String username;public String password;
}
2.交互
// 创建出ObjectMapper实例private ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 1.将JSON字符串转化为Java对象Request request = objectMapper.readValue(req.getInputStream(),Request.class);// 2.获取query stringString username = request.username;String password = request.password;// 3.打印String user = "username=" + username + "<br>password=" + password;System.out.println(user);// 4.将打印的结果传输给客户端// 这个方法就是和上面的read方法相反// 这里把Java对象转化为json字符串,传输给客户端String respBody = objectMapper.writeValueAsString(user);resp.getWriter().write(respBody);}
3.发送请求并打印结果
objectMapper.readValue()内部做了很多工作,最终的结果是把JSON字符串转化为Request的Java对象,内部具体实现细节为:
- 根据请求报文的输入流(就是方法的第一个参数)获取到请求报文中的body部分的所有内容
- 按照JSON格式进行解析,并把解析出来的所有的键值对都存储到一个Map之中
- 通过第二个参数(反射)来明确要转化的Java对象,拿着Java对象中的属性从Map之中寻找对应的值,并把对应的值赋给赋给创建出来的Java对象
通过以上三步就完成了JSON字符串到Java对象的转化,举一个简单的例子
以上就是关于HttpRequestServlet的所有内容,可以观察到,这个类中大部分方法都是get方法,主要原因是因为我们是想通过这个类获取到客户端发送的请求报文,而不是去修改请求报文,所以不需要设置set方法,而在下面要介绍的HttpResponseServlet类中,更多的方法就是set方法(服务器这边要返回一个响应报文)
三.HttpResponseServlet
常用方法:
setStatus() 设置状态码
@WebServlet("/status")
public class StatusServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// resp.setStatus(404);// 此时响应报文的状态码就被设置为404// 如果只传输上述的状态码 浏览器是空白界面 不会有任何信息// 我们一般看到的都是有一个"错误界面"// 可以通过resp.sendError(404,"你小子搜的是什么资源???");// 这里面就比较的灵活了// 根据一定的逻辑 来灵活的输出状态码信息}
}
当需要发送报错信息时,更推荐使用第二种方式,更加灵活
结果显示:
setHeader 在头部中设置一些属性
自动刷新
每间隔1s就自动刷新界面,利用header中的refresh字段
@WebServlet("/refresh")
public class RefreshServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setHeader("refresh","1");// 1s 后自动刷新界面resp.getWriter().write("" + System.currentTimeMillis());}
}
sendRedirect() 构造重定向
当状态码是3xx时,就代表要进行重定向,利用sendRedirect方法可以直接跳转
@WebServlet("/redirect")
public class RedirectServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// // 一个重定向的报文包含两部分:状态码 和 要跳转的界面
// resp.setStatus(302);// 核心是要有对应的状态码(以3开头),以及要跳转的界面(Location字段)
// resp.setHeader("Location","https://sogou.com");// 此时就会自动跳转到搜狗界面// 上述写法比较繁琐 一般来说会使用下面这个方法来设置一个跳转的响应报文resp.sendRedirect("https://sogou.com");}
}
不仅可以通过服务器这边设置跳转的界面,还可以在前端中使用JS定时器来实现相同的效果
<!DOCTYPE html>
<html>
<head><title>页面跳转示例</title><script>// 在页面加载后,等待 3 秒后跳转到新页面window.onload = function() {setTimeout(function() {window.location.href = "https://www.sogou.com";}, 3000); // 3 秒};</script>
</head>
<body><h1>页面将在 3 秒后跳转到搜狗主页</h1>
</body>
</html>
以上就是<<网站开发–详解Servlet>>的所有内容,重点掌握三个类的api的基本用法,多多使用就能融会贯通,实现更复杂的交互!