Servlet学习
Posted 爱敲代码的三毛
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Servlet学习相关的知识,希望对你有一定的参考价值。
文章目录
Servlet
Servlet是一种实现动态页面的技术,是一组Tomcat提供给程序员的API,帮助程序员简单高效的开发一个 web app(网站)。
静态页面和动态页面
静态页面也就是内容始终固定的页面,即使用户不同/时间不同/输入的参数不同,页面内容也不会发生变化(除非网站的开发人员修改源代码,否则页面内容始终不变)
对应的,动态页面指的就是 用户不同/时间不同/输入的参数不同,页面内容会发送变化
第一个Servlet程序
xml配置路由映射
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1"
<!--所有路由文件已经在本xml,没办法使用注解的范式配置路由 -->
metadata-complete="true">
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
Servlet 注解的 web.xml 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1"
<!-- 设置成false就可以了-->
metadata-complete="false">
</web-app>
-
首先要创建一个 Maven项目
-
在Maven项目里的pom.xml里引入依赖,从中央仓库的Servlet api 的jar包
选择Tomcat8对应的3.1版本的Servlet依赖
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<!-- scope这个标签表示当前jar包只是在开发阶段使用,而不需要打包到最终的发布包中 -->
<!-- Tomcat里面内置了Servlet,把程序部署到Tomcat上的时候,已经有了Servlet了 -->
<!-- 就不需要把自己代码里的Servlet也打包一份放到Tomcat上了 -->
<scope>provided</scope>
</dependency>
把依赖放到 pom.xml中的 dependencies标签中
-
开始写代码
1.先创建好一个类继承HttpServlet
2.重写doGet方法
@WebServlet("/test") public class TestServlet extends HttpServlet @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException //这个调用父类的方法一定要干掉 //super.doGet(req, resp); resp.getWriter().write("hello Servlet"); //doGet是HttpServlet这个父类的方法 //这个方法的参数分别是 // HttpServletRequest:HTTP请求 //HttpServletResponse:HTTP响应 //doGet方法里要做的事情,就是根据请求,生成响应
注意:重写的 doGet 方法之后,并不需要手动调用 doGet,而是由Tomcat自动来调用,也不需要咱们手动的创建 TestServlet 实例,也是由Tomcat自动创建实例
resp.getWriter.write("hello Servlet");
这个操作,就是往HTTP响应的body中,写了一个 "hello Servlet"字符串
@WebServlet
@WebServlet("/test")
@WebServlet 也是Servlet中提供的注解,功能就是把类和HTTP 特定请求进行关联,根据HTTP请求URL的路径来进行关联的
如果咱们的Tomcat收到了一个路径为 /test 的请求,就会调用到 TestServlet 的代码
如果这个请求是GET请求,就会调用到HelloServlet.doGet 方法
如果这个请求是POST请求,就会调用到HelloServlet.doPost方法
注意
super.doGet(req, resp)
重写方法后,一定要把这个调用父类代码的操作,给干掉
这个方法里面直接构造了一个错误的响应(状态码为 405 的响应)
web.xml
在main目录中,创建一个webapp目录,里面再创建一个 WEB-INF目录,在WEB-INF里再创建 web.xml文件
把下面的代码复制到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>
webapp 目录就是未来部署到 Tomcat 中的一个重要的目录. 当前我们可以往 webapp 中放一些静态资源, 比如 html , css 等.
在这个目录中还有一个重要的文件 web.xml. Tomcat 找到这个文件才能正确处理 webapp 中的动态资源
打包操作
双击package就能进行打包,或者右击运行
由于Maven默认打成的是一个 jar格式的包,而Tomcat不能识别 jar格式的包。Tomcat识别的是war格式的包,所以要修改 maven 中的 pom.xml 的配置,修改打包类型为 war
在 pom,xml中添加下面这行代码,就能让maven打成的包为 war格式
<packaging>war</packaging>
打包的包名默认是 artifact id + version,名字太复杂
在pom.xml中添加下面的代码就能修改打包的包名
<build>
<finalName>java</finalName>
</build>
部署程序
把war包拷贝到 Tomcat的webapps目录中即可,启动Tomcat之后,就会解压缩出来一个同名的目录
完整的pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>TestServlet</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<!-- 按照jdk1.8这样的版本来理解源码,并按照这个版本来生成字节码 -->
<maven.compiler.soutce>8</maven.compiler.soutce>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<!-- 改成war包 -->
<packaging>war</packaging>
<build>
<finalName>java</finalName>
</build>
<dependencies>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<!-- scope这个标签表示当前jar包只是在开发阶段使用,而不需要打包到最终的发布包中 -->
<!-- Tomcat里面内置了Servlet,把程序部署到Tomcat上的时候,已经有了Servlet了 -->
<!-- 就不需要把自己代码里的Servlet也打包一份放到Tomcat上了 -->
<scope>provided</scope>
</dependency>
</dependencies>
</project>
Smart Tomcat
IDEA专业版自带 Tomcat的部署功能,不需要使用 Smart Tomcat插件
IDEA社区版没有自带Tomcat的部署功能,需要使用 Smart Tomcat插件
Servlet常见问题
1.404
出现404最大的原因就是URL的路径写错了
或者是@WebServlet注解的内容写错了,比如少了一个 /,或者是URL的路径和注解对不上号
如果 web.xml写错了,也可能导致404
2.405
405最重要的原因,请求的方法和代码中重写的方法对不上号
往浏览器里输入URL应该是GET请求,而实现的却是POST方法
还有一个原因就是调用父类的 doGet方法,没有删掉。
父类的doGet方法是直接返回405的
3.500
出现500最主要的原因就是代码中抛异常了,页面上/Tomcat日志里面会明确提示出异常的调用栈等详细信息
在实际开发中,异常调用栈直接展示在页面上,是一件非常危险的事情
一个商业产品,如果异常调用栈被用户看到了,就可能带来安全隐患
从异常调用栈里可以看到出错误的代码的具体位置,也知道了是用什么代码写的
同时也知道了 Tomcat的版本号
黑客手里一般都有漏洞库,就可以去找对应的版本的漏洞库,从而对你的服务器进行攻击(很可能会入侵成功,窃取破坏服务器上的信息)
4.空白页面
@WebServlet("/test")
public class TestServlet extends HttpServlet
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
//resp.getWriter().write("hello Servlet");
如果没有调用resp.getWriter().write,没往body里写就会出现空白页面。有时候可能里面的代码比较复杂,没有注意是否调用resp.getWriter().write就可能出现这种情况
5.无法访问此网站
一般就是Tomcat启动失败了
Servlet API
学习Servlet的具体使用,核心掌握三个类就够了
- HttpServlet
- HttpServletRequest
- HttpServletResponse
HttpServlet
方法名 | 调用时机 |
---|---|
init | 在 HttpServlet 实例化之后被调用一次 |
destory | 在 HttpServlet 实例不再使用的时候调用一次 |
service | 收到 HTTP 请求的时候调用 |
doGet | 收到 GET 请求的时候调用(由 service 方法调用) |
doPost | 收到 POST 请求的时候调用(由 service 方法调用 |
doPut/doDelete/doOptions/… | 收到其他请求的时候调用(由 service 方法调用) |
实际开发中主要重写 doxxx方法,很少会重写init/destory/service。
这些方法的调用时机,就称为"Serlet生命周期",也就是描述了一个Servlet实例从生到死的过程
Serlet的生命周期
一个常见的面试题:说一下Servlet的生命周期
- Servlet在实例化之后调用一次 init
- Servlet每次收到请求,调用一次service
- Servlet在销毁之前,调用一次destroy
代码实例:处理GET、POST、PUT请求
创建一个 MethodServlet类
@WebServlet("/method")
public class MethodServlet extends HttpServlet
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
//设置响应的内容类型和字符编码
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write("get 请求");
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write("post 请求");
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
resp.setContentType("text/html; charset=utf-8");
resp.getWriter().write("PUT 请求");
html代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>MethodServlet</title>
</head>
<body>
<button onclick="sendGet()">发送GET请求</button>
<button onclick="sendPost()">发送POST请求</button>
<button onclick="sendPut()">发送PUT请求</button>
<script src="https://lib.baomitu.com/jquery/3.6.0/jquery.min.js"></script>
<script>
function sendGet()
$.ajax(
type: 'get',
url: 'method',
success: function (data,status)
console.log(data);
)
function sendPost()
$.ajax(
type: 'post',
url: 'method',
success: function (data,status)
console.log(data);
)
function sendPut()
//构造一个 XMLHttpRequest 对象
let httpRequest = new XMLHttpRequest;
//这里注册的 function 不是立马执行的,响应啥时候到,function啥时候执行
httpRequest.onreadystatechange = function ()
if (httpRequest.readyState == 4)
console.log(httpRequest.responseText+" "+httpRequest.status);
//构造一个HTTP请求,设置要向哪个URL发送请求
httpRequest.open('PUT','method');
httpRequest.send();
</script>
</body>
</html>
乱码处理
setContentType
这 个方法设置发送到客户端的响应的内容类型,此时响应还没有提交。给出的内容类型可以包括字符编码说明,此处注明响应编码是 utf-8
该方法如果在getWriter()方法被调用之后或者在被提交之后调用,将不会设置响应的字符编码
HttpServletRequest
由Tomcat把字符串结构的请求解析成了一个结构化的数据
从字符串到结构化数据的过程,称为"反序列化"
URL和URI
URL和URI含义是类似的,都是表示网络上的一个资源
L Lication,资源的位置
I Id, 资源的标识符
所以这俩东西往往使用的场景都差不多,也就不做过多区分了
方法 | 描述 |
---|---|
String getProtocol() | 返回请求协议的名称和版本 |
String getMethod() | 返回请求的 HTTP 方法的名称,例如,GET、POST 或 PUT |
String getRequestURI() | 从协议名称直到 HTTP 请求的第一行的查询字符串中,返回该请求的 URL 的一部分 |
String getContextPath() | 返回指示请求上下文的请求 URI 部分 |
String getQueryString() | 返回包含在路径后的请求 URL 中的查询字符串,得到的是完整的QueryString |
Enumeration getParameterNames() | 返回一个 String 对象的枚举,包含在该请求中包含的参数的名称,也就是header中所有键值对的key |
String getParameter(String name) | 以字符串形式返回请求参数的值,或者如果参数不存在则返回null,输入key返回jQuery中的value值 |
String[] getParameterValues(String name) | 返回一个字符串对象的数组,包含所有给定的请求参数的值,如果参数不存在则返回 null |
Enumeration getHeaderNames() | 返回一个枚举,包含在该请求中包含的所有的头名 |
String getHeader(String name) | 以字符串形式返回指定的请求头的值,header中的键值对的value |
String getCharacterEncoding() | 返回请求主体中使用的字符编码的名称 |
String getContentType() | 返回请求主体的 MIME 类型,如果不知道类型则返回 null |
int getContentLength() | 以字节为单位返回请求主体的长度,该方法用于获取请求的 Body 的长度,如果不确定长度,则返回 -1 |
InputStream getInputStream() | 用于读取请求的 body 内容. 返回一个 InputStream 对象 |
通过这些方法可以获取到一个请求中的各个方面的信息
注意:请求对象是服务器收到的内容, 不应该修改. 因此上面的方法也都只是 “读” 方法, 而不是 “写”
方法
代码示例:打印request中的信息
@WebServlet("/showRequest")
public class ShowRequestServlet extends HttpServlet
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
resp.setContentType("text/html; charset=utf-8");
StringBuilder requestBody = new StringBuilder();
//获取协议的名称和版本
requestBody.append(req.getProtocol());
requestBody.append("<br/>");
//返回HTTP方法名称
requestBody.append(req.getMethod());
requestBody.append("<br/>");
//返回资源的标识和URL类似
requestBody.append(req.getRequestURI());
requestBody.append("<br/>");
//返回ContextPath,就是那个用来区分webapp的根路径
requestBody.append(req.getContextPath());
requestBody.append("<br/>");
//返回完整的querystring
requestBody.append(req.getQueryString());
requestBody.append("<h3>headers:</h3>");
//返回一个枚举类型返回请求头 header中所有键值对的 key
Enumeration<String> headerNames = req.getHeaderNames();
//类似于迭代器的遍历
while (headerNames.hasMoreElements())
String headerName = headerNames.nextElement();
requestBody.append(headerName+": ");
//getHeader是根据key获取value
requestBody.append(req.getHeader(headerName));
requestBody.append("<br/>");
resp.getWriter().write(requestBody.toString());
代码示例:获取GET请求中的参数
GET请求中的参数一般都是通过query string 传递给服务器的列如:
http://127.0.0.1:8080/TestServlet/showRequest?userId=111&classId=222
此时浏览器通过 query string 给服务器传递了两个参数, userId 和 classId,值分别是 111和222
在服务端就可以通过 getParameter来获取到参数的值
@WebServlet("/getParameter")
public class GetParameter extends HttpServlet
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
//设置响应的内容类型和字符编码
resp.setContentType("text/html; charset=utf-8");
String userId = req.getParameter("userId");
String classId = req.getParameter("classId");
resp.java web学习: 三大组件之servlet基础