目录
Tomcat
Servlet的工作
创建Servlet
①项目
②依赖
③目录
④代码
⑤打包
⑥部署
⑦验证
Servlet的运行原理
Servlet API
HttpServlet
方法
处理Get/POST请求
HttpServletRequest
方法
获取请求中的信息
获取GET请求中的参数
获取POST请求中的参数
FORM表格形式
JSON形式
方法一:
方法二:
HttpServletResponse
Servlet是一种由Tomcat提供的开发动态页面的技术
动态页面指的就是,能够根据用户给予参数的不同,返回不同页面的效果.
Tomcat
Tomcat是一个HTTP服务器.我们可以将程序部署到其中的webapps目录下,就可以通过Tomcat服务器对其进行访问.
Tomcat8的下载🔗:Apache Tomcat® - Apache Tomcat 8 Software Downloads
Servlet的工作
Servlet由Tomcat提供的一组API
主要是功能是:
- 允许创建一个类,帮助在Tomcat收到特定的请求后,根据请求执行程序
- 帮助解析HTTP请求,将HTTP请求从一个字符串解析成一个HttpRuquest对象方便对其进行处理
- 帮助构造HTTP响应,填写属性到HttpResponse,Servlet就会自动的根据HTTP协议构造出一个HTTP响应字符串,并通过Socket返回给客户端
创建Servlet
①项目
在IDEA中创建一个项目,选择Maven.
再点击next,即可创建.
Maven是一个项目管理工具,给到一定的项目结果,与创建的流程方便开发与管理.
创建成功后,会出现一个名为pom的xml文件
pom.xml文件主要是作为项目对象模型,规定了项目的一定行为,或是存放一些属性.
②依赖
要使用Servlet就要在pom.xml中引入Servlet API依赖的jar包
在此网页中寻找Servlet的依赖:Maven Repository: Search/Browse/Explore (mvnrepository.com)
找到Tomcat版本对应的Servlet版本
可以在此网页查询Tomcat与Servlet的对应关系:Apache Tomcat® - Which Version Do I Want?
在pom.xml的<poject>中,创建一个<dependencies>标签,再把刚刚Servlet的xml给复制进去.
复制后,IDEA中就会下载相应的依赖到本地中.下载成功后就不会标红啦.
③目录
在main目录下,创建一个webapp目录.
并在其下创建一个WEB-INF目录,在WEB-INF目录中创建一个web.xml.
此作用是,告知Tomcat这个项目你得认识噢.
在创建好的web.xml当中存放一段代码
代码中红不红都不要紧的哟.
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
</web-app>
④代码
前面准备这么多,终于可以编写代码啦.
在java目录当中,创建一个类.
此类继承HttpServlet
(如果显示库中还没有HttpServlet这个类,可以去右侧的maven刷新一下噢)
public class HelloServlet extends HttpServlet {
}
此处要写的就是,永恒的Hello啦.在网页中写上一个HELLO
在HelloServlet类中,重写HttpSerlvet的doGet方法.
此方法是,Tomcat当中对此网页使用了一个GET方法才会运行到.
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HELLO");
resp.getWriter().write("HELLO");//获取到一个流对象,并使用其write方法写一个hello
}
}
要注意doGet的两个参数,一个是HTTP请求对象,一个是HTTP响应的对象.
- 将HTTP请求的字符串经过相应的格式转换成HTTP请求对象,可以在HTTP请求对象中,获取到请求的信息,像是方法(get或post...),URL,header和body等
- 在HTTP响应的对象中,可以根据我们的想法构造一个HTTP响应的字符串格式.
关于resp.getWriter.write方法,获取到HTTP响应对象的一个流对象.
再通过流对象的write方法,写入相应的字符串.
写入的数据,就会变成HTTP的body部分.Tomcat服务器再将HTTP响应转化成字符串,通过Socket发给客户端接收.
@WebServlet("/HELLO")//注释
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HELLO");
resp.getWriter().write("HELLO");
}
}
最后要记得在创建的类上写上一个@WebServlet("/")的注释噢.
在Tomcat服务器收到路径为/HELLO的URL请求才会调用HelloServlet类的代码.
⑤打包
打包的作用是,为了让项目能在别处(服务器)运行,将一整个打包带走的感觉.
打包之前可以先对将要打的包制定一些规矩.
在pom.xml的<poject>标签下,给上下面的代码.
给要打的包命名,并指定其格式为war包.(默认打出来的是jar包)
要打成war包才会被Tomcat识别
//命名一下打的包
<build>
<finalName>HelloServlet</finalName>//注意命名噢
</build>
//打成一个war包
<packaging>war</packaging>
在IDEA右侧,点击maven按钮.打开图中相应的目录,找到"package"
打包完成后,会发现又新生成了一个目录.打出来的包则会在其的底下
⑥部署
部署,就是将你刚刚所写的项目提交到Tomcat服务器当中.
找到Tomcat文件中的webapps,将刚刚打包好的war包复制粘贴进去.
启动一下Tomcat服务器,就会自动的对war包进行解压缩.就把项目扔给了Tomcat服务器啦.
⑦验证
到了验证成果的时候了.
在启动Tomcat服务器的基础下,通过Tomcat服务器访问我们刚刚创建的Hello项目.
在地址上输入
127.0.0.1:8080/HelloServlet/HELLO
此处的URL是一个环回地址啦,访问本机的.其中的端口号就是Tomcat服务器的端口号.
后面的两个路径对应:
- 第一个是war包解压出的目录的名称.
- 第二个是我们给创建的类@WebServlet()注释的字符串.
最终的结果就是:
以上是Servlet的创建与手动打包的过程.
还可以在IDEA中安装一个Smart Tomcat插件.对其自动的打包和部署,一点即成.
Servlet的运行原理
在上面的代码中,我们可以发现与以往的代码不同,他是没有创建main方法的.
是由Tomcat服务器来承担起了启动程序的重任.
当我们想要设计一个程序,他可以在任意时刻被任意用户所使用.那么这个程序该在什么时候打开,又在什么时候关上呢?
用户所使用的时间是不固定的,随机的.程序员们不可能接收到用户要使用的信号后再去手动的打开相应的程序.
那么这个时候,服务器出现了.服务器存储着将要被使用的代码,当用户想要使用的时候:
- 首先用户向服务器发出请求,申请使用某一程序实现某一功能.
- 在服务器接收到请求后,解析此请求再根据请求中的内容找到相应的程序并将其运行,
- 计算所得的结果再由服务器返回响应给用户,达到用户的需求
而此处的Servlet与Tomcat服务器的过程也是差不多的.
①请求
用户在网页上输入URL,访问Tomcat服务器中的文件,此时浏览器就会构建一个HTTP请求.
这个HTTP请求就会按照网络协议栈层逐层的封装,转换成比特流,通过物理层进行传输给Tomcat服务器.
服务器收到此比特流后,根据相应的协议对其进行分用,最终还原成为一个HTTP请求,交给Tomcat处理.
Tomcat通过Socket读取到此请求(读取到的是一个字符串),并根据HTTP请求的格式对其进行解析成为一个HttpServletRequest对象.
根据请求中的路径,找到服务器当中要被使用的webapp和具体的某一个类.再根据请求中的方法去决定调用这个类的doGet方法还是doPost方法.
②执行
执行doGet或doPost方法中可以给HttpServletResponse对象设置其header或body.
执行完后,Tomcat会自动的把HttpSeverletResponse对象构造成一个符合Http协议的字符串.再由Socket发送出去.
③响应
这个被Socket发送的数据,又经过网络协议栈层的封装,通过物理层传输到达客户端后.又被响应的协议分用,构造成了一个Http响应.
浏览器的Socket收到响应后,通过Http响应的格式进行解析,最终呈现到浏览器界面上.
Servlet API
HttpServlet
Servlet继承的类
方法
方法名称 | 调用时机 |
init | 在 HttpServlet 实例化之后被调用一次 |
destory | 在 HttpServlet 实例不再使用的时候调用一次 |
service | 收到 HTTP 请求的时候调用 |
doGet | 收到 GET 请求的时候调用(由 service 方法调用) |
doPost | 收到 POST 请求的时候调用(由 service 方法调用) |
doPut/doDelete/doOptions/... | 收到其他请求的时候调用(由 service 方法调用) |
init,destory和service三个方法,正是代表了Servlet的生命周期(什么时候干什么时).
- init是实例时调用一次
- service是在接收HTTP请求时被调用
- destory是结束前被调用一次
处理Get/POST请求
在HttpServlet中是如何处理Get请求的
创建一个Servlet,记得要完成上面打包部署的步骤噢.也可以借助SmartTomcat
@WebServlet("/DOGET")
public class DoGet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("doGet");
}
}
生成一个html文件在项目的webapp目录下,并写下ajax代码.
URL上写的是类的注释名,即是@WebServlet中的.
<body>
<script src = "http://lib.sinaapp.com/js/jquery/1.7.2/jquery.min.js"></script>
<script>
$.ajax({
type: 'get',
url : 'DOGET', //这里写的是相对路径,也可以写成绝对路径.
success: function(data){
console.log("OK")
}
});
</script>
</body>
此处是,通过ajax创建了一个http请求.是向Tomcat服务器上的DOGET申请.
Tomcat服务器接收到后,就会相应的调用DoGet类中的doGet方法.在浏览器显示页中写下doGet字样.
可以在浏览器输入URL:127.0.0.1:8080/ServletHello/doGet.html来访问
并可以在浏览器-右键-检查中的控制器看到打印的"OK"
POST方法也是一样的,只要把ajax的type改成post,并再重写doPost方法就可以了
HttpServletRequest
这个类是当Tomcat读取到HTTP请求,按照HTTP请求的格式将字符串解析成为HttpServletRequest对象
方法
方法 | 描述 |
String getProtocol() | 返回请求协议的名称和版本。 |
String getMethod() | 返回请求的 HTTP 方法的名称,例如,GET、POST 或 PUT。 |
String getRequestURI() | 从协议名称直到 HTTP 请求的第一行的查询字符串中,返回该请 求的 URL 的一部分。 |
String getContextPath() | 返回指示请求上下文的请求 URI 部分。 |
String getQueryString() | 返回包含在路径后的请求 URL 中的查询字符串。 |
Enumeration getParameterNames() | 返回一个 String 对象的枚举,包含在该请求中包含的参数的名 称。 |
String getParameter(String name) | 以字符串形式返回请求参数的值,或者如果参数不存在则返回 null。 |
String[] getParameterValues(String name) | 返回一个字符串对象的数组,包含所有给定的请求参数的值,如 果参数不存在则返回 null。 |
Enumeration getHeaderNames() | 返回一个枚举,包含在该请求中包含的所有的头名。 |
String getHeader(String name) | 以字符串形式返回指定的请求头的值。 |
String getCharacterEncoding() | 返回请求主体中使用的字符编码的名称。 |
String getContentType() | 返回请求主体的 MIME 类型,如果不知道类型则返回 null。 |
int getContentLength() | 以字节为单位返回请求主体的长度,并提供输入流,或者如果长 度未知则返回 -1。 |
InputStream getInputStream() | 用于读取请求的 body 内容. 返回一个 InputStream 对象. |
获取请求中的信息
@WebServlet("/req")
public class ShowServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html; charset=utf-8");//文本格式,编译符为utf-8
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(req.getProtocol());//获取协议的名称与版本
stringBuilder.append("<br>");//在StringBuilder中添加换行符
stringBuilder.append(req.getMethod());//返回请求的方法的名称
stringBuilder.append("<br>");
stringBuilder.append(req.getRequestURI());//返回该请求URL的部分
stringBuilder.append("<br>");
stringBuilder.append(req.getContextPath());//返回请求URI的部分
stringBuilder.append("<br>");
stringBuilder.append(req.getQueryString());//返回请求URL中的查询字符串。
stringBuilder.append("<br>");
stringBuilder.append("header:");
stringBuilder.append("<br>");
Enumeration<String> headerNames = req.getHeaderNames();
while(headerNames.hasMoreElements()){//如果header中还存在键值对,就继续执行
String headName = headerNames.nextElement();
stringBuilder.append(headName);//header名
stringBuilder.append(" : ");
stringBuilder.append(req.getHeaders(headName));//header值
stringBuilder.append("<br>");
}
resp.getWriter().write(stringBuilder.toString());
}
}
访问到的结果
获取GET请求中的参数
在get方法中,请求的参数一般是URL中的query string键值对.
https:/ /www.baidu.com/?name=wow HTTP/1.1
就像这样的,URL中的query string为name=wow.
这里的参数就是name,值为wow.
在服务器就可以通过getParameter来获取参数.
@WebServlet("/getParameter")
public class GetParameter extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text ; charset = utf-8");
String name = req.getParameter("name");
resp.getWriter().write("name:" + name);
}
}
当URL中没有query string时,值为空
当含有query string时,就会得到相应的值
获取POST请求中的参数
post请求中的参数形式,可以为form表格,也可以为json的格式
FORM表格形式
如果为form表格的形式进行传输,仍然可以使用getParameter来获取参数.
在类上重写doPost方法
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text ; charset = utf-8");
String name = req.getParameter("name");
resp.getWriter().write("name:" + name);
}
在项目的webapp目录下,创建一个html.用来发出form表格的请求
<body>
<form action="getParameter" meth = "post">
<input type="text" name = "name">
<input type="submit" value="提交">
</form>
</body>
访问刚刚创建的html文件
JSON形式
方法一:
我们可以知道,POST方法中的键值对会存在于其body部分,我们可以通过流对象直接读取其body部分即可.
重写doPost方法
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("application/json;charset=utf8");
int len = req.getContentLength();//得到body的长度
byte[] arr = new byte[len];//根据长度创建一个数组
InputStream inputStream = req.getInputStream();
inputStream.read(arr);
String body = new String(arr,"utf8");//构建出字符串
System.out.println(body);
//resp.getWriter().write(body)这条语句在此处是不会成功的
//write是通过文本的格式发送给浏览器,而浏览器要接收的是json格式
}
发送一个json请求
<body>
<script src = "http://lib.sinaapp.com/js/jquery/1.7.2/jquery.min.js"></script>
<button onclick="sendJson()">发送 JSON 格式 POST 请求</button>
<script>
let body={
"name":'john',
"age":34
};
function sendJson() {
$.ajax({
type : 'post',
url : 'getParameter',
data : JSON.stringify(body),
contentType: "application/json;charset=utf8",
});
}
</script>
</body>
通过抓包查看结果
服务器控制台
方法二:
方法一通过观察代码可以发现,他只能获取到json格式的一串字符串,并不能通过键值对的key来获取对应的value值
所以可以使用jackson这个库,来解析json.
在pom.xml中引入Jackson库的依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.0</version>
</dependency>
重写doPost方法
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("application/json;charset=utf8");
//首先要将请求的body变成一串字符串
int len = req.getContentLength();//得到body的长度
byte[] arr = new byte[len];//根据长度创建一个数组
InputStream inputStream = req.getInputStream();
inputStream.read(arr);
String body = new String(arr,"utf8");//构建出字符串
//创建一个objectMapper对象,此类为Jackson的核心类
ObjectMapper objectMapper = new ObjectMapper();
//再通过objectMapper类的readValue方法,将字符串转换成JsonData对象
JsonData jsonData = objectMapper.readValue(body,JsonData.class);
//就可以根据key来获取value啦
System.out.println("name: " + jsonData.name + " age: " + jsonData.age);
}
上面的代码方便理解,后面熟悉后可以把body直接换乘成inputstream
JsonData jsonData1 = objectMapper.readValue(req.getInputStream(),JsonData.class);
这样就可以单独的获取某一个键值对了
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 中写入二进制格式数据 |
这么多就不一一展开啦.
此处讲一下setStatus设置状态码与sendRedirect重定向.
resp.sendRedirect("http://www.sogou.com");
像是这样,把URL放在重定向的代码中.当别人访问到此程序的时候,就会去访问重定向中的网站.
而设置状态码,是可以通过getPamameter来获取URL中的参数.
可以把重定向中的URL加上一个status=404的query string键值对.
在某种情况下,就可以先进行重定向跳转网页,再通过获取status的值来设置状态码.