jetty有一個口號,不要把你的應用部署到jetty,而是把jetty部署到你的應用中。這說明使用jetty有兩種選擇,一種選擇是將你的應用部署到jetty容器中,另一種選擇是將jetty嵌入到你的應用層序中,后者意味着將一個http的模塊嵌入到了你的程序中,而不是把你的程序放入一個http的server中。
這個文檔就是幫助你一步一步將jetty嵌入到你的程序中。
1.概述
把jetty嵌入到你的程序中,一般都是下面的步驟:
- 創建一個server實例
- 增加或者配置connector
- 增加或者配置handler、context、servlet
- 啟動server
- 等待連接
2.創建一個server
下面的代碼創建了一個最簡單的server。
import org.eclipse.jetty.server.Server;
/**
* Created by zdd on 2017/4/1.
*/
public class SimpleServer {
public static void main(String[] args) throws Exception{
Server server = new Server(8090);
server.start();
server.dumpStdErr();
server.join();
}
}
上面程序運行一個http server在8090端口。但是這個server沒有任何用處,因為它沒有任何handler,因此不管任何的請求都會返回404.
3.使用handler
為了對請求產生一個回應,jetty需要你在server上設置handler。一個handler可以:
- 檢查或者修改HTTP請求。
- 生成response回應。
- 調用另一個handler(查看HandlerWrapper)
- 選擇一個或者很多個handlers去調用(查看HandlerCollection)
3.1hello world Handler
下面的代碼展示了一個最簡單的helloworld handler:
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* Created by zdd on 2017/4/1.
*/
public class HelloHandler extends AbstractHandler {
final String greeting;
final String body;
public HelloHandler(){
this("hello world");
}
public HelloHandler(String greeting){
this(greeting,null);
}
public HelloHandler(String greeting,String body){
this.greeting = greeting;
this.body = body;
}
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
response.setContentType("text/html;charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
PrintWriter out = response.getWriter();
out.println("<h1>"+greeting+"</h1>");
if(body != null){
out.println(body);
}
baseRequest.setHandled(true);
}
}
傳遞給handle方法的參數有:
- target:請求的目標
- baseRequest:沒有被包裝的request對象
- request:不可變的request對象,可能被filter或者servlet包裝過。
- response:請求的回應,也可能被filter或者servlet包裝過。
handler會設置response的狀態、response的內容類型、並且標記request被處理了,最后才通過out生成response的內容。
3.2運行helloworld Handler
為了允許handler去處理http請求,你必須把它綁定到server實例上。下面的代碼顯示了jetty server是如何和handler綁定的。
import org.eclipse.jetty.server.Server;
/**
* Created by zdd on 2017/4/1.
*/
public class SimpleServer {
public static void main(String[] args) throws Exception{
Server server = new Server(8090);
server.setHandler(new HelloHandler());
server.start();
server.join();
}
}
在jetty中,一般總會有一個或者多個handler區處理所有的請求。有些handler會選擇其他指定的handler去做處理,例如(一個ContextHandlerCollection使用context path去選擇ContextHandler);還有一些是使用程序的邏輯去產生response,例如(ServletHandler將請求轉發給Servlet來處理),還有其他的一些是做一些無關的任務來生成response,例如(RequestLogHandler或者StatisticsHandler)。
后面的章節描述了你可以像切面一樣合並多個handler。你可以看到一些有價值的handler在org.eclipse.jetty.server.handler包中。
4.Handler Collections and Wrappers
復雜的請求需要你合並多個handler來處理。jetty提供了幾個HandlerContainer接口的實現:
- HandlerCollection:擁有一個handler的集合並且調用順序調用每個handler。當你想合並統計和日志的handler這是很有用的。
- HandlerList:一個handler的集合,會依次調用這個集合里面的handler,直到如果有異常發生、response已經被提交、resquest.isHandled()被調用。你可以使用這個來合並一些handler,例如調用handler來處理直到被匹配到的情況下。
- HandlerWrapper:你可以使用這個來完成連接handlers類似於面向切面編程的風格。例如,一個標准的web程序被實現成一個context、session、security和servlet的串聯。
- ContextHandlerClollection:一個特殊的HandlerCollection,使用最長uri匹配原則來選擇handler。
5.Scoped Handlers
Server.handle(...)
ContextHandler.doScope(...)
ServletHandler.doScope(...)
ContextHandler.doHandle(...)
ServletHandler.doHandle(...)
SomeServlet.service(...)
6.Resource Handler
下面的代碼給你展示了你可以使用ReourceHandler去處理靜態的資源。
public class FileServer {
public static void main(String[] args) throws Exception{
// 創建一個最基本的jetty server並且綁定8090端口
// 如果把端口指定為0,那么程序會自動選擇一個可用的端口
Server server = new Server(8090);
ResourceHandler resourceHandler = new ResourceHandler();
// 設置resourceHandler監聽的目錄
resourceHandler.setDirectoriesListed(true);
resourceHandler.setWelcomeFiles(new String[]{"idenx.html"});
resourceHandler.setResourceBase(".");
HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[]{resourceHandler, new DefaultHandler()});
server.setHandler(handlers);
server.start();
server.join();
}
}
注意我們使用了ResourceHandler和DefaultHandler,所以DefaultHandler對於沒有匹配的請求會友好的返回404。
7.內置connectors
在上面的例子中,server實例都會綁定一個端口並且創建一個默認的connector來監聽這個端口。但是,在內置jetty中,自定義一個或者多個connetor是很常見的。
7.1一個connector
public class OneConnector {
public static void main(String[] args) throws Exception{
Server server = new Server();
ServerConnector http = new ServerConnector(server);
http.setHost("localhost");
http.setPort(8090);
http.setIdleTimeout(30000);
server.addConnector(http);
server.setHandler(new HelloHandler());
server.start();
server.join();
}
}
在這個例子里connector處理http協議,因為這是ServerConnetor默認的配置。
7.2多connector
當配置多connector的時候,最好是多個connector享用相同的配置信息。為了實現這個你需要明確的指定serverConnector的配置,使用的是ConnectionFactory這個實例。
8內置servlets
servlet是讓應用程序通過邏來處理請求的標准的方式。servlet和handler很相似。servlet在jetty中是被ServletHandler處理的。它使用標准的路徑映射到一個servlet。
下面的代碼創建了一個ServletHandler並且配置了一個簡單的HelloServlet:
public class MinimalServlets {
public static void main(String[] args) throws Exception{
Server server = new Server(8090);
ServletHandler handler = new ServletHandler();
server.setHandler(handler);
// 需要注意的是,這是個原生的servlet,你可以通過web.xml或者@WebServlet來配置
handler.addServletWithMapping(HelloServlet.class,"/*");
server.start();
server.join();
}
}
class HelloServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
resp.setStatus(HttpServletResponse.SC_OK);
resp.getWriter().println("<h1>hello from HelloServlet</h1>");
}
}