首页 > 其他 > 详细

How Tomcat Works 2

时间:2015-03-17 23:44:39      阅读:328      评论:0      收藏:0      [点我收藏+]

上一节(How Tomcat Works 1 编写一个简单静态web服务器)编写了一个简单的web服务器,只能处理静态的资源,本节将继续向前迈出一个小步,创建两个不同的servlet容器,能够利用servlet简单的处理动态内容。注意每节的代码都是基于上一节的继续丰富,因此有必要从第一节开始看起。

在编写代码之前,需要先大体了解一下Servlet是什么,方便后面的理解,下面就是一个最简单的Servlet什么也没做:

package prymont;

import java.io.IOException;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
 * 每个Servlet都实现Servlet接口
 * 该接口有5个方法需要实现,该Servlet只是为了让大家对
 * Servlet有个整体的印象,因此所有方法都未实现。
 */
public class PrimitiveServlet implements Servlet{

	//当servlet容器正在被关闭或者servlet容器内存不够的时候,该方法由servlet容器调用,且只调用一次
	//该方法通常用来清除资源。
	@Override
	public void destroy() {
		
	}

	@Override
	public ServletConfig getServletConfig() {
		return null;
	}

	@Override
	public String getServletInfo() {
		return null;
	}

	//当servlet已经初始化的时候,该方法由servlet容器调用,且只调用一次
	//该方法适合做一些一次性的加载动作,比如数据库驱动等。
	@Override
	public void init(ServletConfig arg0) throws ServletException {
		
	}
	
	//servlet容器负责为每一次请求调用一次service方法,并且传递一个ServletRequest(封装客户端请求)
	//和ServletResponse(封装响应)对象。
	@Override
	public void service(ServletRequest arg0, ServletResponse arg1)
			throws ServletException, IOException {
		
	}

}

先来说说我们的第一个版本的Servlet容器要实现的功能:等待Http请求,如果是请求静态资源则交给静态资源处理器,如果是请求动态资源则加载相应的Servlet并调用它的service方法,同时传递ServletRequest和ServletResponse对象。(第一个版本每次请求servlet类都被加载)

本版本针对上次主要新增StaticResourceProcessor和ServletProcessor1两个类分别处理静态资源和动态资源,同时Request和Response对象也各自集成了

http://machineName:port/staticResource请求的是一个静态资源,http://localhost:8080/servlet/PrimitiveServlet请求的则是一个动态的资源,因此对于HttpServer1只需要稍稍改动一下即可满足需求。

StaticResourceProcessor只是简单的调用了response的sendStaticResource()方法,也没有可讲的,下面重点讲解一下ServletProcessor1的实现:

package server1;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandler;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
 * 完成servlet类的动态加载
 * 并调用servlet的service的方法
 *
 */
public class ServletProcessor1 {
 /**
  * 加载Servlet
  * 
  * @param request
  * @param response
  */
 public void process(Request request, Response response) {
  // /servlet/servetclass
  String uri = request.getUri();
  // 截取servlet名字
  String servletName = uri.substring(uri.lastIndexOf("/") + 1);
  // jdk提供的URL类加载器,根据URL加载class
  URLClassLoader loader = null;
  URL[] urls = new URL[1];
  // 指定加载webapp下所有的class文件
  File classPath = new File(Constant.WEB_APP);
  URLStreamHandler streamHandler = null;
  try {
   // 使用file协议从本机的classPath加载class
   // getCanonicalPath返回绝对路径(不带.)
   String repository = new URL("file", null,
     classPath.getCanonicalPath() + File.separator).toString();
   urls[0] = new URL(null, repository, streamHandler);
  } catch (MalformedURLException e) {
   e.printStackTrace();
  } catch (IOException e) {
   e.printStackTrace();
  }
  loader = new URLClassLoader(urls);
  Class clazz = null;
  try {
   // 根据servlet名字加载class,这里对名字的处理并不完善,实际需要根据包名做拼接
   //为了简单servlet类全不不带包名
   clazz = loader.loadClass(servletName);
  } catch (ClassNotFoundException e) {
   e.printStackTrace();
  }
  Servlet servlet = null;
  try {
   // 反射创建实例
   servlet = (Servlet) clazz.newInstance();
   // 向下转型调用service方法
   servlet.service((ServletRequest) request,
     (ServletResponse) response);
  } catch (InstantiationException e) {
   e.printStackTrace();
  } catch (IllegalAccessException e) {
   e.printStackTrace();
  } catch (ServletException e) {
   e.printStackTrace();
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
}
上面就是处理Servlet的整个过程,可以通过http://localhost:8080/servlet/PrimitiveServlet来访问我们的servlet。

我们的第一个程序有一个严重的问题,就是当传递给servlet的service方法的时候将Request和Response对象都向上转型了,这样如果知道内部实现的人,则可以在写自己的Servlet的时候向下转型成Request和Response对象,并调用他们的方法,实际上这两个对象是容器私有的不应该暴露给开发者(具体是有些方法不能让开发者使用),有一个方法是让这两个类使用默认的包访问权限,其实有一个更优雅的实现方式就是门面模式。

在本节的第二版中增加两个类,RequestFacade和ResponseFacade用来控制某些方法的可见性。具体的方式是给RequestFacade提供一个带参构造函数,参数类型为Request对象,门面类封装servletrequest方法,其实只是传递给Request对象实现,这样在调用service方法时传递的facade对象,这样即使通过向下转型得到的也是被封装过的Facade对象。RequestFacade对象片段如下:

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);
  }
ResponseFacade对象类似,不贴出来代码了。

还有一点不同的是ServletProcessor1的部分处理,具体变化的如下:

Servlet servlet = null;
  RequestFacade requestFacade = new RequestFacade(request);
  ResponseFacade responseFacade = new ResponseFacade(response);
  try {
   // 反射创建实例
   servlet = (Servlet) clazz.newInstance();
   // 向下转型调用service方法
   servlet.service((ServletRequest) requestFacade,
     (ServletResponse) responseFacade);
  } catch (InstantiationException e) {
   e.printStackTrace();
  } catch (IllegalAccessException e) {
   e.printStackTrace();
  } catch (ServletException e) {
   e.printStackTrace();
  } catch (IOException e) {
   e.printStackTrace();
  }
通过Request对象构造RequestFacade对象并向上转型传递给service方法。

以上就是本节实现的一个简单的servlet容器。下一节将会接触到tomcat中一个非常重要的概念——连接器


How Tomcat Works 2

原文:http://blog.csdn.net/tangyongzhe/article/details/9004165

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!