用Embedded Jetty和SpringMVC打造Http Rest Server

jopen 8年前發布 | 22K 次閱讀 Jetty HTTP Web服務器 Spring MVC

你是不是一直很討厭寫 web.xml 。其實你就只需要一個簡單的 Dynamic HTTP Server ,結果你不得不去下載一個 tomcat ,把你的應用打包成 war ,然后部署到 tomcat 中。如果你只想在你的應用中提供一個 Restful 的 Http Server 給外部,對內還要提供一些 RPC 的邏輯,這時候你是不是還得去Google一下Tomcat如何提供2個 Connectors ,然后如何讓不同的 Request 發送到不同的 Connector 。

這個時候Jetty就是你的救星,特指Embeded Jetty。

$2 你唯一需要的就是一個jar包

node.js 需要配置文件嗎?不需要!需要打包成一個可部署的組件嗎?不需要!需要你先啟動一個類似容器的玩意嗎?不需要!

http.createServer(function (request, response) {
        //handle request
        //make response 
    });
}).listen(port);

node.js 創建一個 HTTP Server 就是這么簡單。那么Embeded Jetty呢?

public class SimplestServer
{
    public static void main( String[] args ) throws Exception
 {
        Server server = new Server(8080);
        server.setHandler(new AbstractHandler(){
            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);
            }
        });
        server.start();
        server.join();
    }
}

看起來還行。那需要討厭的配置文件嗎?不需要,你只需要在maven中依賴jetty-all就可以啦

<dependency>
  <groupId>org.eclipse.jetty.aggregate</groupId>
  <artifactId>jetty-all</artifactId>
  <version>9.3.4.RC1</version>
</dependency>

所有配置文件的邏輯在Embeded Jetty中都可以通過參數解決,最關鍵的是:Jetty定義得很清晰,一目了然。

$3 Jetty啟動和處理Request的過程

$3.1 Jetty啟動

1 創建一個Server

Server server = new Server();

2 創建一個或多個Connector

ServerConnector http = new ServerConnector(server);
http.setPort(8080);
http.setHost("localhost");
server.addConnector(http);

3 創建一堆Handler

HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[]{
        requestLogHandler,
        rewrite,
        spingMvcHandler,
// new DefaultHandler()
});
server.setHandler(handlers);

4 Go!

server.start();

代碼已經簡單到不忍直視了。有同學要問了,那原來那一堆配置文件能改動的參數,現在應該咋弄啊。

$3.2 Jetty的配置

在官方文檔上有一個 Like Jetty XML例子 。要注意的是這個例子給出來的是修改jetty本身的默認參數的,對于web.xml里面的那種參數和 Spring 集成相關的參數不一樣。也就是說,這個是類似于修改tomcat的參數。

  • 設置http的參數
    // HTTP Configuration
    HttpConfiguration http_config = new HttpConfiguration();
    http_config.setSecureScheme("https");
    http_config.setSecurePort(8443);
    http_config.setOutputBufferSize(32768);
    http_config.setRequestHeaderSize(8192);
    http_config.setResponseHeaderSize(8192);
    http_config.setSendServerVersion(true);
    http_config.setSendDateHeader(false);
    ServerConnector http = new ServerConnector(server,
            new HttpConnectionFactory(http_config));
    http.setPort(8080);
    http.setIdleTimeout(30000);
    server.addConnector(http);

其他的配置不展開了,因為我也沒有好好研究。遇上了參考著改就行。

$3.3 Jetty處理REQUEST的過程

一言以蔽之,就是你設置的那一堆handler被一個個地調用。

  • HandlerCollection 會每個都調用,哪怕某個Handler拋了Exception,或者 baseRequest.isHandled()==true
  • HandlerList 在執行過程中,如果哪個handler掛了就不繼續了,或者 baseRequest.isHandled()==true 也不繼續了。
    上面示例中給出的 HandlerList 包含了
  • RequestLogHandler ,這個就是Request的log記錄,當然你也可以用log4j之類的

    NCSARequestLog requestLog = new AsyncNCSARequestLog();
    requestLog.setFilename("./jetty-yyyy_mm_dd.log");
    requestLog.setExtended(false);
    RequestLogHandler requestLogHandler = new RequestLogHandler();
    requestLogHandler.setRequestLog(requestLog);
  • RewriteHandler ,用做UrlRewrite。如果 RequestLogHandler 放在 RewriteHandler 之后,會看到Http請求的Url被轉換了。

    //urlrewrite handler
    RewriteHandler rewrite = new RewriteHandler();
    
    RewritePatternRule oldToNew = new RewritePatternRule();
    oldToNew.setPattern("/some/old/spingMvcHandler");
    oldToNew.setReplacement("/someAction?val1=old&val2=spingMvcHandler");
    rewrite.addRule(oldToNew);
    
    RewriteRegexRule reverse = new RewriteRegexRule();
    reverse.setRegex("/reverse/([^/]*)/(.*)");
    reverse.setReplacement("/reverse/$2/$1");
    rewrite.addRule(reverse);
  • ServletContextHandler ,這個是我用來接入SpringMVC的Handler,并且排在最后一個。在初始化這個Handler的時候要注意同時初始化好 Spring 。

    ServletContextHandler spingMvcHandler = new ServletContextHandler();
    spingMvcHandler.setContextPath("/some");
    XmlWebApplicationContext context = new XmlWebApplicationContext();
    context.setConfigLocations(new String[]{"classpath:config/spring/appcontext-bean.xml"});
    spingMvcHandler.addEventListener(new ContextLoaderListener(context));
    spingMvcHandler.addServlet(new ServletHolder(new DispatcherServlet(context)), "/*");

用Spring和Tomcat的同學一下子就能看懂了吧,這里的WebApplicationContext傳入的配置文件就是spring的配置文件;EventListener就是ContextLoaderListener。

提醒一句,如果是用SpringMVC的注解,千萬記得加上 <mvc:annotation-driven />

appcontext-bean.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
 xmlns:mvc="http://www.springframework.org/schema/mvc"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <context:annotation-config></context:annotation-config>
    <context:component-scan base-package="jetty9"/>
    <mvc:annotation-driven />
</beans>

至此,Request進入到SpringMVC的Controller了。

$4 用SpringMVC處理Http Request

$4.1 通過一堆Annotation獲取到任何你想要的Http參數

@RestController
public class MySpringMVC {
    @RequestMapping(value = "/greeting/{who}", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseStatus(code = HttpStatus.OK)
    public GreetingVO greeting(@RequestParam String name,
 @PathVariable String who,
 @RequestHeader("id") long id,
 @CookieValue("JSESSIONID") String cookie) {
        return new GreetingVO(id, who, name);
    }
}
  • @RequestParam 用來獲取GET的參數,或者HttpHeader中 Content-Type: application/x-www-form-urlencoded 的POST參數
  • @RequestBody 一般用來獲取Post時Content-Type不是application/x-www-form-urlencoded的參數
  • @PathVariable URI中的參數
  • @RequestHeader 和 @CookieValue 不言而喻
  • @ResponseStatus 用來標記返回Http的Code,
  • @RestController 是@Controller和@ResponseBody的組合,默認是json格式返回,記得加上下面的maven依賴
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-core</artifactId>
  <version>2.2.0</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.2.0</version>
</dependency>

$4.2 有潔癖的,或者喜歡從底層處理的朋友

直接拿到 RequestEntity ,拿Request Header,拿Request Body。構造ResponseEntity, Set Response Header和Response Body。應有盡有,想干嘛干嘛啊

@RequestMapping(value = "/test", method = {RequestMethod.POST, RequestMethod.GET})
public ResponseEntity<String> handle(HttpEntity<byte[]> requestEntity) throws UnsupportedEncodingException {
    String id = requestEntity.getHeaders().getFirst("id");

    byte[] requestBody = requestEntity.getBody();
    MediaType contentType = requestEntity.getHeaders().getContentType();
    // do something with request header and body
    if (contentType.equals(MediaType.APPLICATION_JSON_UTF8_VALUE)) {
        String requestBodyStr = new String(requestBody, "UTF-8");
    }

    HttpHeaders responseHeaders = new HttpHeaders();
    responseHeaders.set("MyResponseHeader", "MyValue");
    return new ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.CREATED);
}

來自: https://blog.huachao.me/2016/1/用Embedded Jetty和SpringMVC打造Http Rest Server/

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!