实现一个简单的Servlet容器

来源:互联网 发布:知乎目标用户 编辑:程序博客网 时间:2024/06/01 19:45

Servlet规范定义了一个API标准,这一标准的实现通常称为Servlet容器,比如开源的Tomcat、JBoss。容器提供一些基本的服务,使得开发者专注于业务领域。当一个请求到达时,监听在相应端口的容器会接受该请求,将请求的信息转化成一个ServletRequest对象,同时创建一个ServletResponse对象,我们在开发Servlet应用时,就可以直接使用这两个对象(容器传递到service()方法的参数)。因此,所谓容器(服务器、中间件等),就是提供一些底层的、业务无关的基本功能,为真正的Servlet提供服务。

例如,在Http请求到达的时候,请求是一个标准的Http请求,包括请求方法、URI、协议、客户端环境参数、请求参数等等。容器通过Socket接收这些信息,并将其转化成特定于Servlet的ServletRequest对象。我们无需关注底层的网络Socket细节就可以直接使用。同样,容器也创建好一个ServletResponse,它帮我们设置了一些返回操作的基本内容,比如用于写入的OutputStream,容器已经把这个输出流定向到客户端的机器上,因此我们操作ServletResponse对象时,是在容器提供的服务至上的。

容器还负责根据请求的信息找到对应的Servlet,传递Request和Response参数,调用Servlet的service方法,完成请求的响应。


接下来我们事先一个简单的、最基本的容器:

时序图如下:



下面给出源码:

SimpleContainer:

package simpleserver.simplecontainer;import static simpleserver.simplecontainer.Constant.HOSTNAME;import static simpleserver.simplecontainer.Constant.LOG_BAKC;import static simpleserver.simplecontainer.Constant.PORT;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.InetAddress;import java.net.ServerSocket;import java.net.Socket;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;public class SimpleContainer {private boolean shutdown = false;private Log log = LogFactory.getLog(this.getClass());public static void main(String[] args) {SimpleContainer server = new SimpleContainer();server.start();}private void start() {ServerSocket serverSocket = null;try {serverSocket = new ServerSocket(PORT, LOG_BAKC,InetAddress.getByName(HOSTNAME));} catch (IOException e) {System.out.println("Server starts failed");e.printStackTrace();System.exit(1);}log.info("Server starts successfully.");service(serverSocket);}private void service(ServerSocket serverSocket) {while (!shutdown) {try {processRequest(serverSocket);} catch (Exception e) {e.printStackTrace();}}}private void processRequest(ServerSocket serverSocket) throws IOException {log.info("waitting for incoming request ... ");Socket socket = serverSocket.accept();log.info("receive a request from "+ socket.getRemoteSocketAddress().toString());InputStream in = socket.getInputStream();BaseRequest request = new BaseRequest(in);log.info("Request Object ready!");OutputStream out = socket.getOutputStream();BaseResponse response = new BaseResponse(out);ServletProcessor.processServletRequest(request, response);socket.close();}}

BaseRequest:只给出部分实现,其他都是ServletRequest的空实现


package simpleserver.simplecontainer;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;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 BaseRequest implements ServletRequest {private InputStream inputStream;private String uri;private StringBuffer requestContentBuffer = new StringBuffer(2048);public BaseRequest(InputStream in) {this.inputStream = in;prepareContent();parseAndSetUri();}private void prepareContent() {byte[] buffer = new byte[2048];int i = -1;try {i = inputStream.read(buffer);} catch (IOException e) {e.printStackTrace();}for (int k = 0; k < i; k++) {requestContentBuffer.append((char) buffer[k]);}System.out.println(requestContentBuffer.toString());}private void parseAndSetUri() {String requestString = requestContentBuffer.toString();int index1 = requestString.indexOf(' ');int index2 = -1;if (index1 != -1) {index2 = requestString.indexOf(' ', index1 + 1);}this.uri = (index2 > index1) ? requestString.substring(index1 + 1,index2) : null;}public String getUri() {return this.uri;}     ......}

BaseResponse:

public class BaseResponse implements ServletResponse {private OutputStream outputStream;public BaseResponse(OutputStream out) {this.outputStream = out;}    ......}

ServletProcessor:

package simpleserver.simplecontainer;import java.io.File;import java.io.IOException;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;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;public class ServletProcessor {private static Log log = LogFactory.getLog(ServletProcessor.class);public static void processServletRequest(BaseRequest request,BaseResponse response) {String uri = request.getUri();String servletName = MappingHelper.resolve(uri);log.info("Processing servlet: " + servletName);Servlet servlet = loadServlet(servletName);callService(servlet, request, response);}private static URLClassLoader createUrlClassLoader() {URLClassLoader loader = null;try {URL[] urls = new URL[1];URLStreamHandler streamHandler = null;File classPath = new File(Constant.RESOURCE_ROOT);// org.apache.catalina.startup.ClassLoaderFactoryString repository = (new URL("file", null,classPath.getCanonicalPath() + File.separator)).toString();urls[0] = new URL(null, repository, streamHandler);loader = new URLClassLoader(urls);} catch (IOException e) {e.printStackTrace();}return loader;}private static Servlet loadServlet(String servletName) {URLClassLoader loader = createUrlClassLoader();Servlet servlet = null;try {@SuppressWarnings("unchecked")Class<Servlet> servletClass = (Class<Servlet>) loader.loadClass(servletName);servlet = (Servlet) servletClass.newInstance();} catch (ClassNotFoundException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}return servlet;}private static void callService(Servlet servlet, ServletRequest request,ServletResponse response) {try {servlet.service(request, response);} catch (ServletException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}}

MappingHeler:
package simpleserver.simplecontainer;import java.io.FileInputStream;import java.io.IOException;import java.util.Properties;public class MappingHelper {public static Properties requestMapping;static {requestMapping = new Properties();try {requestMapping.load(new FileInputStream(Constant.MAPPING_FILE));} catch (IOException e) {e.printStackTrace();}}public static String resolve(String requestPath) {return requestMapping.getProperty(requestPath);}}

该类负责请求URL到Servlet的映射,读入一个属性文件,如下:

/servlet/PrimitiveServlet=simpleserver.simplecontainer.webapp.PrimitiveServlet/servlet/Test=simpleserver.simplecontainer.webapp.TestServlet

另外还有一个帮助类定义常量。


欢迎交流。


1 0
原创粉丝点击