tomcat源码系列文章开始!!(不是为了说教,市面上讲tomcat的很多,我只是做笔记而已。。。)
虽然是tomcat源码系列的第一篇,但是其实是从第三章开始写的,原因嘛。。。因为我愿意这么写。。。
第三章源码分析:
主要类有以下几个:
1.启动类Bootstrap.class
2.连接器相关类HttpConnector.class和HttpProcessor.class
3.HTTP请求相关类HttpRequest.class,HttpResponse.class(以及两个facade类,主要是出于安全考虑,封装了组件内部的通信部分,只暴漏必要的部分)
4.请求处理类:静态资源处理类StaticResourceProcessor.class和动态资源处理类ServletProcessor.class
启动和处理请求流程如下:
1.首先Bootstrap启动类创建一个HttpConnector实例,该实例的类是一个实现了多线程接口的类,通过启动额外线程来监听相应地址和端口,并且阻塞等待请求到来:
while (!stopped) {
// Accept the next incoming connection from the server socket
Socket socket = null;
try {
socket = serverSocket.accept();
}
catch (Exception e) {
continue;
}
// Hand this socket off to an HttpProcessor
HttpProcessor processor = new HttpProcessor(this);
processor.process(socket);
}
以上代码通过一个while循环来接受请求,这几乎是所有web服务器的实现方式(在一个循环中阻塞等待请求),每次接收到一个请求都会创建一个HttpProcessor实例来处理请求,该类的职责就是解析http请求并且创建和填充HttpRequest和HttpResponse对象,注意这个时候只是把http请求解析到了Httprequest中,具体请求参数等步骤并没有做,而是等到具体servlet实例调用getParamter()等方法的时候才会去解析,这样的好处就是不做不必要的解析,在需要的时候再解析,减少了CPU等开销。代码如下:
input = new SocketInputStream(socket.getInputStream(), 2048);
output = socket.getOutputStream();
// create HttpRequest object and parse
request = new HttpRequest(input);
// create HttpResponse object
response = new HttpResponse(output);
response.setRequest(request);
response.setHeader("Server", "Pyrmont Servlet Container");
parseRequest(input, output);
parseHeaders(input);
接下来的一步就是根据请求的类型将请求传递给相应的动/静态处理器来处理:
//check if this is a request for a servlet or a static resource
//a request for a servlet begins with "/servlet/"
if (request.getRequestURI().startsWith("/servlet/")) {
ServletProcessor processor = new ServletProcessor();
processor.process(request, response);
}
else {
StaticResourceProcessor processor = new StaticResourceProcessor();
processor.process(request, response);
}
静态处理器就不说了,这里再说一下动态(servlet)处理器部分的处理步骤:
1.首先通过解析url识别出请求的servlet名字
String uri = request.getRequestURI();
String servletName = uri.substring(uri.lastIndexOf("/") + 1);
2.然后创建一个类加载器,并且加载请求的servlet
URLClassLoader loader = null;
try {
// create a URLClassLoader
URL[] urls = new URL[1];
URLStreamHandler streamHandler = null;
File classPath = new File(Constants.WEB_ROOT);
String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ;
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());
}
3.实例化servlet,调用service方法处理request(注意这里传入的是facade类)
servlet = (Servlet) myClass.newInstance();
HttpRequestFacade requestFacade = new HttpRequestFacade(request);
HttpResponseFacade responseFacade = new HttpResponseFacade(response);
servlet.service(requestFacade, responseFacade);
4.最后返回结果,结束请求
这里再次提一下,只有在加载的servlet方法中调用了request.getParameters()等方法,才会解析请求内容,并且只会解析一次。
解析步骤可以在HttpRequest类中找到,是调用了 parseParameters()方法
ok,基本上一个请求流程就是这样了,另外HttpRequest类中的很多方法值得细看,特别是一些同步的部分