一部分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
下文将依次介绍如下三点:缓存
咱们都知道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协议拟定的格式,从Socket
的InputStream
读取一些数据后,用Request
和Response
包装Socket
和未读取的流(好比标记下次读取流的起点),下文会再提到。架构
接着HttpParser
用HttpConnection
包装了Request
和Reponse
返回,可想而知,做为服务端程序,HttpConnection
至少包涵了Request
和Response
对象: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的实现。
应用层就是上文中提到的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的处理、缓存的处理等都未提到,其中任何一个知识点单独拿出来均可以写一篇文章,因为篇幅关系这里不作详细介绍。
架构设计和流程到此就都介绍完了,有兴趣的开发者也能够本身实现一下。
AndServer对于方便使用的理念是:只须要添加注解便可,不须要再作额外的配置。因此除了像文章开头那样用注解写好Api以外,只须要指定监听端口启动服务器就能够了。
与读者作个约定,下文中服务器Address都是
192.168.1.11
,监听的端口是8080
。
咱们先来部署一个位于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
。
在文章开头咱们看了一个模拟用户的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
复制代码
接下来什么配置都不用作,只须要启动服务器,这几个地址就能够用了。
AndServer.serverBuilder()
.inetAddress(NetUtils.getLocalIPAddress())
.port(8080)
.timeout(10, TimeUnit.SECONDS)
.build()
.start();
复制代码
如上所示只须要指定要监听的服务端地址和端口,启动服务器就能够访问上面的全部地址了,不须要再作其余配置。
因为篇幅关系,本文到此结束,介绍的比较粗略,有兴趣的开发者能够看看项目使用文档:
www.yanzhenjie.com/AndServer