最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

【JavaEE】

运维笔记admin15浏览0评论

【JavaEE】

【JavaEE】

目录

1. HttpServlet

1.1 init方法

1.2 destroy方法

1.3 service方法

1.4 Servlet的生命周期

1.5 代码示例

1.5.1 使用postman构造请求

1.5.2 使用ajax构造请求

2. HttpServletRequest

2.1 核心方法

2.2 代码示例1:打印请求信息

3. 前端给后端传参

3.1 通过GET的query string

3.2 通过POST,借助form表单

3.3 通过POST,使用json格式构造body

4. HttpServletResponse


[便捷起见,非必要时Servlet程序均以smart tomcat方式部署程序]

Servlet有3个重要类,分别为HttpServlet,HttpServletRequest,HttpServletResponse;

1. HttpServlet

编写Servlet程序第一步就是创建一个类继承自HttpServlet类并重写其doGet方法;

其核心方法有:

方法名称调用时机
init在HttpServlet实例化之后被调用一次
destroy在HttpServlet实例不再使用的时候调用一次
service收到HTTP请求的时候调用
doGet收到GET请求时调用(由service方法调用)
doPost收到POST请求时调用(由service方法调用)
doPut/doDelete/do Options/...收到其他请求时调用(由service方法调用)

1.1 init方法

1. 不由程序员手动调用,由tomcat自动调用

2. 当tomcat首次收到了和该类相关联的请求时,才会进行实例化

    以上一篇用Servlet的hello world程序为例:

WebServlet注解就是将/hello类和HelloServlet类绑定在一起,表示:

如果Tomcat收到了/hello这样路径的请求,就会调用HelloServlet,于是就对HelloServlet进行实例化;

实例化只进行一次,后续再收到/hello,就不必再重复实例化了,直接复用之前的HelloServlet实例即可;

3. 可以重写init方法,插入一些我们自己的初始化的逻辑:

运行以下代码:

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {@Overridepublic void init() throws ServletException {// 重写init方法System.out.println("init");}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        把数据显示在服务器控制台System.out.println("Hello world");
//        把数据写回浏览器resp.getWriter().write("Hello world");}
}

(1)运行程序,仅启动服务器时,在控制台并未打印init:

原因:只有当请求关联到hello路径时才会打印;

(2)在浏览器中打开页面,即触发了请求,此时服务器的日志上就会调用init进行实例化:

(3)多次刷新页面,即多次发送请求:

doGet方法多次被调用,而init方法仅被调用一次;

4. 也可以通过修改web.xml配置让tomcat启动时立即实例化该servlet;

5. servlet是服务器上运行的代码,只要服务器不重新启动,init就不会再执行

1.2 destroy方法

1. 可以使用destroy方法进行一些清理工作

2. 只要服务器在运行,都有可能再使用,服务器终止时就不可再使用了;

3. 重写destroy方法:

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {@Overridepublic void init() throws ServletException {// 重写init方法System.out.println("init");}@Overridepublic void destroy() {
//        重写destroy方法System.out.println("destroy");}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        把数据显示在服务器控制台System.out.println("Hello world");
//        把数据写回浏览器resp.getWriter().write("Hello world");}
}

启动服务器并刷新页面后,点击停止后,可见服务器日志调用了destroy方法:

注:但此处的destroy方法是否能被执行到并不确定:

第一种情况:如果是使用smart tomcat的停止按钮终止程序,这个操作本质上是通过tomcat的8005端口来主动停止,可以触发destroy方法;

第二种情况:如果是直接杀进程,此时可能就来不及执行destroy方法;

故而并不推荐使用destroy方法;

1.3 service方法

当收到路径匹配的HTTP请求就会触发service方法

比如doGet方法就是在service方法中调用的:

父类HttpServlet有一个service方法,其内部就会调用doGet方法;

1.4 Servlet的生命周期

开始时,执行 init;

每次收到请求,执行 service;

销毁前执行 destroy;

注:一个servlet程序包含很多个servlet,某个servlet的生死,不影响整个sevlet程序;

1.5 代码示例

在java目录下再创建一个类:MethodServlet;

其内容如下:

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("/method")
public class MethodServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("doGet");resp.getWriter().write("doGet");}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("doPost");resp.getWriter().write("doPost");}@Overrideprotected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("doPut");resp.getWriter().write("doPut");}@Overrideprotected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("doDelete");resp.getWriter().write("doDelete");}
}

直接在浏览器输入地址访问,只能发送GET请求,其他请求类型可以通过ajax或postman进行构造。

1.5.1 使用postman构造请求

基于一次浏览器地址访问后(即已构造一个GET请求),使用postman依次构造:GET,POST,PUT,DELETE请求并发送,再查看服务器日志:

1.5.2 使用ajax构造请求

1. 在IDEA中创建.html文件:

tomcat要求:.html文件设置在webapp目录下,与WEB-INF同级

可以使用vscode进行编写:

用vscode打开即可;

2. 编写代码:

<!doctype html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head>
<body><script src=".6.4/jquery.min.js"></script><script>$.ajax({type:'get',url:'method',// 此处url为相对路径,此时基准路径为当前html所在的路径:127.0.0.1:8080/hello_servletsuccess:function(body, status){console.log(body);}})</script>
</body>
</html>

在浏览器输入地址:

按f12进入chrome的console标签页:

同样在服务器日志也可见:

3. 依次修改请求类型为post,put,delete,再运行,即可得到不同的结果:

注:使用ajax构造HTTP请求的注意点:

(1)html文件的位置:webapp下,与WEB-INF同级;

(2)在html文件中使用ajax构造HTTP请求时的url可以写为相对路径,该相对路径的基准路径就是当前html文件的路径,在此例中为:127.0.0.1:8080/hello_servlet;

也可以写为绝对路径:(浏览器要求:以 / 开头的为绝对路径

        $.ajax({type:'get',url:'/hello_servlet/method',success:function(body, status){console.log(body);}})

(3)注意@WebServlet注解的路径必须 / 开头,此处的含义不是绝对路径,而是servlet的要求;

2. HttpServletRequest

HttpServletRequest表示一个HTTP请求,这个对象是tomcat自动构造的,

tomcat其实会自动监听端口,接受连接,读取请求,解析请求,构造请求对象等一系列工作;

2.1 核心方法

方法描述
String getProtocal()返回请求协议的名称和版本

String getMethod()

返回请求的HTTP方法的名称,如GET、POST或PUT
String getQequestURI()从协议名称直到HTTP请求的第一行的查询字符串中,返回该请求的URL的一部分
String getContexPath()返回指示请求上下文的请求URI部分
String getQueryString()返回包含在路径后的请求URL中的查询字符串(?后的参数)
Enumeration getParameterNames()返回一个String对象的枚举,包含在该请求中包含的参数的名称
String getParameter(String name)以字符串形式返回请求参数的值,或者如果参数不存在则返回null
String[] getParameterValues(String name)返回一个字符串对象的数组,包含所有给定的请求参数的值,如果参数不存在则返回null
Enumeration getHeaderNames()返回一个枚举,包含在该请求中包含的所有头名
String getHeader(String name)以字符串形式返回指定的请求头的值
String getCharacter Encoding()返回请求主题中使用的字符编码的名称
String getContentType()返回请求主题的MIME类型,如果不知道类型则返回null
int getContentLength()以字节为单位返回请求主体的长度,并提供输入流,或者如果长度未知则返回-1
InputStream getInputStream()用于读取请求的body内容,返回一个InputStream对象

 注:(1)query string是键值对结构,此处可以通过getParameter方法来根据key获取到value

(2)InputStream就是输入流对象,进行read操作即可把body数据读取出来;

2.2 代码示例1:打印请求信息

基于hello_servlet项目,创建一个ShowRequestServlet.java文件,其内容如下:

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.util.Enumeration;@WebServlet("/showRequest")
public class ShowRequestServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 设置响应的content-type,即表明body里的数据格式是何种类型resp.setContentType("text/html");// 创建一个StringBuilder,把这些api的结果拼起来统一写回响应中StringBuilder stringBuilder = new StringBuilder();stringBuilder.append(req.getProtocol());stringBuilder.append("<br>");stringBuilder.append(req.getMethod());stringBuilder.append("<br>");stringBuilder.append(req.getRequestURI());stringBuilder.append("<br>");stringBuilder.append(req.getContextPath());stringBuilder.append("<br>");stringBuilder.append(req.getQueryString());stringBuilder.append("<br>");stringBuilder.append("<br>");stringBuilder.append("<br>");// 获取到header中的所有键值对Enumeration<String> headerNames = req.getHeaderNames();while(headerNames.hasMoreElements()){String headerName = headerNames.nextElement();stringBuilder.append(headerName+" : "+req.getHeader(headerName));stringBuilder.append("<br>");}resp.getWriter().write(stringBuilder.toString());}
}

运行后根据url打开页面:

注:(1)此处实现html页面的换行操作不能使用\n,而使用<br>标签,同时设置响应的content-type为text/html,告诉浏览器,响应的body里的数据格式是何种类型;

(2)按照没有添加query string时,默认响应为null,比如增加query string 为a=10&b=20,响应的query string便为a=10&b=20;

3. 前端给后端传参

3.1 通过GET的query string

 (1)前端:test.html:

<!doctype html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head>
<body><form action="postParameter" method="post"><input type="text" name="studentId"><input type="text" name="classId"><input type="submit" value="提交"></form><script src=".6.4/jquery.min.js"></script><script></script>
</body>
</html>

(2)后端:GetParameterServlet.java:

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("/getParameter")
public class GetParameterServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 预期浏览器会发一个形如 /getParameter?studentId=001&classId=2001;// 借助req的getParameter方法就能拿到query string 中的键值对内容了// getParameter得到的是一个String类型的值String studentId = req.getParameter("studentId");String classId = req.getParameter("classId");resp.setContentType("text/html");resp.getWriter().write("studentId = "+studentId +" classId = "+classId);}
}

 运行程序后,根据url打开浏览器页面如下:

注:(1)输入在url后的查询字符串键值对会被Tomcat处理成形如Map这样的结构,后续就可以随时通过key来获取到value了;

(2)如果key在query string中不存在,则返回值为空;

3.2 通过POST,借助form表单

form表单也是键值对形式组织数据的,只是这部分内容在body中;

对于前端是form表单这样格式的数据,后端还是使用paraMeter来获取;

(1)前端:test.html:

<!doctype html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head>
<body><form action="postParameter" method="post"><input type="text" name="studentId"><input type="text" name="classId"><input type="submit" value="提交"></form><script src=".6.4/jquery.min.js"></script><script></script>
</body>
</html>

 (2)后端:PostParameterSrvlet.java:

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("/postParameter")
public class PostParameterServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String studentId = req.getParameter("studentId");String classId = req.getParameter("classId");resp.setContentType("text/html");resp.getWriter().write("studentId = "+studentId+" classId = "+classId);}
}

运行程序后根据url打开浏览器页面:

提交后,页面如下:

 注:(1)使用query string和使用form表单的后端代码格式基本相同,都是通过getParameter根据key获取value,只是使用form表单构造的键值对不在query string中,而是在body中;

(2)getParameter方法不仅能获取到请求中query string中的键值对,还可以获取到form表单构造的body中的键值对;

(3)以form表单形式构造并发送的请求一定会触发页面跳转;

(4).html及请求、浏览器页面的对应关系图:

(5)前端后端交互的过程:

图1:

图2:

3.3 通过POST,使用json格式构造body

json也是键值对结构的数据格式,可以把body按照该形式组织;

在前端,可以通过ajax方式构造该内容,也可以使用postman;

(1)postman构造请求:

 (2)后端:PostParameter2Sevlet.java

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.InputStream;@WebServlet("/postParameter2")
public class PostParameter2Servlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 通过这个方法处理 body 为 json 格式的数据// 直接把 req 对象中的 body 完整地读取出来// 获取请求的body长度并构造数组int length = req.getContentLength();byte[] buffer = new byte[length];InputStream inputStream = req.getInputStream();inputStream.read(buffer);//把这个字节数组构造成 String, 打印出来String body = new String(buffer, 0, length,"utf8");System.out.println("body = "+body);resp.setContentType("text/html");resp.getWriter().write(body);}
}

使用fiddler可以查看到构造的json请求:

当请求到达tomcat后,tomcat就会将其解析为req对象;

在servlet代码中,req.getInputStrea,,读取body内容,又把body的内容构造成一个响应结果返回给浏览器(此例中为postman),在post页面中就有响应结果:

注:(1)使用form表单构造请求传参与json格式请求传参的代码执行流程是类似的,只是传输数据的格式不同,

form表单的格式形如:

json格式形如:

但是当前通过json传递数据,服务器只是将整个body都读出来,没有按照键值对的方式处理,还不能根据key获取到value,可以使用第三方库来解析json的body,如jackson,gson,fastjson,由于spring mvc内置了jackson这个库,故而天然就支持处理jackson的数据。

故而,以引入jackson库为例:

通过maven引入第三方库:

基于jackson库,修改代码使得获取到请求body内部的键值对:

 class Student{public int studentId;public int classId;
}
@WebServlet("/postParameter2")
public class PostParameter2Servlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 使用jackson的核心对象:ObjectMapper objectMapper = new ObjectMapper();// readValue就是把一个json格式的字符串转为java对象:Student student = objectMapper.readValue(req.getInputStream(), Student.class);}
}

注意:(1)objectMapper对象的readValue方法的作用:

① 将json格式的字符串读取出来;

② 根据第二个参数类对象创建Student实例,第二个参数是一个类对象

③ 解析json格式的字符串,处理成map键值对结构;

④ 遍历所有键值对,看键的名字和Student实例的哪个属性名匹配,就把对应的value设置到该属性中;

⑤ 返回该Student实例;

(2)readValue方法用于将json字符串转成java对象,writeValue方法用于把一个java对象转成json格式字符串

(3)在上文代码中,将Student类内的属性设置为public修饰,这在Java中并不常见。但是如果要把一个类作为jackson返回的对象,就需要让jackson能够看到类中的属性,可以采取的方法有:

① 把属性设置为public;② 给该属性提供public修饰的getter与setter方法;

4. HttpServletResponse

servlet代码种的doXXX方法就是根据请求计算响应后,将响应的数据设置到HttpServletResponse对象中。tomcat就会将这个HttpServletResponse对象按照HTTP协议的格式,转成一个字符串,并通过socket写回给浏览器;

其核心方法有:

方法描述
void setStatus(int sc)为该响应设置状态码
void setHeader(String name, String value)设置一个带有给定的名称和值的header,如果name已经存在,则覆盖旧值
void addHeader(String name, String value)添加一个带有给定的名称和值的header,如果name已经存在,不覆盖旧值,并列添加新的键值对
void setContentType(String type)设置被发送到客户端的响应的内容类型
void setCharacterEncoding(String charset)设置被发送到客户端的响应的字符编码(MIME字符集),例如UTF-8
void sendRedirect(String Location)使用指定的重定向URL发送临时重定向响应到客户端
PrintWriter getWriter()用于往body中写入文本格式数据
OutputStream getOutputStream()用于往body中写入二进制格式数据

(1)一个HTTP响应中报头的key是可以存在多个重复的

(2)可以通过setCharacterEncoding指定响应的编码格式:

浏览器默认不知道程序员的页面编码方式,会采取猜测的方式使用字符集进行解析,比如在getParameterServlet.java中将写回浏览器的响应格式写为:

resp.setContentType("text/html");
resp.getWriter().write("学生id: "+studentId +" 班级id: "+classId);

 此时根据路径打开浏览器页面,就会出现乱码: 

为了避免这种情况,我们需要在写回响应前,显式指定响应的编码格式字符集:

        resp.setContentType("text/html");resp.setCharacterEncoding("utf-8");resp.getWriter().write("学生id: "+studentId +" 班级id: "+classId);

刷新浏览器页面,有: 

(3)也可以把字符集和ContentType一起设置:

        resp.setContentType("text/html; charset=utf8");resp.getWriter().write("学生id: "+studentId +" 班级id: "+classId);

(4)void sendRedirect用于构造重定向响应,3xxx的状态码就会跳转到另外一个页面:

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("/redirect")
public class RedirectServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.sendRedirect("");}
}

运行代码后,根据路径打开浏览器页面,即可跳转至搜狗主页:

前后端交互逻辑如下:

 

实现重定向,尤其是返回错误页面有多种方式:

发布评论

评论列表(0)

  1. 暂无评论