这是我在华沙大学参加的课程的一部分创建的项目。这是一个简单的Servlet容器,该容器支持Java Servlets符合Java Servlet API。即使它是准排骨,它也可以加载和运行简单的应用程序,这些应用程序使用servlet的基本功能,包括派遣,JSP和异步servlet。
我没有找到许多其他小型servlet容器实现,因此可以随意将其用作如何创建这样的容器的示例。请记住,由于时间限制,这是一个非常不完美的越野车项目,尽管我试图忠于Servlet API,但我在这里和那里获得了一些自由。
这个想法是,该容器以与Tomcat相似的方式工作。您可以使用ServletContainer :: AddRoute添加Servlet类,但首选的方法是使用组件扫描功能。也就是说,如果应用程序被划定到warName.war中。WAR文件并将其放置在server/src/main/resources内部的deploy目录中,它将在服务器启动上加载并在localhost:8000/warName下可用。
默认情况下,服务器在端口8000上运行。可以在Main函数中更改它。此功能还包含如何启动和配置该服务器的示例。
该项目是使用Gradle构建的。它包含两个子项目:服务器和演示应用程序(简单书籍数据库)。它在Linux下进行了测试,我不知道它甚至在Windows/MacOS下工作。
运行服务器使用:
./gradlew server:run
启动服务器要求我们创建ServletContainer的实例。
ServletContainer(int threads)创建ServletContainer的新实例。给定数量的线程用于创建线程池。该池将用于处理HTTP请求。
void ServletContainer::start(int port)在给定端口上启动servlet容器。请记住,目前尚未加载.war文件。它们仅在ServletContainer:servletScan时才加载。
void ServletContainer::stop()优雅地停止容器。
void ServletContainer::servletScan()从server/src/main/resources内部deploy目录中加载所有.WAR文件。它也将加载.jsp文件,并立即将它们转移到.class文件中。只有@WebServlet注释的课程才会加载。一个警告是,我使用的.war文件具有我不确定与大多数其他.war文件相同的特定目录结构。您可以在演示应用程序中查看war Gradle任务,以查看此结构的外观。在开始()方法之前,该方法最多应一次调用。
void ServletContainer::servletScan(String url)与ServletContainer::servletScan()相同,但可以从给定的URL加载.war文件。
void ServletContainer::addRoute(Class<? extends HttpServlet> cls, String path)将servlet添加到容器中。 Servlet只能处理给定的URL(有关更多详细信息,请参见常见问题解答)。
该服务器同时支持多个客户端的处理。每个客户端将通过ThreadPool的单独线程处理。默认的池大小为4,可以在Main功能中更改。
该服务器将支持从Httpservlet继承的任何类。可以使用ServletContainer::addRoute添加此类类。
这些类别的最重要功能是实施的。您可以使用(仅) PrintWriter ,设置标头和响应状态写入客户端。 httpservletrequest可以提取请求URL,http方法(获取,发布,删除,补丁),查询参数和从身体的参数(在POST的情况下)。冲洗缓冲区或关闭HttpServletResponse后,数据将发送给客户端。使用RequestDispatcher ,您可以将查询发送到包括JSP Servlet在内的其他servlet。
有支持异步servlet。执行HttpServletRequest::startAsync之后,请求进入异步模式。使用此模式有两种方法。任何代码都将在某个时候执行AsyncContext::complete ,这将运行。您也可以使用AsyncConext::start(Runnable) ,在这里您还需要在某个时候执行AsyncContext::complete 。后者与Java Servlet API兼容。它还支持超时( AsyncContext::setTimeout )和AsyncListener 。异步servlet是使用CompletableFuture实现的,因此它们将使用与同步客户端使用的螺纹池。
如果该应用程序已被拉到.war并移至deploy文件夹,则将自动加载它。用@WebServlet注释并从HttpServlet继承的所有类都将添加到Servlet容器中,并将在localhost:port/warName/servletUrl上找到。每个这样的类都必须在值属性中的@WebServlet注释中指定一个servletUrl 。如果应用程序使用JSP,它将自动编译为.class并加载。可以加载许多应用程序,但是,当两个应用程序中使用相同的类名称时,我不知道会发生什么。
服务器可以将.jsp文件转换为.class。 JSP中几乎所有语法都有支持。其中包括:
<%@ page import/include=... %><% ... %><%! ... %><%= ... %><%-- %>${...} ${}语法部分支持表达语言。可以执行简单的算术操作,并将表单实例的任何表达式instance.property1.property2转换为request.getAttribute("instance").getProperty1().getProperty2() 。在<% ... %>中也有out.println(...) ,直接写给客户。可以使用RequestDispatcher::forward显示JSP,也可以直接在localhost:8000/warName/jspFileName.jsp显示。可在localhost:8000/library/jsp上获得示例JSP操作。请记住,我实施了解析自己,因此怪异的代码格式/语法可能会破坏它。
为了演示servlet容器,我实施了一个简单的应用程序,该应用程序模拟了书籍数据库。可以使用HTML表格添加,删除和更新书籍。所有书也可以在HTML表中查看。前端使用JSP完全开发。应用程序被划入库中。 war gradle任务,在启动时移动到服务器上deploy并加载到服务器。
端点:
有30多次测试可以检查大多数功能和演示应用程序。由于常见问题解答中解释的问题,首选从Intellij运行它们。或者,它们可以使用:
./gradlew test
该项目是大学课程的一部分,除Junit以外,我不允许使用任何第三方图书馆。
插座端口已经在使用中存在问题。我从来没有解决过它来修复它,但是大多数时候它可以从Intellij运行。
目前,Servlet应处理给定URL的分辨率非常原始。基本上,给定url服务器寻找servletUrl是url的前缀的Servlet。如果有许多前缀最长的一个。这意味着,如果servletUrl等于/app/home等于/app/home/nonexisting/ , /app/home/12345
欢迎任何人为这个项目做出贡献。我创建了一些良好的问题,因此请随时检查一下:)