《深入剖析Tomcat》阅读(二)
Tomcat是基于Sun公司标准的开源Servlet容器。
Servlet是什么?
Servlet(Server Applet),全称Java Servlet,未有中文译文。是用Java编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态Web内容。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。
Servlet运行于支持Java的应用服务器中。从实现上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。
最早支持Servlet标准的是JavaSoft的Java Web Server。此后,一些其它的基于Java的Web服务器开始支持标准的Servlet。
Servlet编程需要使用到javax.servlet 和 javax.servlet.http两个包下面的类和接口,在所有的类和接口中,javax.servlet.servlet 接口最为重要。所有的servlet程序都必须实现该接口或者继承实现了该接口的类。
Servlet只是一种标准,按照这种标准既可以实现HTTP请求也可以实现其他的协议,用于执行Servlet的启动和管理程序称为Servlet容器,Tomcat便是一个很经常使用的Servlet容器。
上面一小节介绍了一个简单的HTTP的web服务器,是单线程的只能处理单个请求,请求处理完毕之后就重新开始准备接受连接的web服务器。
没有用到Servlet标准,只是简单的定义了Request和Response,简单解析了url以及将静态的文本文件通过输出流输出,并且我们发现response并没有http报文头,浏览器应该是针对这种情况做了特殊处理,比如直接打印流中的内容。
------------------------------------------------------------------------------------
一个功能齐全的Servlet容器有以下几件事情
1)第一次调用某个Servlet时,要载入该Servlet,并调用其init()方法,并且仅此一次
------------------------------------------------------------
下面举一个很简单的Servlet容器的例子
Constants.java
import java.io.File;
//定义了访问静态资源的位置
public class Constants
{
public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot";
}
Constants类定义了静态finla WEB_ROOT,WEB_ROOT指定了该Servlet容器中使用PrimitiveServlet类和静态资源的位置。
HttpServer1一直等待HTTP请求,并且是单线程处理HTTP请求,直到等待到shut命令
与一种不同的是,此时将会在HttpServer1当中解析url,通过url判断寻找的是Servlet对象还是静态资源,然后分发到不同的处理过程。
都分别架构Request对象和Response对象传入
ServletProcessor1 和
StaticResourceProcessor的process方法当中,在方法当中回调Request和Response的相关方法
HttpServer1.java
import java.net.Socket;
import java.net.ServerSocket;
import java.net.InetAddress;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException; public class HttpServer1
{ /**
* WEB_ROOT is the directory where our HTML and other files reside. For this
* package, WEB_ROOT is the "webroot" directory under the working directory.
* The working directory is the location in the file system from where the
* java command was invoked.
*/
// shutdown command
private static final String SHUTDOWN_COMMAND = "/SHUTDOWN"; // the shutdown command received
private boolean shutdown = false; public static void main(String[] args)
{
HttpServer1 server = new HttpServer1();
server.await();
} public void await()
{
ServerSocket serverSocket = null;
int port = 8080;
try
{
serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
} catch (IOException e)
{
e.printStackTrace();
System.exit(1);
} // Loop waiting for a request
while (!shutdown)
{
Socket socket = null;
InputStream input = null;
OutputStream output = null;
try
{
socket = serverSocket.accept();
input = socket.getInputStream();
output = socket.getOutputStream(); // create Request object and parse
Request request = new Request(input);
request.parse(); // create Response object
Response response = new Response(output);
response.setRequest(request); // check if this is a request for a servlet or a static resource
// a request for a servlet begins with "/servlet/"
if (request.getUri().startsWith("/servlet/"))
{
ServletProcessor1 processor = new ServletProcessor1();
processor.process(request, response);
} else
{
StaticResourceProcessor processor = new StaticResourceProcessor();
processor.process(request, response);
} // Close the socket
socket.close();
// check if the previous URI is a shutdown command
shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
} catch (Exception e)
{
e.printStackTrace();
System.exit(1);
}
}
}
}
Request.java
import java.io.InputStream;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map; import javax.servlet.AsyncContext;
import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; public class Request implements ServletRequest
{ private InputStream input;
private String uri; public Request(InputStream input)
{
this.input = input;
} public String getUri()
{
return uri;
} private String parseUri(String requestString)
{
int index1, index2;
index1 = requestString.indexOf(' ');
if (index1 != -1)
{
index2 = requestString.indexOf(' ', index1 + 1);
if (index2 > index1)
return requestString.substring(index1 + 1, index2);
}
return null;
} public void parse()
{
// Read a set of characters from the socket
StringBuffer request = new StringBuffer(2048);
int i;
byte[] buffer = new byte[2048];
try
{
i = input.read(buffer);
} catch (IOException e)
{
e.printStackTrace();
i = -1;
}
for (int j = 0; j < i; j++)
{
request.append((char) buffer[j]);
}
System.out.print(request.toString());
uri = parseUri(request.toString());
} /* implementation of the ServletRequest */
public Object getAttribute(String attribute)
{
return null;
} public Enumeration getAttributeNames()
{
return null;
} public String getRealPath(String path)
{
return null;
} public RequestDispatcher getRequestDispatcher(String path)
{
return null;
} public boolean isSecure()
{
return false;
} public String getCharacterEncoding()
{
return null;
} public int getContentLength()
{
return 0;
} public String getContentType()
{
return null;
} public ServletInputStream getInputStream() throws IOException
{
return null;
} public Locale getLocale()
{
return null;
} public Enumeration getLocales()
{
return null;
} public String getParameter(String name)
{
return null;
} public Map getParameterMap()
{
return null;
} public Enumeration getParameterNames()
{
return null;
} public String[] getParameterValues(String parameter)
{
return null;
} public String getProtocol()
{
return null;
} public BufferedReader getReader() throws IOException
{
return null;
} public String getRemoteAddr()
{
return null;
} public String getRemoteHost()
{
return null;
} public String getScheme()
{
return null;
} public String getServerName()
{
return null;
} public int getServerPort()
{
return 0;
} public void removeAttribute(String attribute)
{
} public void setAttribute(String key, Object value)
{
} public void setCharacterEncoding(String encoding) throws UnsupportedEncodingException
{
} @Override
public int getRemotePort()
{
// TODO Auto-generated method stub
return 0;
} @Override
public String getLocalName()
{
// TODO Auto-generated method stub
return null;
} @Override
public String getLocalAddr()
{
// TODO Auto-generated method stub
return null;
} @Override
public int getLocalPort()
{
// TODO Auto-generated method stub
return 0;
} @Override
public ServletContext getServletContext()
{
// TODO Auto-generated method stub
return null;
} @Override
public AsyncContext startAsync()
{
// TODO Auto-generated method stub
return null;
} @Override
public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse)
{
// TODO Auto-generated method stub
return null;
} @Override
public boolean isAsyncStarted()
{
// TODO Auto-generated method stub
return false;
} @Override
public boolean isAsyncSupported()
{
// TODO Auto-generated method stub
return false;
} @Override
public AsyncContext getAsyncContext()
{
// TODO Auto-generated method stub
return null;
} @Override
public DispatcherType getDispatcherType()
{
// TODO Auto-generated method stub
return null;
} }
Response.java
import java.io.OutputStream;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.File;
import java.io.PrintWriter;
import java.util.Locale;
import javax.servlet.ServletResponse;
import javax.servlet.ServletOutputStream; public class Response implements ServletResponse
{ private static final int BUFFER_SIZE = 1024;
Request request;
OutputStream output;
PrintWriter writer; public Response(OutputStream output)
{
this.output = output;
} public void setRequest(Request request)
{
this.request = request;
} /* This method is used to serve a static page */
public void sendStaticResource() throws IOException
{
byte[] bytes = new byte[BUFFER_SIZE];
FileInputStream fis = null;
try
{
/* request.getUri has been replaced by request.getRequestURI */
File file = new File(Constants.WEB_ROOT, request.getUri());
fis = new FileInputStream(file);
/*
* HTTP Response = Status-Line(( general-header | response-header |
* entity-header ) CRLF) CRLF [ message-body ] Status-Line =
* HTTP-Version SP Status-Code SP Reason-Phrase CRLF
*/
int ch = fis.read(bytes, 0, BUFFER_SIZE);
while (ch != -1)
{
output.write(bytes, 0, ch);
ch = fis.read(bytes, 0, BUFFER_SIZE);
}
} catch (FileNotFoundException e)
{
String errorMessage = "HTTP/1.1 404 File Not Found\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 23\r\n" + "\r\n" + "<h1>File Not Found</h1>";
output.write(errorMessage.getBytes());
} finally
{
if (fis != null)
fis.close();
}
} /** implementation of ServletResponse */
public void flushBuffer() throws IOException
{
} public int getBufferSize()
{
return 0;
} public String getCharacterEncoding()
{
return null;
} public Locale getLocale()
{
return null;
} public ServletOutputStream getOutputStream() throws IOException
{
return null;
} public PrintWriter getWriter() throws IOException
{
// autoflush is true, println() will flush,
// but print() will not.
writer = new PrintWriter(output, true);
return writer;
} public boolean isCommitted()
{
return false;
} public void reset()
{
} public void resetBuffer()
{
} public void setBufferSize(int size)
{
} public void setContentLength(int length)
{
} public void setContentType(String type)
{
} public void setLocale(Locale locale)
{
} @Override
public String getContentType()
{
// TODO Auto-generated method stub
return null;
} @Override
public void setCharacterEncoding(String charset)
{
// TODO Auto-generated method stub }
}
ServletProcessor1.java
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandler;
import java.io.File;
import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; public class ServletProcessor1 { public void process(Request request, Response response) { String uri = request.getUri();
String servletName = uri.substring(uri.lastIndexOf("/") + 1);
URLClassLoader loader = null; try {
// create a URLClassLoader
URL[] urls = new URL[1];
URLStreamHandler streamHandler = null;
File classPath = new File(Constants.WEB_ROOT);
// the forming of repository is taken from the createClassLoader method in
// org.apache.catalina.startup.ClassLoaderFactory
String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ;
// the code for forming the URL is taken from the addRepository method in
// org.apache.catalina.loader.StandardClassLoader class.
urls[0] = new URL(null, repository, streamHandler);
loader = new URLClassLoader(urls);
}
catch (IOException e) {
System.out.println(e.toString() );
}
Class myClass = null;
try {
myClass = loader.loadClass(servletName);
}
catch (ClassNotFoundException e) {
System.out.println(e.toString());
} Servlet servlet = null; try {
servlet = (Servlet) myClass.newInstance();
servlet.service((ServletRequest) request, (ServletResponse) response);
}
catch (Exception e) {
System.out.println(e.toString());
}
catch (Throwable e) {
System.out.println(e.toString());
} }
}
import java.io.IOException; public class StaticResourceProcessor { public void process(Request request, Response response) {
try {
response.sendStaticResource();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
在静态资源的访问过程中,静态资源的的outpu过程放在了Response当中,其实是不合理的,应该放在Processor当中
---------------------------------------------------
private String parseUri(String requestString)
{
int index1, index2;
index1 = requestString.indexOf(' ');
if (index1 != -1)
{
index2 = requestString.indexOf(' ', index1 + 1);
if (index2 > index1)
return requestString.substring(index1 + 1, index2);
}
return null;
}
Request类的parseURI的过程
利用了HTTP请求报文中的GET uri HTTP/1.1的规范找到第一行中间的uri字段
该资源是host后面的部分
比如www.baidu.com/guowuxin
则该字段便是/guowuxin
----------------------------------------------------------------------------------------------------------
下面讲一个这个程序的缺点
1:编程的OOP可能被破坏,封装性,传送给Servlet的ServletRequest对象本身应该
只能使用ServletRequest对象的相关方法,但是由于本身是子类Request对象,
所以如果认为进行强转,也是可以使用子类的方法的,破坏了封装性质,
不过这个问题可能出现在很多情景下,并不是一定要处理的额。
使用外观模式将这个类包起来就可以解决这个问题,
包接口的方法很类似于适配器模式,但是二者是不同的,
适配器模式是由于参数个数的不同或者方法名的不同而进行适配的,
这里是为了进一步的封装子类的方法。
package ex02.pyrmont; import java.io.IOException;
import java.io.BufferedReader;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest; public class RequestFacade implements ServletRequest { private ServletRequest request = null; public RequestFacade(Request request) {
this.request = request;
} /* implementation of the ServletRequest*/
public Object getAttribute(String attribute) {
return request.getAttribute(attribute);
} public Enumeration getAttributeNames() {
return request.getAttributeNames();
} public String getRealPath(String path) {
return request.getRealPath(path);
} public RequestDispatcher getRequestDispatcher(String path) {
return request.getRequestDispatcher(path);
} public boolean isSecure() {
return request.isSecure();
} public String getCharacterEncoding() {
return request.getCharacterEncoding();
} public int getContentLength() {
return request.getContentLength();
} public String getContentType() {
return request.getContentType();
} public ServletInputStream getInputStream() throws IOException {
return request.getInputStream();
} public Locale getLocale() {
return request.getLocale();
} public Enumeration getLocales() {
return request.getLocales();
} public String getParameter(String name) {
return request.getParameter(name);
} public Map getParameterMap() {
return request.getParameterMap();
} public Enumeration getParameterNames() {
return request.getParameterNames();
} public String[] getParameterValues(String parameter) {
return request.getParameterValues(parameter);
} public String getProtocol() {
return request.getProtocol();
} public BufferedReader getReader() throws IOException {
return request.getReader();
} public String getRemoteAddr() {
return request.getRemoteAddr();
} public String getRemoteHost() {
return request.getRemoteHost();
} public String getScheme() {
return request.getScheme();
} public String getServerName() {
return request.getServerName();
} public int getServerPort() {
return request.getServerPort();
} public void removeAttribute(String attribute) {
request.removeAttribute(attribute);
} public void setAttribute(String key, Object value) {
request.setAttribute(key, value);
} public void setCharacterEncoding(String encoding)
throws UnsupportedEncodingException {
request.setCharacterEncoding(encoding);
} }
package ex02.pyrmont; import java.io.IOException;
import java.io.PrintWriter;
import java.util.Locale;
import javax.servlet.ServletResponse;
import javax.servlet.ServletOutputStream; public class ResponseFacade implements ServletResponse { private ServletResponse response;
public ResponseFacade(Response response) {
this.response = response;
} public void flushBuffer() throws IOException {
response.flushBuffer();
} public int getBufferSize() {
return response.getBufferSize();
} public String getCharacterEncoding() {
return response.getCharacterEncoding();
} public Locale getLocale() {
return response.getLocale();
} public ServletOutputStream getOutputStream() throws IOException {
return response.getOutputStream();
} public PrintWriter getWriter() throws IOException {
return response.getWriter();
} public boolean isCommitted() {
return response.isCommitted();
} public void reset() {
response.reset();
} public void resetBuffer() {
response.resetBuffer();
} public void setBufferSize(int size) {
response.setBufferSize(size);
} public void setContentLength(int length) {
response.setContentLength(length);
} public void setContentType(String type) {
response.setContentType(type);
} public void setLocale(Locale locale) {
response.setLocale(locale);
} }
package ex02.pyrmont; import java.net.Socket;
import java.net.ServerSocket;
import java.net.InetAddress;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException; public class HttpServer2 { // shutdown command
private static final String SHUTDOWN_COMMAND = "/SHUTDOWN"; // the shutdown command received
private boolean shutdown = false; public static void main(String[] args) {
HttpServer2 server = new HttpServer2();
server.await();
} public void await() {
ServerSocket serverSocket = null;
int port = 8080;
try {
serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
}
catch (IOException e) {
e.printStackTrace();
System.exit(1);
} // Loop waiting for a request
while (!shutdown) {
Socket socket = null;
InputStream input = null;
OutputStream output = null;
try {
socket = serverSocket.accept();
input = socket.getInputStream();
output = socket.getOutputStream(); // create Request object and parse
Request request = new Request(input);
request.parse(); // create Response object
Response response = new Response(output);
response.setRequest(request); //check if this is a request for a servlet or a static resource
//a request for a servlet begins with "/servlet/"
if (request.getUri().startsWith("/servlet/")) {
ServletProcessor2 processor = new ServletProcessor2();
processor.process(request, response);
}
else {
StaticResourceProcessor processor = new StaticResourceProcessor();
processor.process(request, response);
} // Close the socket
socket.close();
//check if the previous URI is a shutdown command
shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
}
catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
}
}
package ex02.pyrmont; import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandler;
import java.io.File;
import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; public class ServletProcessor2 { public void process(Request request, Response response) { String uri = request.getUri();
String servletName = uri.substring(uri.lastIndexOf("/") + 1);
URLClassLoader loader = null; try {
// create a URLClassLoader
URL[] urls = new URL[1];
URLStreamHandler streamHandler = null;
File classPath = new File(Constants.WEB_ROOT);
// the forming of repository is taken from the createClassLoader method in
// org.apache.catalina.startup.ClassLoaderFactory
String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ;
// the code for forming the URL is taken from the addRepository method in
// org.apache.catalina.loader.StandardClassLoader class.
urls[0] = new URL(null, repository, streamHandler);
loader = new URLClassLoader(urls);
}
catch (IOException e) {
System.out.println(e.toString() );
}
Class myClass = null;
try {
myClass = loader.loadClass(servletName);
}
catch (ClassNotFoundException e) {
System.out.println(e.toString());
} Servlet servlet = null;
RequestFacade requestFacade = new RequestFacade(request);
ResponseFacade responseFacade = new ResponseFacade(response);
try {
servlet = (Servlet) myClass.newInstance();
servlet.service((ServletRequest) requestFacade, (ServletResponse) responseFacade);
}
catch (Exception e) {
System.out.println(e.toString());
}
catch (Throwable e) {
System.out.println(e.toString());
} }
}
《深入剖析Tomcat》阅读(二)的更多相关文章
- 07.ElementUI 2.X 源码学习:源码剖析之工程化(二)
0x.00 前言 项目工程化系列文章链接如下,推荐按照顺序阅读文章 . 1️⃣ 源码剖析之工程化(一):项目概览.package.json.npm script 2️⃣ 源码剖析之工程化(二):项目构 ...
- 深入剖析tomcat 笔记——第8章 载入器
深入剖析tomcat 笔记 目录:
- WCF技术剖析之十二:数据契约(Data Contract)和数据契约序列化器(DataContractSerializer)
原文:WCF技术剖析之十二:数据契约(Data Contract)和数据契约序列化器(DataContractSerializer) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济 ...
- 深入剖析Tomcat会话机制
1缓存机制 Tomcat默认将Session保存到内存中.但同时,Tomcat也提供了PersistentManager配合不同的Store实现的方式,使Session可以被保存到不同地方(Datab ...
- 深入剖析TOMCAT
理解tomcat之搭建简易http服务器 做过java web的同学都对tomcat非常熟悉.我们在使用tomcat带来的便利的同时,是否想过tomcat是如何工作的呢?tomcat本质是一个http ...
- web服务器专题:tomcat(二)模块组件与server.xml 配置文件
web服务器专题:tomcat(二)模块组件与server.xml 配置文件 回顾: Web服务器专题:tomcat(一) 基础模块 一个Server.xml的实例 <?xml version= ...
- 《深入剖析Tomcat》源码
<深入剖析Tomcat>翻译自<How Tomcat Works> 可以到官网下载:https://brainysoftware.com/download 官网下载比较慢,我就 ...
- 《深入剖析Tomcat》读书笔记(二)
三.容器Container Container 是容器的父接口,所有子容器都必须实现这个接口.Container 容器的设计用的是典型的责任链的设计模式,它有四个子容器组件构成,分别是:Engine. ...
- 《深入剖析Tomcat》阅读(三)
这里要介绍下Tomcat的一个重要设计方法,Catalina设计方式. Servlet容器是一个复杂系统,但是,它有三个基本任务,对每个请求,servlet容器会为其完成以下三个操作: 1.创建一个R ...
随机推荐
- java.sql.SQLException: Before start of result set解决方法
java.sql.SQLException: Before start of result set解决方法 今天做东西的时候发现这个错误,查了查,特地记下来,以后开始积累了 哈哈 解决发法是: 使用r ...
- Python中的判断、循环 if...else,while
if...else语句: a=3; b=3; if a == b :print(a,b)elif a <= b :print(str(a) + " is less than " ...
- leetCode 70.Climbing Stairs (爬楼梯) 解题思路和方法
Climbing Stairs You are climbing a stair case. It takes n steps to reach to the top. Each time you ...
- Request的getParameter和getAttribute方法的区别
下面整理一下getParameter和getAttribute的区别和各自的使用范围. (1)HttpServletRequest类有setAttribute()方法,而没有setParam ...
- HDU1711-----Number Sequence-----裸的KMP
题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=1711 题目意思: 找出b在a中的起始位置,没有则是-1 解题思路: 裸的KMP,不多说 不会KMP的话 ...
- Android 获取运营商信息(完整版)-解决高通,MTK等双卡问题
由于国内的运营商问题,双卡手机获取IMSI号问题要根据厂商API 来实现. 下面我们就来做一套完整的分析运营商获取IMSI号逻辑. 1,首先我们要判断手机的平台. 1.1,判断手机是否MTK平台 1 ...
- Chapter 5 - How to Detect the Collisions
Chapter 5 - How to Detect the Collisions Our hero can fire bullets now, but the bullets are only vis ...
- Java基础知识强化之IO流笔记23:计算机是如何识别把两个字节拼接为中文(附加)
1. 计算机是如何识别什么时候该把两个字节转换为一个中文呢? 在计算机中中文的存储分两个字节: • 第一个字节肯定是负数. • 第二个字节常见的是负数,可能有正数.但是没影响. 2. 代码示例: pa ...
- 【bzoj1212】 [HNOI2004]L语言
题目描述 标点符号的出现晚于文字的出现,所以以前的语言都是没有标点的.现在你要处理的就是一段没有标点的文章. 一段文章T是由若干小写字母构成.一个单词W也是由若干小写字母构成.一个字典D是若干个单词的 ...
- gitcafe 使用hexo搭建博客
--缘由:因为看大家都用github等搭建博客,作为半个程序员的自己,也按捺不住了,终于有空来尝试一把了,选择了和github 相同功能的gitcafe网站,因为在国内比较快,这是大家的看法,下面写一 ...