在Android上做服務端開發/Web開發/SpringMVC開發 - 新聞資(zī)訊 - 雲南小程序開發|雲南軟件開發|雲南網站(zhàn)建設-西山區知普網絡科技工作室

159-8711-8523

雲南網建設/小程序開發/軟件開發

知識

不管是網站(zhàn),軟件還是小程序,都要直接或間接能為您産生價值,我們在追求其視覺表現的同時,更側重于功能的便捷,營銷的便利,運營的高效,讓網站(zhàn)成為營銷工具,讓軟件能切實提升企業(yè)内部管理水平和(hé)效率。優秀的程序為後期升級提供便捷的支持!

您當前位置>首頁 » 新聞資(zī)訊 » 網站(zhàn)建設 >

在Android上做服務端開發/Web開發/SpringMVC開發

發表時間:2018-9-26

發布人:葵宇科技

浏覽次數:49

一部分Android開發者看到這個(gè)标題時可(kě)能有點疑惑,SpringMVC不是用來做JavaWeb開發的嗎?難道被移植到Android上來了?答案是否定的,因為SpringMVC是基于Servlet的,在Android上開發一個(gè)支持Servlet的容器(qì)(Tomcat、JBoss)可(kě)不簡單,所以我們是在Android上開發了一套全新的WebServer + WebFramework。

AndServer2.0基于編譯時注解實現了SpringMVC的大部分注解Api,其Request的分發流程也基本和(hé)SpringMVC一緻,與SpringMVC最大的不同是SpringMVC基于運行時注解,并且SpringMVC提供的功能更多更強大。不過AndServer提供的功能在Android上來做服務端開發是完全足夠的。

看到這裡讀者朋友應該知道了,AndServer2.0是使用注解開發Web程序的,為了有個(gè)更直觀的了解,我們先看一個(gè)模拟用戶登錄的Http Api:

@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,那麼通(tōng)過http://192.168.1.11:8080/login就可(kě)以訪問(wèn)該登錄Http Api了。

感興趣的讀者可(kě)以幫我們做一下(xià)Code Review:
https://github.com/yanzhenjie/AndServer

下(xià)文(wén)将依次介紹以下(xià)三點:

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

1. 系統層架構

我們都知道Http是根據Http協議使用Socket做了連接屬性、數據格式、交互邏輯方面的包裝,我們來模拟一段服務端啟動(dòng)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監聽了某個(gè)端口,當有Socket連接上來的時候去把這個(gè)Socket解析為HttpConnection,解析過程是按照Http協議拟定的格式,從SocketInputStream讀取一些數據後,用Request和(hé)Response包裝Socket和(hé)未讀取的流(比如(rú)标記下(xià)次讀取流的起點),下(xià)文(wén)會再提到。

接着HttpParserHttpConnection包裝了Request和(hé)Reponse返回,可(kě)想而知,作為服務端程序,HttpConnection至少(shǎo)包涵了Request和(hé)Response對象:

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

    ...
}

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

public class HttpDispatcher {

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

}

HttpThead裡面,當線程被喚起時隻需要負責調用HttpDispatcher#diaptch()即可(kě),到這裡就比較清晰了,隻需要HttpDispatcher把當前Request派發到對應的Html File或者Java Method處理就可(kě)以了,具體的處理就屬于HttpFramework的事,我們下(xià)文(wén)再講。

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

系統層架構圖

系統層運行時流程圖:

系統層運行流程圖

上圖中(zhōng),Handler表示處理請求的操作手柄,可(kě)能是Html File或者Java Method。值得高興的一點是,在我們疊代了幾個(gè)版本後,發現Apache組織提供了上述藍圖中(zhōng)的HttpParser層,因此為了穩定性和(hé)節省人力我們已經替換該層為Apache的實現。

2. 應用層架構

應用層就是上文(wén)中(zhōng)提到的WebFramework,也就是上一個(gè)小節流程圖的Framework層,包括了Session的處理、Cookie的處理、Cache的處理等。

接着上文(wén),HttpDispatcher需要把當前Request派發到對應的Html File或者Java Method處理,而Handler代表了Html File或者Java Method,因為此二者區别極大,用一個(gè)類來表示它們顯然有些不合理,于是我們想到了使用Adapter模式,所以有了一個(gè)抽象類RequestHandler

public abstract class RequestHandler {

    public abstract void handle(Request request, Response response);
}

RequestHandler可(kě)以表示任何文(wén)件或者Java Method,HttpDispatcher的作用是分發請求到各個(gè)資(zī)源,所以HttpDispatcher不應該來分析某個(gè)RequestHandler具體是什麼東西,它應該直接調用RequestHandler來處理請求,因為Html File或者Java Method對應的RequestHandler在實現上顯然大有不同,所以這裡适用Adapter模式,于是我們用HandlerAdapter去做RequestHandler的适配:

public class HandlerAdapter {

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

    ...
}

HandlerAdapter除了能獲取RequestHandler之外,還需要做一些描述性的工作,好讓HttpDispatcher知道當前适配的RequestHandler是可(kě)以處理正要分發的這個(gè)Request的。

因為Html File和(hé)Java Method的返回值又是大相徑庭,因為返回值是輸出到客戶端展示的,所以我們把返回值抽象為View

public class View {

    public Object output() {
        ...
    }

    ...
}

如(rú)上所以,output()方法就是獲取Handler輸出的内容,還有其他方法是對這個(gè)輸出的描述,這裡不例舉。

因為View是返回值,沒有具體的交互了,所以不适用Adapter模式了,因此我們必須有一個(gè)處理返回值的機制,把處理返回值的機制叫做ViewResolver

public class ViewResolver {

    public void resolver(View view, Request request, Response response) {
        ...
    }
}

ViewResolver中(zhōng)根據輸出内容的類型不同,處理方式也不同,最終把輸出内容通(tōng)過Response對象寫出去,底層是使用上文(wén)中(zhōng)提到的被Response包裝的Socket寫出。

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

應用層架構圖

應用層運行時流程圖:

應用層運行流程圖

上圖中(zhōng),Interceptor表示對請求的攔截器(qì),比如(rú)可(kě)以做一些不允許沒登錄或者沒權限的請求進入的工作。ExceptionResolver表示全局異常處理器(qì),比如(rú)某個(gè)Api發生了異常,會轉到ExceptionResolver中(zhōng)處理,而不至于當前請求不響應或者響應了不想被客戶端看到的消息。

另外需要補充的是,上文(wén)中(zhōng)提到的都是粗略的設計,中(zhōng)間還有一些細節,例如(rú)Session的處理、Cookie的處理、緩存的處理等都未提到,其中(zhōng)任何一個(gè)知識點單獨拿出來都可(kě)以寫一篇文(wén)章,由于篇幅關(guān)系這裡不做詳細介紹。

架構設計和(hé)流程到此就都介紹完了,有興趣的開發者也可(kě)以自己實現一下(xià)。

3. 使用示例

AndServer對于方便使用的理念是:隻需要添加注解即可(kě),不需要再做額外的配置。所以除了像文(wén)章開頭那樣用注解寫好Api之外,隻需要指定監聽端口啟動(dòng)服務器(qì)就可(kě)以了。

與讀者做個(gè)約定,下(xià)文(wén)中(zhōng)服務器(qì)Address都是192.168.1.11,監聽的端口是8080

3.1. 網站(zhàn)部署示例

我們先來部署一個(gè)位于Assets中(zhōng)/web下(xià)的網站(zhàn):

@Website
public class InternalWebsite extends AssetsWebsite {

    public InternalWebsite() {
        super("/web");
    }
}

因此SD的文(wén)件可(kě)以删除也可(kě)以增加,所以很方便做一些文(wén)件的熱插拔,部署SD卡的網站(zhàn):

@Website
public class InternalWebsite extends StorageWebsite {

    public InternalWebsite() {
        super("/sdcard/AndServer/web");
    }
}

如(rú)上所示,開發者隻需要将網站(zhàn)所在的路(lù)徑告訴AndServer,并添加Website注解即可(kě),該網站(zhàn)的Html、CSS、JS、其它文(wén)件都可(kě)以被訪問(wèn),例如(rú)/web目錄下(xià)有一個(gè)index.html文(wén)件,那麼訪問(wèn)地址就是http://192.168.1.11:8080/或者http://192.168.1.11:8080/index.html

3.2. Http Api開發示例

在文(wén)章開頭我們看了一個(gè)模拟用戶的Http Api,下(xià)面我們增加一個(gè)模拟獲取用戶信息的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;
    }
}

于是我們得到兩個(gè)地址:

POST http://192.168.1.11:8080/user/login
GET  http://192.168.1.11:8080/user/info/uid_001

接下(xià)來什麼配置都不用做,隻需要啟動(dòng)服務器(qì),這幾個(gè)地址就可(kě)以用了。

3.3. 啟動(dòng)服務器(qì)

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

如(rú)上所示隻需要指定要監聽的服務端地址和(hé)端口,啟動(dòng)服務器(qì)就可(kě)以訪問(wèn)上面的所有地址了,不需要再做其他配置。

由于篇幅關(guān)系,本文(wén)到此結束,介紹的比較粗略,有興趣的開發者可(kě)以看看項目使用文(wén)檔:
https://www.yanzhenjie.com/AndServer

相關(guān)案例查看更多