像SpringMVC同樣在Android上作Web開發

一部分Android開發者看到這個標題時可能有點疑惑,SpringMVC不是用來作JavaWeb開發的嗎?難道被移植到Android上來了?答案是否認的,由於SpringMVC是基於Servlet的,在Android上開發一個支持Servlet的容器(Tomcat、JBoss)可不簡單,因此咱們是在Android上開發了一套全新的WebServer + WebFramework。html

AndServer2.0基於編譯時註解實現了SpringMVC的大部分註解Api,其Request的分發流程也基本和SpringMVC一致,與SpringMVC最大的不一樣是SpringMVC基於運行時註解,而且SpringMVC提供的功能更多更強大。不過AndServer提供的功能在Android上來作服務端開發是徹底足夠的。java

看到這裏讀者朋友應該知道了,AndServer2.0是使用註解開發Web程序的,爲了有個更直觀的瞭解,咱們先看一個模擬用戶登陸的Http Api:git

@RestController
public class UserController {

    @PostMapping("/login")
    public String login(@RequestParam("account") String account, @RequestParam("password") String password) {

        if(...) {
            return "Successful";
        }
        return "Failed";
    }
}
複製代碼

假設服務端的Address是192.168.1.11,監聽的端口是8080,那麼經過http://192.168.1.11:8080/login就能夠訪問該登陸Http Api了。github

感興趣的讀者能夠幫咱們作一下Code Review:
github.com/yanzhenjie/…web

下文將依次介紹如下三點:緩存

  1. 系統層架構
  2. 應用層架構
  3. 使用示例

1. 系統層架構

咱們都知道Http是根據Http協議使用Socket作了鏈接屬性、數據格式、交互邏輯方面的包裝,咱們來模擬一段服務端啓動Server的代碼:服務器

public void startServer(String address, int port) {
    InetAddress inetAddress = InetAddress.getByName();
    ServerSocket serverSocket = new ServerSocket(8080, 512, inetAddress);
    while (true) {
        Socket socket = serverSocket.accept();

        HttpConnection connection = HttpParser.parse(socket);
        HttpThead thread = new HttpThread(connection);
        thread.start();
    }
}
複製代碼

ServerSocket監聽了某個端口,當有Socket鏈接上來的時候去把這個Socket解析爲HttpConnection,解析過程是按照Http協議擬定的格式,從SocketInputStream讀取一些數據後,用RequestResponse包裝Socket和未讀取的流(好比標記下次讀取流的起點),下文會再提到。架構

接着HttpParserHttpConnection包裝了RequestReponse返回,可想而知,做爲服務端程序,HttpConnection至少包涵了RequestResponse對象:app

public class HttpConnection {
    private Request mRequest;
    private Response mResponse;

    ...
}
複製代碼

緊接着啓動了一個線程去處理當前鏈接,其實也就是處理當前Request,用Response寫出數據,怎麼處理這個Request是一個WebFramework的核心,做爲Http服務端程序,應該能提供Html文件、JS文件、Java Method(Http Api)等讓客戶端訪問,所以得有一個管理員來負責請求和資源的匹配,因此有一個叫作HttpDispatcher的類來決定這個Request應該發給哪一個資源去處理:socket

public class HttpDispatcher {

    public void dispath(Request request, Response response) {
        ...
    }

}
複製代碼

HttpThead裏面,當線程被喚起時只須要負責調用HttpDispatcher#diaptch()便可,到這裏就比較清晰了,只須要HttpDispatcher把當前Request派發到對應的Html File或者Java Method處理就能夠了,具體的處理就屬於HttpFramework的事,咱們下文再講。

這就是一個簡單的WebServer的藍圖,咱們根據設想畫出了系統層架構圖:

系統層架構圖

系統層運行時流程圖:

系統層流程圖

上圖中,Handler表示處理請求的操做手柄,多是Html File或者Java Method。值得高興的一點是,在咱們迭代了幾個版本後,發現Apache組織提供了上述藍圖中的HttpParser層,所以爲了穩定性和節省人力咱們已經替換該層爲Apache的實現。

2. 應用層架構

應用層就是上文中提到的WebFramework,也就是上一個小節流程圖的Framework層,包括了Session的處理、Cookie的處理、Cache的處理等。

接着上文,HttpDispatcher須要把當前Request派發到對應的Html File或者Java Method處理,而Handler表明了Html File或者Java Method,由於此兩者區別極大,用一個類來表示它們顯然有些不合理,因而咱們想到了使用Adapter模式,因此有了一個抽象類RequestHandler

public abstract class RequestHandler {

    public abstract void handle(Request request, Response response);
}
複製代碼

RequestHandler能夠表示任何文件或者Java Method,HttpDispatcher的做用是分發請求到各個資源,因此HttpDispatcher不該該來分析某個RequestHandler具體是什麼東西,它應該直接調用RequestHandler來處理請求,由於Html File或者Java Method對應的RequestHandler在實現上顯然大有不一樣,因此這裏適用Adapter模式,因而咱們用HandlerAdapter去作RequestHandler的適配:

public class HandlerAdapter {

    public RequestHandler getHandler(Request request) {
        ...
    }

    ...
}
複製代碼

HandlerAdapter除了能獲取RequestHandler以外,還須要作一些描述性的工做,好讓HttpDispatcher知道當前適配的RequestHandler是能夠處理正要分發的這個Request的。

由於Html File和Java Method的返回值又是截然不同,由於返回值是輸出到客戶端展現的,因此咱們把返回值抽象爲View

public class View {

    public Object output() {
        ...
    }

    ...
}
複製代碼

如上因此,output()方法就是獲取Handler輸出的內容,還有其餘方法是對這個輸出的描述,這裏不例舉。

由於View是返回值,沒有具體的交互了,因此不適用Adapter模式了,所以咱們必須有一個處理返回值的機制,把處理返回值的機制叫作ViewResolver

public class ViewResolver {

    public void resolver(View view, Request request, Response response) {
        ...
    }
}
複製代碼

ViewResolver中根據輸出內容的類型不一樣,處理方式也不一樣,最終把輸出內容經過Response對象寫出去,底層是使用上文中提到的被Response包裝的Socket寫出。

這就是一個簡單的WebFramework的藍圖,咱們根據設想畫出了應用層架構圖:

應用層架構圖

應用層運行時流程圖:

應用層流程圖

上圖中,Interceptor表示對請求的攔截器,好比能夠作一些不容許沒登陸或者沒權限的請求進入的工做。ExceptionResolver表示全局異常處理器,好比某個Api發生了異常,會轉到ExceptionResolver中處理,而不至於當前請求不響應或者響應了不想被客戶端看到的消息。

另外須要補充的是,上文中提到的都是粗略的設計,中間還有一些細節,例如Session的處理、Cookie的處理、緩存的處理等都未提到,其中任何一個知識點單獨拿出來均可以寫一篇文章,因爲篇幅關係這裏不作詳細介紹。

架構設計和流程到此就都介紹完了,有興趣的開發者也能夠本身實現一下。

3. 使用示例

AndServer對於方便使用的理念是:只須要添加註解便可,不須要再作額外的配置。因此除了像文章開頭那樣用註解寫好Api以外,只須要指定監聽端口啓動服務器就能夠了。

與讀者作個約定,下文中服務器Address都是192.168.1.11,監聽的端口是8080

3.1. 網站部署示例

咱們先來部署一個位於Assets中/web下的網站:

@Website
public class InternalWebsite extends AssetsWebsite {

    public InternalWebsite() {
        super("/web");
    }
}
複製代碼

所以SD的文件能夠刪除也能夠增長,因此很方便作一些文件的熱插拔,部署SD卡的網站:

@Website
public class InternalWebsite extends StorageWebsite {

    public InternalWebsite() {
        super("/sdcard/AndServer/web");
    }
}
複製代碼

如上所示,開發者只須要將網站所在的路徑告訴AndServer,並添加Website註解便可,該網站的Html、CSS、JS、其它文件均可以被訪問,例如/web目錄下有一個index.html文件,那麼訪問地址就是http://192.168.1.11:8080/或者http://192.168.1.11:8080/index.html

3.2. Http Api開發示例

在文章開頭咱們看了一個模擬用戶的Http Api,下面咱們增長一個模擬獲取用戶信息的Api:

@RequestMapping("/user")
@RestController
public class UserController {

    @PostMapping("/login")
    public String login(@RequestParam("account") String account, @RequestParam("password") String password) {

        if(...) {
            return "Successful";
        }
        return "Failed";
    }

    @GetMapping("/info/{userId}")
    public User info(@PathVariable("userId") String userId) {
        User user = ...;
        return user;
    }
}
複製代碼

因而咱們獲得兩個地址:

POST http://192.168.1.11:8080/user/login
GET  http://192.168.1.11:8080/user/info/uid_001
複製代碼

接下來什麼配置都不用作,只須要啓動服務器,這幾個地址就能夠用了。

3.3. 啓動服務器

AndServer.serverBuilder()
    .inetAddress(NetUtils.getLocalIPAddress())
    .port(8080)
    .timeout(10, TimeUnit.SECONDS)
    .build()
    .start();
複製代碼

如上所示只須要指定要監聽的服務端地址和端口,啓動服務器就能夠訪問上面的全部地址了,不須要再作其餘配置。

因爲篇幅關係,本文到此結束,介紹的比較粗略,有興趣的開發者能夠看看項目使用文檔:
www.yanzhenjie.com/AndServer

相關文章
相關標籤/搜索