1.Servlet简介
1.1.Servlet概述
Servlet是用Java语言编写的运行在服务器端的程序,能 够接收Web客户端的请求,并能对 Web客户端进行响应,通常是通过HTTP协议进行工作的。
Servlet的运行需要有 Web容器的支持,它为 Servlet提供了运行时环境,并负责在适当的时刻加载和调用 Servlet。 Servlet的主要作用有:
- 读取客户发送的所有数据
- 查询 HTTP 请求中包含的任何其他请求信息
- 处理数据并生成结果
- 设置合适的 HTTP 响应参数
-
1.2.Servlet API结构
Servlet API中包含两个软件包:
javax.servlet:定义了所有Servlet类都必须实现或继承的通用接口和类。 此包下有一个GenericServlet抽象类。它是一个通用的协议无关的Servlet。 继承自GenericServlet的Servlet应该要覆盖service()方法。
- javax.servlet.http包:定义了采用HTTP协议通信的HttpServlet类。 此包下有HttpServlet抽象类。它继承自GenericServlet。能够处理HTTP请求的Servlet, 它在原有Servlet接口上添加了对HTTP协议的处理,它比Servlet接口的功能更为强大。
注意:由于普遍都采用http协议,所以在实际开发中,我们创建一个Servlet时,继承HttpServlet即可。
2.第一个Servlet程序
2.1.创建Servlet
创建demo工程,在src下创建servlet包,在此包中创建一个HelloServlet类
package servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(urlPatterns="/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//设置请求信息的字符编码(必须在使用request之前进行设置)
request.setCharacterEncoding("utf-8");
//使客户端浏览器并根据不同的MIME调用浏览器内不同的程序嵌入模块来处理相应的数据。
response.setContentType("text/html;charset=utf-8");
//设置响应信息的字符编码(必须在创建响应输出流之前进行设置)
response.setCharacterEncoding("utf-8");
//获取客户端提交参数
String name = request.getParameter("name");
//创建响应输出流
PrintWriter out = response.getWriter();
//向客户端响应信息
out.println("<html>");
out.println(" <head><title>Servlet</title></head>");
out.println(" <body>");
out.println("你好,"+name+",欢迎来到Servlet的世界!");
out.println(" </body>");
out.println("</html>");
//关闭响应输出流
out.close();
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
注意:
- 一个类继承HttpServlet类,我们就将它称为一个Servlet。
- 继承HttpServlet类后,必须要重写doGet和doPost方法。 当get请求时,自动由doGet方法进行处理。 当post请求时,自动由doPost方法进行处理。
- doGet和doPost方法都有两个参数:HttpServletRequest、HttpServletResponse HttpServletRequest:封装了请求信息。 HttpServletResponse:封装了响应信息。
- 所以,可以使用HttpServletRequest对象中的getParameter方法获取客户端提交参数。 使用HttpServletResponse创建响应输出流,向客户端响应信息。
- @WebServlet注解中,使用urlPatterns属性来设置Servlet映射:将客户端请求映射到对应的Servlet上。
- @WebServlet注解中,也可以简写为:@WebServlet(“/hello”)
2.2.访问Servlet
启动Tomcat服务器,然后在浏览器地址栏中输入:http://localhost:8080/demo/hello?name=zhangsan 就可以看到Servlet响应信息了。
3.Servlet生命周期
Servlet实例从创建直到毁灭的整个过程,就是Servlet 生命周期。以下是 Servlet 遵循的过程:
- Servlet 容器创建 Servlet 的实例(下面会详述Servlet实例创建时机)
- Servlet 通过调用 init () 方法进行初始化(只调用一次)。
- Servlet 调用 service() 方法来处理客户端的请求(每一次请求都要调用一次)。
service() 方法会自动调用doGet或doPost。 - Servlet 通过调用 destroy() 方法终止(只调用一次)。
在调用 destroy() 方法之后,servlet 对象被标记为垃圾回收。
3.1.演示Servlet生命周期
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
@Override
public void init() throws ServletException {
System.out.println("init方法");
}
@Override
protected void service(HttpServletRequest arg0, HttpServletResponse arg1)
throws ServletException, IOException {
PrintWriter out = arg1.getWriter();
out.println("hello!aaaaa");
}
@Override
public void destroy() {
System.out.println("destroy方法");
}
}
3.2.Servlet实例的创建时机
Servlet对象由Web服务器负责创建,创建的时机可以使用loadOnStartup属性进行配置
- loadOnStartup属性的值必须是一个整数,表示servlet应该被载入的顺序
- 当值为0或者大于0时,表示容器在应用启动时就加载并初始化这个servlet;
- 当值小于0或者没有指定时,则表示容器在该servlet被选择时才会去加载。
- 正数的值越小,该servlet的优先级越高,应用启动时就越先加载。
- 当值相同时,容器就会自己选择顺序来加载。
- 取值1,2,3,4,5代表的是优先级,而非启动延迟时间。
@WebServlet(urlPatterns="/hello",loadOnStartup=1)
3.3.Servlet实例的初始化参数
我们可以在Servlet创建实例时,给Servlet传一些初始化的参数。package servlet; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.annotation.WebInitParam; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; //使用注解给Servlet传初始化参数 @WebServlet(urlPatterns="/hello",initParams={ @WebInitParam(name="setChar",value="utf-8") }) public class HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取ServletConfig ServletConfig config = this.getServletConfig(); //然后通过ServletConfig的getInitParameter方法获取初始化参数 String encoding = config.getInitParameter("setChar"); request.setCharacterEncoding(encoding); response.setContentType("text/html;charset=utf-8"); response.setCharacterEncoding(encoding); String name = request.getParameter("name"); PrintWriter out = response.getWriter(); out.println("<html>"); out.println(" <head><title>Servlet</title></head>"); out.println(" <body>"); out.println("你好," + name + ",欢迎来到Servlet的世界!"); out.println(" </body>"); out.println("</html>"); out.close(); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
4.HTTP状态码
HTTP状态码(英语:HTTP Status Code)是用以表示网页服务器超文本传输协议响应状态的3位数字代码。它由 RFC 2616 规范定义的。所有状态码的第一个数字代表了响应的五种状态之一。
1xx:消息。这一类型的状态码,代表请求已被接受,需要继续处理。这类响应是临时响应,只包含状态行和某些可选的响应头信息,并以空行结束。
2xx:成功。代表请求已成功被服务器接收、理解、并接受。比如:
- 200:请求已成功。出现此状态码是表示正常状态。
3xx:重定向。代表需要客户端采取进一步的操作才能完成请求。通常,这些状态码用来重定向,后续的请求地址(重定向目标)在本次响应的 Location 域中指明。
4xx:请求错误。代表客户端请求发生错误。比如:
- 404:找不到请求的资源
- 405:服务器端处理请求方法不被允许。
5xx:服务器错误。代表服务器端出现错误。 比如:
- 500:服务器端代码错误。
- 503:服务器异常(通常重启服务器即可解决)。
5.Servlet返回JSON数据
现在的Web应用程序,都是采用前后端分离的开发模式。这样就要求前端工程与服务器端工程之间,要进行数据交互。
那么,前端向服务器端请求时,采用AJAX请求。而服务器端向前端返回数据时,采用JSON格式数据。
Jackson 是当前用的比较广泛的,用来序列化和反序列化 json 的 Java 的开源框架。 Jackson 社 区相对比较活跃,更新速度也比较快, 从 Github 中的统计来看,Jackson 是最流行的 json 解析器之一 。Spring MVC 的默认 json 解析器便是 Jackson。 本项目中,Servlet中使用Jackson将java对象或集合转换为json对象或数组后,返回前端。
在浏览器地址栏中直接进行访问,body中就会接收响应的json字符串。package servlet; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.annotation.WebInitParam; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.fasterxml.jackson.databind.ObjectMapper; import po.User; @WebServlet("/hello") public class HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); response.setCharacterEncoding("utf-8"); List<User> list = new ArrayList<>(); list.add(new User(1,"张三",20)); list.add(new User(2,"李四",21)); list.add(new User(3,"王五",22)); PrintWriter out = response.getWriter(); //使用Jackson的jar包中的ObjectMapper进行java对象与jose数据的转换 ObjectMapper om = new ObjectMapper(); //使用writeValueAsString方法将java对象转换为json格式字符串 out.print(om.writeValueAsString(list)); out.close(); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
5.服务器端设置允许跨域访问
我们知道,浏览器默认不允许AJAX跨域访问;所以,下面代码运行后会出现 ”Access-Control-Allow-Origin“ 错误。
此时就会发现,在浏览器控制台中会出现 “跨域访问” 错误。<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script> axios.get('http://localhost:8080/demo/hello') .then(function(response) { console.log(response.data); }) .catch(function(error) { console.log(error); }); </script> </body> </html>
在前面的课程中,我们使用 Vue-cli 代理实现了前端的跨域访问设置。 下面我们使用CORS来实现服务器跨域访问的设置。5.1.CORS简介
CORS是H5新增的一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing)。跨域只存在于浏览器端,不存在于Node.js/python/ java等其它环境;跨域请求是能够发出去的,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。 CORS可以在服务器端响应时,向浏览器发送 “Access-Control-Allow-Origin” 配置信息,通知浏览器解除跨域限制。如果浏览器支持CORS、并且判断Origin通过的话,就会允许XMLHttpRequest发起跨域请求。 所以,CORS配置要写在服务器端。
5.2.CORS跨域设置
CORS跨域设置:response.setHeader(“Access-Control-Allow-Origin”, “*”);
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//设置允许跨域访问,并且设置允许什么样的url进行跨域访问。
response.setHeader("Access-Control-Allow-Origin", "*");
//response.setHeader("Access-Control-Allow-Origin", "http://localhost:8081");
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
response.setCharacterEncoding("utf-8");
List<User> list = new ArrayList<>();
list.add(new User(1,"张三",20));
list.add(new User(2,"李四",21));
list.add(new User(3,"王五",22));
PrintWriter out = response.getWriter();
ObjectMapper om = new ObjectMapper();
out.print(om.writeValueAsString(list));
out.close();
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
7.请求与响应的总结
7.1.HttpServletRequest常用API
- getParameter() 获取客户端提交的单值参数,也就是返回值是一个字符串。
- getParameterValues() 获取客户端提交的多值参数(复选框组),也就是返回值是一个字符串数组。
setCharacterEncoding() 设置请求的字符编码。
7.2.HttpServletResponse常用API
getWriter() 获取一个向客户端响应的输出流。
- setCharacterEncoding() 设置输出流的字符编码。
- setContentType() 设置MIME类型,也就是通知浏览器使用何种编码解析响应数据。
课后作业
- 简述Servlet的创建过程?
- 简述HttpServletRequest、HttpServletResponse两个参数的作用?
- 简述Servlet 生命周期过程?
- 简述@WebServlet注解的作用?
- 简述如何配置Servlet的创建时机?
- 什么是HTTP状态码?请至少说出4种HTTP状态码及其作用?
- 简述CORS的跨域设置?
- 编程题:
- 使用Servlet实现一个登录案例。
- 编写一个用户登录页面,包括用户名和密码表单。
- 编写一个LoginServlet,并在Servlet中判断是否登录成功,然后向前端返回成功或失败的json数据。
- 前端使用ajax请求,并通过服务器端返回的json数据来显示是否登录成功。