由各种组件构成,结构比较复杂,可以了解一下它的基本结构及各组件的作用。
server.xml 中的配置内容的结构与 tomcat 的组件结构保持一致,相关的激活配置项目与 tomcat 的运行紧密相关,不可随意修改。
tomcat 的设计模式:观察者模式;外观模式;命令模式;模板模式。
tomcat 程序的基本结构:
HTTP 协议的工作模型
什么是 http 协议的请求
http 请求包含的内容
(1)请求行:请求方式(GET/POST),请求的 uri(资源路径),使用的协议及版本。
(2)请求头:多个键值对,是客户需要告诉服务器的相关内容。
(3)请求正文(不是必须的)。
请求方式:http1.1 支持八种请求方式
http 响应包含的内容
(1)响应状态行:协议名;状态码;状态短语
(2)响应消息头
(3)响应正文
说明 | |
---|---|
200 | OK //客户端请求成功,即处理成功,这是我们最希望看到的结果 |
303 | Other //我把你redirect到其它的页面,目标的URL通过响应报文头的Location告诉你 |
304 | Not Modified //告诉客户端,你请求的这个资源至你上次取得后,并没有更改,你直接用你本地的缓存吧,我很忙哦,你能不能少来烦我啊! |
400 | Bad Request //客户端请求有语法错误,不能被服务器所理解 |
401 | Unauthorized //请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用 |
403 | Forbidden //服务器收到请求,但是拒绝提供服务 |
404 | Not Found //请求资源不存在,eg:输入了错误的URL,找不到页面/页面已经被网站删除了 |
500 | Internal Server Error //服务器发生不可预期的错误,此时应该查查服务端的日志了,肯定抛出了一堆异常 |
503 | Server Unavailable //服务器当前不能处理客户端的请求,一段时间后可能恢复正常 |
状态码 | 说明 通俗解释 |
---|---|
1xx | 响应中——表示请求已经接受,继续处理 消息:一般是告诉客户端请求已经收到了,正在处理,别急 |
2xx | 成功——表示请求已经被成功接收、理解、接受。 处理成功:一般表示请求收悉、我明白你要的、请求已受理、已经处理完成等信息 |
3xx | 重定向——要完成请求必须进行更进一步的操作 重定向到其它地方:它让客户端再发起一个请求以完成整个处理 |
4xx | 客户端错误——请求有语法错误或请求无法实现 处理发生错误,责任在客户端:如客户端的请求一个不存在的资源,客户端未被授权,禁止访问等 |
5xx | 服务器端错误——服务器未能实现合法的请求 "处理发生错误,责任在服务端:如服务端抛出异常,路由出错,HTTP版本不支持等 |
tomcat 是基于组件化的结构,由多个组件组成整个 server。包括:
以上提到的所有部分都属于组件,tomcaat 为了统一的有序的管理和监听这些组件的生命周期,它们都具有相似的层级结构。
结合源码了解各组件的启动过程
class Bootstrap 是 tomcat 的启动类,包含 main() 方法。bootstrap.init();对自己进行初始化。
startupInstance = startupClass.getConstructor().newInstance();类型是 org.apache.catalina.startup.Catalina,用成员变量 catalinaDaemon 引用。
main 方法中调用:daemon.load(args);daemon.start();这两个方法就是所有组件的初始化和启动的方法。
bootstrap#aeqnon.load(args);
catalinaDaemon.getClass().getMethod( "load”, paramTypes);
catalina 的 load() 方法:protected String configFile = “conf/server.xml”;//解析配置文件
getServer().init();//得到 server 组件,进行初始化
server.init();//初始化服务器,在 init() 方法内部调用的是 initInternal() 方法。
service.init();
executor.init();//初始化线程池
connector.init();//初始化连接组件
……所有的组件按照层次关系,由上级调用下级的初始化方法完成初始化。
daemon.start();
getServer().start();//启动server
……所有的组件按照层次关系,由上级调用下级的 start() 方法进行启动。
tomcat 是如何工作的:主要是接收请求,处理后发出响应。
(1)protocolHandler.start(); //启动对8080端口的请求监听,serverSocked. accept);
(2)当请求到达,解析http连接的内容.
(3)创建请求对象Request
public Request createRequest () {
Request request = new Request ();
request. setConnector (this); return (request);
}
(4)创建响应对象
public Response oreateResponse () {
Response response = new Response();
response.setConnector (this);
return (response);
}
(5)把请求和响应对象发送给 engine,按照顺序向后传递,每个节点都会做出一些处理,一直到 wrapper,把请求和响应对象交给 servlet,由 servlet 来使用。此过程采用的设计模式就是责任链模式。
java 设计模式之责任链模式(responsibility):属于行为型模式
package com.zhong.test_13;
/**
* @author 华韵流风
*/
public interface Process {
//表示处理的接口
void execute(Request request, ProcessChain processChain);
}
package com.zhong.test_13;
/**
* @author 华韵流风
*/
public class Request {
private String content;//请求的内容
public Request(String content) {
this.content = content;
}
@Override
public String toString() {
return "Request{" +
"请求内容='" + content + '\'' +
'}';
}
}
package com.zhong.test_13;
import java.util.ArrayList;
import java.util.List;
/**
* @author 华韵流风
* 包含所有的责任链上的处理对象,以及处理的顺序
*/
public class ProcessChain {
private int index;//处理的顺序
private List<Process> processList = new ArrayList<>();//包含所有的处理对象
//添加处理对象的方法
public ProcessChain addProcess(Process process) {
processList.add(process);
return this;
}
//启动责任链的处理方法,所有的处理都能够按顺序完成
public void doProcess(Request request) {
if (index == processList.size()) {
return;
}
processList.get(index).execute(request, this);
index++;
doProcess(request);
}
}
package com.zhong.test_13;
/**
* @author 华韵流风
*/
public class ParseRequestHeader implements Process {
@Override
public void execute(Request request, ProcessChain processChain) {
System.out.println("解析" + request + "的请求头");
}
}
package com.zhong.test_13;
/**
* @author 华韵流风
*/
public class ParseRequestLine implements Process {
@Override
public void execute(Request request, ProcessChain processChain) {
System.out.println("解析" + request + "的请求行");
}
}
package com.zhong.test_13;
/**
* @author 华韵流风
*/
public class ResponsibilityDemo {
public static void main(String[] args) {
Request request = new Request("查询用户");
Process lines = new ParseRequestLine();
Process header = new ParseRequestHeader();
ProcessChain chain = new ProcessChain();
//加入处理对象
chain.addProcess(lines).addProcess(header);
//执行整个责任链
chain.doProcess(request);
/*
*结果
*解析Request{请求内容='查询用户'}的请求行
*解析Request{请求内容='查询用户'}的请求头
*/
}
}
在 webapps 下面创建一个项目目录名称为 myproject,在根目录下放置安全目录,在安全目录下添加 classes 目录和 web.xml 配置文件。
创建 Servlet
(1)创建一个 java 工程,把 servlet.api.jar 引入到本工程中。
(2)创建一个类实现 Servlet 接口,实现所有的方法。
(3)在以上类的 service 方法中向客户端输出一句话。
(4)编译以上 Servlet 类。
(5)把 Servlet 的字节码文件按照包名在 classes 下创建相应的目录,复制文件到该目录下。
(6)在 web.xml 文件中,对刚创建的 Servlet 进行配置。
(7)启动 tomcat,本项目就被发布,外部就可以访问它。
(8)通过浏览器地址栏发出 get 请求,请求的资源就是上面的 Servlet。
(9)Servlet 执行完毕,向浏览器发回响应消息。
由 servlet 处理请求的工作过程
(1)发出的 URL :Request URL: http://localhost:8080/myproject/myservleturl。
(2)http 表示使用 http 协议,localhost 表示访问本机,8080表示访问本机工作在8080端口的进程(tomcat)。
(3)tomcat 接收到请求后解析请求行,myservlet 表示当前请求要访问的项目名称,myservleturl 表示当前请求需要哪个 servlet 进行处理。
(4)在配置文件中,servlet 的 servlet-mapping 下面的 url-pattern 的内容是 myservleturl,它对应 servlet-name。
(5)容器就依据以上的配置信息,根据 servlet-name 得到 servlet-class 的字节码文件名,首先加载 servlet 的字节码,再通过反射创建 servlet 的对象,把请求和响应对象通过 service 方法传递给 servlet,在 servlet 方法中就可以使用以上两个对象。
(6)通过字节码对象调用 service 方法,service 就执行了。它需要利用响应对象得到字节输出流对象,利用字节流对象发送信息。
(7)service 方法执行完成后就向客户的浏览器发回响应消息。
什么是 servlet
注意,在使用 Servlet 之前,要针对它在 web.xml 中进行配置。配置是因为 web 程序是没有 main() 方法的。所以 servlet 的对象要交给容器进行加载并实例化。配置信息明确的告诉了容器 servlet 的字节码是谁,交给哪个请求进行使用。
<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_4_0.xsd"
version="4.0"
metadata-complete="true">
<servlet>
<servlet-name>myservletservlet-name>
<servlet-class>com.zhong.MyServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>myservletservlet-name>
<url-pattern>/myservleturlurl-pattern>
servlet-mapping>
web-app>
通过 idea 平台来创建 JavaWeb 应用程序。(创建步骤)
Servlet 接口:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package javax.servlet;
import java.io.IOException;
public interface Servlet {
void init(ServletConfig var1) throws ServletException;
ServletConfig getServletConfig();
void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
String getServletInfo();
void destroy();
}
其中的三个 void 方法是生命周期方法,在 Servlet 的创建,使用及销毁过程会被调用。其中 service() 方法就是处理请求的方法。其中的请求和响应对象的类型,属于底层的对象类型。
GenericServlet 实现了 Servlet 接口,也实现了 ServletConfig 接口,在实现以上接口的方法的同时,它还添加了自己的方法。
HttpServlet 的主要方法的作用。
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest)req;
response = (HttpServletResponse)res;
} catch (ClassCastException var6) {
throw new ServletException(lStrings.getString("http.non_http"));
}
this.service(request, response);
}
该方法把底层的请求和响应对象强转为 HttpServletRequest 和 HttpServletResponse 对象,ServletRequest 和 HttpServletRequest 都是接口,后者的功能更加强大,它新增了一些与 http 协议相关的功能。protected void service(HttpServletRequest req, HttpServletResponse resp) 方法是本类自己的方法,该方法依据不同的请求方式,分别调用该类所添加的针对不同请求方式的处理方法。
自定义的 Servlet 方法