程序执行步骤
- 创建一个ServerSocket对象;
- 调用ServerSocket对象的accept方法,等待连接,连接成功会返回一个Socket对象,否则一直阻塞等待;
- 从Socket对象中获取InputStream和OutputStream字节流,这两个流分别对应request请求和response响应;
- 处理请求:读取InputStream字节流信息,转成字符串形式,并解析,这里的解析比较简单,仅仅获取uri(统一资源标识符)信息;
- 处理响应(分两种类型,静态资源请求响应或servlet请求响应):如果是静态资源请求,则根据解析出来的uri信息,从WEB_ROOT目录中寻找请求的资源资源文件, 读取资源文件,并将其写入到OutputStream字节流中;如果是Servlet请求,则首先生成一个URLClassLoader类加载器,加载请求的servlet类,创建servlet对象,执行service方法(往OutputStream写入响应的数据);
- 关闭Socket对象;
- 转到步骤2,继续等待连接请求;
代码实现:
添加依赖:
<!-- https://mvnrepository.com/artifact/javax.servlet/servlet-api --> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.3</version> </dependency>
服务器代码:
package ex02.pyrmont.first; import java.net.Socket; import java.net.ServerSocket; import java.net.InetAddress; import java.io.InputStream; import java.io.OutputStream; import java.io.IOException; import ex02.pyrmont.Request; import ex02.pyrmont.Response; import ex02.pyrmont.StaticResourceProcessor; public class HttpServer1 { // 关闭服务命令 private static final String SHUTDOWN_COMMAND = "/SHUTDOWN"; 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); } // 循环等待请求 while (true) { Socket socket = null; InputStream input = null; OutputStream output = null; try { //等待连接,连接成功后,返回一个Socket对象 socket = serverSocket.accept(); input = socket.getInputStream(); output = socket.getOutputStream(); // 创建Request对象并解析 Request request = new Request(input); request.parse(); // 检查是否是关闭服务命令 if (request.getUri().equals(SHUTDOWN_COMMAND)) { break; } // 创建 Response 对象 Response response = new Response(output); response.setRequest(request); if (request.getUri().startsWith("/servlet/")) { //请求uri以/servlet/开头,表示servlet请求 ServletProcessor1 processor = new ServletProcessor1(); processor.process(request, response); } else { //静态资源请求 StaticResourceProcessor processor = new StaticResourceProcessor(); processor.process(request, response); } // 关闭 socket socket.close(); } catch (Exception e) { e.printStackTrace(); System.exit(1); } } } }
常量类:
package ex02.pyrmont; import java.io.File; public class Constants { public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot"; public static final String WEB_SERVLET_ROOT = System.getProperty("user.dir") + File.separator + "target" + File.separator + "classes"; }
Request:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
Response:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
静态资源请求处理:
package ex02.pyrmont; import java.io.IOException; public class StaticResourceProcessor { public void process(Request request, Response response) { try { response.sendStaticResource(); } catch (IOException e) { e.printStackTrace(); } } }
Servlet请求处理:
package ex02.pyrmont.first; import java.net.URL; import java.net.URLClassLoader; import java.net.URLStreamHandler; import java.io.IOException; import javax.servlet.Servlet; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import ex02.pyrmont.Constants; import ex02.pyrmont.Request; import ex02.pyrmont.Response; public class ServletProcessor1 { public void process(Request request, Response response) { String uri = request.getUri(); String servletName = uri.substring(uri.lastIndexOf("/") + 1); //类加载器,用于从指定JAR文件或目录加载类 URLClassLoader loader = null; try { URLStreamHandler streamHandler = null; //创建类加载器 loader = new URLClassLoader(new URL[]{new URL(null, "file:" + Constants.WEB_SERVLET_ROOT, streamHandler)}); } catch (IOException e) { System.out.println(e.toString()); } Class<?> myClass = null; try { //加载对应的servlet类 myClass = loader.loadClass(servletName); } catch (ClassNotFoundException e) { System.out.println(e.toString()); } Servlet servlet = null; try { //生产servlet实例 servlet = (Servlet) myClass.newInstance(); //执行ervlet的service方法 servlet.service((ServletRequest) request,(ServletResponse) response); } catch (Exception e) { System.out.println(e.toString()); } catch (Throwable e) { System.out.println(e.toString()); } } }
Servlet类:
import javax.servlet.*; import java.io.IOException; import java.io.PrintWriter; public class PrimitiveServlet implements Servlet { public void init(ServletConfig config) throws ServletException { System.out.println("init"); } public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException { System.out.println("from service"); PrintWriter out = response.getWriter(); out.println("Hello. Roses are red."); out.print("Violets are blue."); } public void destroy() { System.out.println("destroy"); } public String getServletInfo() { return null; } public ServletConfig getServletConfig() { return null; } }
结果测试:
静态资源请求:
servlet请求(因为只是第一个字符串被刷新到浏览器,所以你不能看到第二个字符串Violets are blue。我们将在后续完善该容器):
改进
前面实现的Servlet容器有一个严重的问题,用户在servlet里可以直接将ServletRequest、ServletResponse向下转 型为Request和Response类型,并直接调用其内部的public方法,这是一个不好的设计,改进方法是给Request、Response 增加外观类,这样,用户只能访问外观类里定义的public方法。
Request外观类
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
Response外观类
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
处理Servlet请求类:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
其它代码与前面实现的Servlet容器基本一致。
验证程序,分别请求静态资源和Servlet,发现结果与前面实现的容器一致;
本文转自风一样的码农博客园博客,原文链接:http://www.cnblogs.com/chenpi/p/5603072.html,如需转载请自行联系原作者