写程序的人,尤其是写web程序的人都不会对servlet感到陌生。在我们实现自己servlet的时候只要在web页面配置下servlet信息,并且实现servlet的service方法就完事儿大吉了。
简单的实现servlet解析至少需要实现连接器、处理器。连接器负责打开连接门户socket。处理器负责创建数据处理对象,解析http请求。
1、连接器职责:
ServerSocket serverSocket = null; int port = 8080; try {//生成服务器socket serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1")); } catch (IOException e) { e.printStackTrace(); System.exit(1); } while (!stopped) { // Accept the next incoming connection from the server socket Socket socket = null; try {//打开socket socket = serverSocket.accept(); } catch (Exception e) { continue; } //调用处理器处理请求 HttpProcessor processor = new HttpProcessor(this); processor.process(socket); }
2、处理器的职责
2.1、创建HttpRequest、HttpResponse
//通过socket的输入流生成输入流缓冲区 input = new SocketInputStream(socket.getInputStream(), 2048); //创建socket输出流缓冲区 output = socket.getOutputStream(); // 创建HttpRequest实例 request = new HttpRequest(input); // 创建HttpResponse实例对象 response = new HttpResponse(output); response.setRequest(request); response.setHeader("Server", "Pyrmont Servlet Container");
2.2、解析请求行 parseRequest(input, output);
// 解析请求行 input.readRequestLine(requestLine); //获得方法名称 post/get String method = new String(requestLine.method, 0, requestLine.methodEnd); String uri = null; //获得协议名称http/1.1 String protocol = new String(requestLine.protocol, 0, requestLine.protocolEnd); // Validate the incoming request line if (method.length() < 1) { throw new ServletException("Missing HTTP request method"); } else if (requestLine.uriEnd < 1) { throw new ServletException("Missing HTTP request URI"); } // Parse any query parameters out of the request URI //解析uri以外的参数 int question = requestLine.indexOf("?"); if (question >= 0) { request.setQueryString(new String(requestLine.uri, question + 1, requestLine.uriEnd - question - 1)); uri = new String(requestLine.uri, 0, question); } else { request.setQueryString(null); uri = new String(requestLine.uri, 0, requestLine.uriEnd); } //获取uri的绝对路径,将http://等信息跳过去 if (!uri.startsWith("/")) { int pos = uri.indexOf("://"); // Parsing out protocol and host name if (pos != -1) { pos = uri.indexOf('/', pos + 3); if (pos == -1) { uri = ""; } else { uri = uri.substring(pos); } } } // uri之外的jessionid信息,如果有设置到request的sessionId之中 String match = ";jsessionid="; int semicolon = uri.indexOf(match); if (semicolon >= 0) { String rest = uri.substring(semicolon + match.length()); //该行除了jessionid之外还有别的字符串 int semicolon2 = rest.indexOf(';'); if (semicolon2 >= 0) { request.setRequestedSessionId(rest.substring(0, semicolon2)); rest = rest.substring(semicolon2); } else { //只有jessionid信息,直接设置其到requestedSessionId字段中 request.setRequestedSessionId(rest); rest = ""; } request.setRequestedSessionURL(true); uri = uri.substring(0, semicolon) + rest; } else { //如果没有jsessionid,则对应字段置空 request.setRequestedSessionId(null); request.setRequestedSessionURL(false); } // Normalize URI (using String operations at the moment) String normalizedUri = normalize(uri); // 设置方法和协议 ((HttpRequest) request).setMethod(method); request.setProtocol(protocol); if (normalizedUri != null) { ((HttpRequest) request).setRequestURI(normalizedUri); } else { ((HttpRequest) request).setRequestURI(uri); } if (normalizedUri == null) { throw new ServletException("Invalid URI: " + uri + "'"); } }
2.3、解析请求头 parseHeaders(input)
while (true) { HttpHeader header = new HttpHeader();; // 按行解析请求行 input.readHeader(header); if (header.nameEnd == 0) { if (header.valueEnd == 0) { return; } else { throw new ServletException (sm.getString("httpProcessor.parseHeaders.colon")); } } String name = new String(header.name, 0, header.nameEnd); String value = new String(header.value, 0, header.valueEnd); // 设置httprequest 头字段 request.addHeader(name, value); //设置cookie字段 if (name.equals("cookie")) { Cookie cookies[] = RequestUtil.parseCookieHeader(value); for (int i = 0; i < cookies.length; i++) { if (cookies[i].getName().equals("jsessionid")) { // Override anything requested in the URL if (!request.isRequestedSessionIdFromCookie()) { // Accept only the first session id cookie request.setRequestedSessionId(cookies[i].getValue()); request.setRequestedSessionCookie(true); request.setRequestedSessionURL(false); } } request.addCookie(cookies[i]); } } //设置contentLength字段 else if (name.equals("content-length")) { int n = -1; try { n = Integer.parseInt(value); } catch (Exception e) { throw new ServletException(sm.getString("httpProcessor.parseHeaders.contentLength")); } request.setContentLength(n); } //设置contentType字段 else if (name.equals("content-type")) { request.setContentType(value); } } //end while
2.4、servlet创建,并执行service方法。
public void process(HttpRequest request, HttpResponse response) { String uri = request.getRequestURI(); String servletName = uri.substring(uri.lastIndexOf("/") + 1); URLClassLoader loader = null; try { // 创建类加载器 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()); } Servlet servlet = null; try { //实例化类 servlet = (Servlet) myClass.newInstance(); //创建request门面模式 HttpRequestFacade requestFacade = new HttpRequestFacade(request); //创建response门面模式 HttpResponseFacade responseFacade = new HttpResponseFacade(response); //调用实例的service方法 servlet.service(requestFacade, responseFacade); //servlet执行完毕,调用fresh方法将数据写入输出流中 ((HttpResponse) response).finishResponse(); } catch (Exception e) { System.out.println(e.toString()); } catch (Throwable e) { System.out.println(e.toString()); } }
题外话:
Facate设计模式:
有的时候不想暴露一些方法,可以通过门面模式去实现。
类A有方法B不想暴漏。类A实现了接口C。 那么生成类D,让其实现接口C。并将接口C放置到类D中去。这样,在调用类A的时候,我们可以将其包装成门面模式去暴露给外部接口使用。这样即能够暴露需要暴露的方法,又能够不暴露不需要暴露的方法。
在tomcat实现httpRequest的时候,使用了门面模式。也就是说暴露给我们自定义servlet的不是httpRequest实例,而是httpRequest的门面类实例。原因就是上面说的。