이것은 바르샤바 대학교에서 수강 한 과정의 일환으로 만들어진 프로젝트입니다. Java Servlet API를 준수하는 Java Servlets를 지원하는 Servlet 컨테이너의 간단한 구현입니다. 비록 맨손이지만 디스패치, JSP 및 비동기 서블릿을 포함한 서블릿의 기본 기능을 사용하는 간단한 응용 프로그램을로드하고 실행할 수 있습니다.
나는 다른 소규모 서블릿 컨테이너 구현을 찾지 못했기 때문에 이것을 이와 같은 컨테이너를 만드는 방법의 예로 자유롭게 사용하십시오. 시간 제한으로 인해 꽤 불완전하고 버그가 많은 프로젝트이며 Servlet API에 충실하려고 노력했지만 여기저기서 자유를 얻었습니다.
아이디어는이 컨테이너가 Tomcat과 비슷한 방식으로 작동한다는 것입니다. ServletContainer :: Addroute를 사용하여 Servlet 클래스를 추가 할 수 있지만 선호하는 방법은 구성 요소 스캔 기능을 사용하는 것입니다. 즉, 응용 프로그램이 warName.war 파일에 적용되고 server/src/main/resources 내부의 deploy 디렉토리에 배치 된 경우 서버 시작에로드되고 localhost:8000/warName 에서 사용 가능합니다.
기본적으로 서버는 포트 8000에서 실행됩니다. Main 기능으로 변경할 수 있습니다. 이 기능에는 또한이 서버를 시작하고 구성하는 방법에 대한 예와 예가 포함되어 있습니다.
이 프로젝트는 Gradle을 사용하여 구축되었습니다. 서버와 데모 응용 프로그램 (간단한 책 데이터베이스)의 두 가지 하위 프로젝트가 포함되어 있습니다. Linux에서 테스트되었으며 Windows/MacOS에서도 작동하는지 모르겠습니다.
서버 사용 실행 :
./gradlew server:run
시작 서버를 사용하려면 ServletContainer 인스턴스를 작성해야합니다.
ServletContainer(int threads) ServletContainer 의 새로운 인스턴스를 만듭니다. 주어진 수의 스레드가 ThreadPool을 작성하는 데 사용됩니다. 이 풀은 HTTP 요청을 처리하는 데 사용됩니다.
void ServletContainer::start(int port) 주어진 포트에서 서블릿 컨테이너를 시작합니다. 이 시점에서 .war 파일이로드되지 않았습니다. ServletContainer:servletScan 이 호출 될 때만로드됩니다.
void ServletContainer::stop()컨테이너를 우아하게 멈 춥니 다.
void ServletContainer::servletScan() server/src/main/resources 내부의 deploy 디렉토리에서 모든 .war 파일을로드합니다. .jsp 파일도로드하고 즉시 .class 파일로 전환합니다. @WebServlet 으로 주석이 달린 클래스 만로드됩니다. 한 가지주의 사항은 내가 사용하는 .war 파일에 내가 확실하지 않은 특정 디렉토리 구조를 가지고 있다는 것입니다. 데모 애플리케이션에서 war Gradle 작업을 확인 하여이 구조가 어떻게 생겼는지 확인할 수 있습니다. 이 메소드는 start () 메소드 전에 최대 한 번 호출해야합니다.
void ServletContainer::servletScan(String url) ServletContainer::servletScan() 과 동일하지만 주어진 URL에서 .war 파일을로드 할 수 있습니다.
void ServletContainer::addRoute(Class<? extends HttpServlet> cls, String path)컨테이너에 서블릿을 추가합니다. 서블릿은 주어진 URL 만 처리합니다 (자세한 내용은 FAQ 참조).
서버는 한 번에 여러 클라이언트의 동시 처리를 지원합니다. 각 클라이언트는 ThreadPool에서 별도의 스레드로 처리됩니다. 기본 풀 크기는 4이며 Main 기능에서 변경할 수 있습니다.
서버는 httpservlet에서 상속되는 클래스를 지원합니다. 이러한 클래스는 ServletContainer::addRoute 사용하여 추가 할 수 있습니다.
이 클래스의 가장 중요한 기능이 구현됩니다. (전용) PrintWriter , 설정 헤더 및 응답 상태를 사용하여 클라이언트에게 편지를 쓸 수 있습니다. httpservletrequest는 요청 URL, http 메소드 (get, post, delete, patch), 본문에서 쿼리 매개 변수 및 매개 변수를 추출 할 수 있습니다 (Post의 경우). 버퍼를 플러시하거나 HttpServletResponse 를 닫은 후 데이터가 클라이언트에게 전송됩니다. RequestDispatcher 사용하면 JSP 서블릿을 포함한 다른 서블릿에 쿼리를 보낼 수 있습니다.
비동기 서블릿에 대한 지원이 있습니다. HttpServletRequest::startAsync 실행 된 후 요청은 비동기 모드로 이동합니다. 이 모드를 사용하는 두 가지 방법이 있습니다. 모든 코드는 AsyncContext::complete 어느 시점에서 실행되면 클라이언트와의 연결을 종료합니다. AsyncConext::start(Runnable) 사용할 수도 있습니다. 여기에서는 AsyncContext::complete 해야합니다. 후자는 Java Servlet API와 호환됩니다. 또한 타임 아웃 ( AsyncContext::setTimeout ) 및 AsyncListener 도 지원합니다. Async 서블릿은 CompletableFuture 사용하여 구현되므로 동기 클라이언트에 사용되는 것과 별도의 ThreadPool을 사용합니다.
응용 프로그램이 .war로 지정되어 deploy 폴더로 이동하면 자동으로로드됩니다. @WebServlet 으로 주석을 달고 HttpServlet 에서 상속 된 모든 클래스는 서블릿 컨테이너에 추가되며 localhost:port/warName/servletUrl 에서 사용할 수 있습니다. 각각의 클래스에는 값 속성에 @WebServlet 주석에 정확히 하나의 servletUrl 지정되어 있어야합니다. 응용 프로그램이 JSP를 사용하면 자동으로 .class로 컴파일되어로드됩니다. 많은 응용 프로그램을로드 할 수 있지만 두 응용 프로그램에 사용 된 동일한 클래스 이름이있을 때 어떻게되는지 모르겠습니다.
서버는 .jsp 파일을 .class로 전송할 수 있습니다. JSP의 거의 모든 구문에 대한 지원이 있습니다. 여기에는 다음이 포함됩니다.
<%@ page import/include=... %><% ... %><%! ... %><%= ... %><%-- %>${...} ${} 구문은 표현어를 부분적으로 지원합니다. 간단한 산술 작업을 수행 할 수 있으며 Form instance.property1.property2 의 모든 표현식이 request.getAttribute("instance").getProperty1().getProperty2() . <% ... %> 에서 클라이언트에게 직접 글을 쓰는 out.println(...) 도 있습니다. JSP는 RequestDispatcher::forward 사용하여 표시 할 수 있거나 localhost:8000/warName/jspFileName.jsp 에서 직접 사용할 수 있습니다. 샘플 JSP 동작은 localhost:8000/library/jsp 에서 사용할 수 있습니다. 이상한 코드 형식/구문이이를 깨뜨릴 수 있도록 직접 구문 분석을 구현했다는 점을 명심하십시오.
서블릿 컨테이너를 시연하기 위해 책 데이터베이스를 시뮬레이션하는 간단한 응용 프로그램을 구현했습니다. HTML 양식을 사용하여 책을 추가, 제거 및 업데이트 할 수 있습니다. 모든 책은 HTML 테이블에서도 볼 수 있습니다. 프론트 엔드는 JSP를 사용하여 완전히 개발되었습니다. 응용 프로그램은 Library에 적용됩니다. war gradle 작업을 사용하여 전쟁으로 이동하여 deploy 로 이동하여 시작할 때 서버에로드했습니다.
엔드 포인트 :
대부분의 기능과 데모 응용 프로그램을 확인하기위한 30 개가 넘는 테스트가 있습니다. FAQ 에서 설명 된 문제로 인해 Intellij에서 실행하는 것이 바람직합니다. 또는 다음을 사용하여 실행할 수 있습니다.
./gradlew test
이 프로젝트는 대학 과정의 일부였으며 Junit을 제외한 제 3 자 도서관을 사용할 수 없었습니다.
소켓 포트가 이미 사용중인 문제가 있습니다. 나는 그것을 고치려고하지 않았지만 대부분의 시간은 Intellij에서 달릴 때 작동합니다.
현재 Servlet이 주어진 URL을 처리 해야하는 해상도는 상당히 원시적입니다. 기본적으로 url Server는 servletUrl 이 url 의 접두사 인 서블릿을 찾습니다. 가장 긴 접두사가있는 것이 많으면 선택됩니다. 즉, 서블릿이 /app/home 과 동일한 servletUrl 있는 경우 /app/home/nonexisting/ , /app/home/12345 등도 처리합니다.
누구나이 프로젝트에 기여할 수 있습니다. 나는 몇 가지 좋은 우선 문제를 만들었으므로 자유롭게 확인하십시오. :)