⭐️前言⭐️
有了前边文章为我们奠定下的网络基础,我们就可以开始学习Servlet的知识了,在部署一个Java的Servlet程序时,必须要有的开发工具是Tomcat,需要自行完成Tomcat的配置,并掌握maven仓库的配置方法,下边我们也会进行演示,这些具体的流程该如何来进行。
🍉博客主页: 🍁【如风暖阳】🍁
🍉精品Java专栏【JavaSE】、【备战蓝桥】、【JavaEE初阶】、【MySQL】、【数据结构】
🍉欢迎点赞 👍 收藏 ⭐留言评论 📝私信必回哟😁🍉本文由 【如风暖阳】 原创,首发于 CSDN🙉
🍉博主将持续更新学习记录收获,友友们有任何问题可以在评论区留言
🍉博客中涉及源码及博主日常练习代码均已上传码云(gitee)、GitHub
📍内容导读📍
- 🍅1.第一个Servlet程序
- 1.1 操作流程
- 1.2 插件简化
- 🍅2.常见的访问出错
- 2.1 出现404
- 2.2 出现405
- 2.3 出现500
- 2.4 出现空白页面
- 2.5 出现“无法访问此网站”
- 🍅3.Servlet运行原理
- 3.1 Servlet运行原理
- 3.2 Tomcat的执行逻辑
🍅1.第一个Servlet程序
我们首先通过idea来完成一个最简单的Servlet
程序,共需要以下七步来完成,在该部分最后也会介绍较为简单的插件配置方法。
1.1 操作流程
第一步:
创建一个maven项目。
1)新建项目
2)选择maven项目
3)编辑信息
第二步:
引入依赖
1)在maven
中央仓库找到Servlet
的依赖
2)找到3.1
版本,复制maven
依赖
3)将依赖引入pom.xml
文件中
第三步:
创建目录结构
1)在main
目录创建webapp
目录
2)在webapp
目录创建WEB-INF
目录
3)在WEB-INF
目录新建web.xml
文件
4)在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>
第四步:
编写Servlet代码
在main-java
目录下新建类,开始编写servlet
代码
- 继承
HttpServlet
类 - 重写
doGet
方法 - 在方法里写执行的代码
- 加上
WebServlet
注解+路径,将该类与http
请求中的URL关联起来
第五步:
打包
当前的代码是不能单独运行的(没有main方法),需要把当前的代码打包,然后部署到Tomcat上,由Tomcat来进行调用。
1)先进行准备工作,修改pom.xml
2)双击package
,进行打包
打包完成
第六步:
部署
1)打开war
包所在路径
2)把war包拷贝到tomcat
的webapps
目录里
3)启动tomcat
第七步:
验证程序
在网页访问路径127.0.0.1:8080/hello/test
此处的路径共有三部分组成
1.2 插件简化
在我们每次修改Servlet类代码后,都需要重复进行1.1中5-6
步操作,较为繁琐,可以利用idea中内嵌的smart tomcat插件来简化开发流程。
完成上图插件的安装后,按下图流程操作。
注意,利用插件时,五六步操作完全不再需要手动进行了,不需要再进行第五步的准备工作——在pom.xml
文件中加代码,直接在下图中定义Context Path
即可。
点击小三角一次性完成打包、部署工作:
🍅2.常见的访问出错
初学Servlet,肯定会遇到很多的问题,我们不仅要学习Servlet代码的基本写法,还需要认识常见的错误,在出现错误时能够解决。
2.1 出现404
404表示用户访问的资源不存在,大概率是URL路径写的不正确。
还有可能是因为web.xml
文件写错了,需要写正确web.xml
中内容,并重新启动tomact
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>
2.2 出现405
405表示对应的HTTP请求方法没有实现
在地址栏输入URL路径进行访问,浏览器默认构造的是GET请求,如果在服务器中的代码没有实现doGet
方法,就会报这个错误。
在什么时候浏览器发的是GET请求呢?
1.直接在地址栏里输入URL
2.通过a
标签跳转
3.通过img/link/script...
4.通过form
表单,method指定为GET
5.通过ajax
,type指定为GET
那在什么时候浏览器发的是POST请求呢?
1.通过form
表单,method指定为POST
2.通过ajax
,type指定为POST
2.3 出现500
500意味着服务器代码抛出异常了,并且代码没有处理异常,异常向上传递到了Tomcat这里了。
这种错误是最好解决的,因为异常的调用栈,会直接打印在页面上,可以直接找到问题的所在。
2.4 出现空白页面
没有resp.getWritter().write()
操作。
2.5 出现“无法访问此网站”
最大可能性是因为Tomcat没有正确启动(比如Tomcat的8080端口被占用,启动失败)
🍅3.Servlet运行原理
3.1 Servlet运行原理
Servlet是位于应用层的上层建筑,下面的传输层、网络层、和数据链路层属于经济基础,经济基础决定上层建筑。
Tomcat其实就是一个运行在用户态的应用程序(普通的Java进程)。
当用户操作浏览器,发送请求给服务器的时候,Tomcat作为HTTP Servlet会调用Servlet API,然后执行我们所写的Servlet程序来处理请求。
更详细的交互过程见下图:
用户浏览器发出请求后,客户端主机会将请求数据包一层层封装,最后到物理层转换成光电信号传输给服务器;到达服务器后再将数据包进行一层层分用,最后到服务器的应用层处理请求并计算响应。
3.2 Tomcat的执行逻辑
我们通过用伪代码的方式来演示Tomcat在Servlet程序运行中起到的作用。
1)Tomcat初始化流程
class Tomcat {
// 用来存储所有的 Servlet 对象
private List<Servlet> instanceList = new ArrayList<>();
public void start() {
// 根据约定,读取 WEB-INF/web.xml 配置文件;
// 并解析被 @WebServlet 注解修饰的类
// 假定这个数组里就包含了我们解析到的所有被 @WebServlet 注解修饰的类.
Class<Servlet>[] allServletClasses = ...;
// 这里要做的的是实例化出所有的 Servlet 对象出来;
for (Class<Servlet> cls : allServletClasses) {
// 这里是利用 java 中的反射特性做的
// 实际上还得涉及一个类的加载问题,因为我们的类字节码文件,是按照约定的
// 方式(全部在 WEB-INF/classes 文件夹下)存放的,所以 tomcat 内部是
// 实现了一个自定义的类加载器(ClassLoader)用来负责这部分工作。
Servlet ins = cls.newInstance();
instanceList.add(ins);
}
// 调用每个 Servlet 对象的 init() 方法,这个方法在对象的生命中只会被调用这一次;
for (Servlet ins : instanceList) {
ins.init();
}
// 利用我们之前学过的知识,启动一个 HTTP 服务器
// 并用线程池的方式分别处理每一个 Request
ServerSocket serverSocket = new ServerSocket(8080);
// 实际上 tomcat 不是用的固定线程池,这里只是为了说明情况
ExecuteService pool = Executors.newFixedThreadPool(100);
while (true) {
Socket socket = ServerSocket.accept();
// 每个请求都是用一个线程独立支持,这里体现了我们 Servlet 是运行在多线程环境下的
pool.execute(new Runnable() {
doHttpRequest(socket);//处理请求
});
}
// 调用每个 Servlet 对象的 destroy() 方法,这个方法在对象的生命中只会被调用这一次;
for (Servlet ins : instanceList) {
ins.destroy();
}
}
public static void main(String[] args) {
new Tomcat().start();
}
}
小结:
- Tomcat的代码中内置了main方法。当我们启动Tomcat的时候,就是从Tomcat的main方法开始执行的。
- 被
@WebServlet
注解修饰的类会在Tomcat启动的时候被获取到,并集中管理。 - Tomcat通过反射机制来创建被
@WebServlet
注解修饰的类的实例。 - 这些实例被创建完了之后,会调用其中的
init
方法进行初始化(该方法是HttpServlet自带的,我们自己写的类可以重写init) - 这些实例被销毁之前,会调用其中的
destory
方法进行收尾工作(只有正常退出Tomcat才会触发该方法,而通过直接杀死进程的方法是来不及调用该方法的) - Tomcat内部也是通过Socket API进行网络通信
- Tomcat为了能同时响应多个HTTP请求,采取了多线程的方式实现。因此Servlet是运行在多线程环境下的
2)Tomcat处理请求流程
class Tomcat {
void doHttpRequest(Socket socket) {
// 参照我们之前学习的 HTTP 服务器类似的原理,进行 HTTP 协议的请求解析,和响应构建
HttpServletRequest req = HttpServletRequest.parse(socket);
HttpServletRequest resp = HttpServletRequest.build(socket);
// 判断 URL 对应的文件是否可以直接在我们的根路径上找到对应的文件,如果找到,就是静态
// 直接使用我们学习过的 IO 进行内容输出
if (file.exists()) {
// 返回静态内容
return;
}
// 走到这里的逻辑都是动态内容了
// 根据我们在配置中说的,按照 URL -> servlet-name -> Servlet 对象的链条
// 最终找到要处理本次请求的 Servlet 对象
Servlet ins = findInstance(req.getURL());
// 调用 Servlet 对象的 service 方法
// 这里就会最终调用到我们自己写的 HttpServlet 的子类里的方法了
try {
ins.service(req, resp);
} catch (Exception e) {
// 返回 500 页面,表示服务器内部错误
}
}
}
小结:
- Tomcat从Socket中读取到HTTP请求,然后按照HTTP协议的格式解析成一个HttpServletRequest对象。
- Tomcat会根据URL中的path判定这个请求是请求一个静态资源还是动态资源;如果是静态资源,直接找到对应的文件把文件的内容通过Socket返回;如果是动态资源,才会执行到Servlet的相关逻辑。
- Tomcat会根据URL中的Context Path和Servlet Path确定要调用哪个Servlet实例的service方法(如果没有找到匹配的Servlet类,就会返回404)
- 通过service方法,就会进一步调用到我们之前写的doGet或者doPost
3)Servlet的service方法的实现
class Servlet {
public void service(HttpServletRequest req, HttpServletResponse resp) {
String method = req.getMethod();
if (method.equals("GET")) {
doGet(req, resp);
} else if (method.equals("POST")) {
doPost(req, resp);
} else if (method.equals("PUT")) {
doPut(req, resp);
} else if (method.equals("DELETE")) {
doDelete(req, resp);
}
......
}
}
小结:
- Servlet的service方法内部会根据当前请求的方法,决定调用其中的某个doXXX方法。
在整个流程中,有三个关键的方法:
- init方法:在初始化阶段执行,用来初始化每一个Servlet对象,对象创建好之后就会执行到。用户可以重写这个方法,来执行一些初始化逻辑。
- service方法:在处理请求阶段来调用,每个请求都要调用一次service
- destory方法:退出主循环,tomcat结束之前会调用,用来释放资源。
⭐️最后的话⭐️
总结不易,希望uu们不要吝啬你们的👍哟(^U^)ノ~YO!!如有问题,欢迎评论区批评指正😁