android基於註解實現類似spring服務端框架
阿新 • • 發佈:2018-12-16
用過spring或Retrofit的人都知道實現函式和http請求的繫結和解耦非常方便,這裡分享一下基於NanoHttpd實現的簡單註解框架。 第一步定義註解類:
//http控制類,被該註解的類用來處理http請求 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Controller { //路徑字首,有此字首的請求會使用此類處理 String name() default "/"; boolean needPermissonControl() default true; }
//被該註解的方法用來一個處理http請求
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequestMapping {
//匹配的路徑
String path() default "";
Method method() default Method.GET;
}
//該註解用於函式的引數,用於繫結請求的參 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) public @interface Param { //引數名 String name() default ""; //引數預設值 String value() default ""; }
第二步,實現serverlet時註冊Controll類
@Override public void addController(Class<?> controller) { // has controller annotation if (controller.isAnnotationPresent(Controller.class)) { Controller controllerAnnotation = controller.getAnnotation(Controller.class); String name = controllerAnnotation.name(); boolean needPermissionControl = controllerAnnotation.needPermissonControl(); Method[] methods = controller.getDeclaredMethods(); // get all request mapping annotation for (Method method : methods) { if (method.isAnnotationPresent(RequestMapping.class)) { RequestMapping requestMapping = method.getAnnotation(RequestMapping.class); String path = requestMapping.path(); org.nanohttpd.protocols.http.request.Method m = requestMapping.method(); // build full path String fullPath = name + File.separatorChar + path; // getparams ArrayList<Param> params = new ArrayList<>(); Annotation[][] paramAnnotation = method.getParameterAnnotations(); for (Annotation[] an : paramAnnotation) { if (an.length > 0) { Param p = (Param)an[0]; params.add(p); } } RequestMappingParams requestMappingParams = new RequestMappingParams(); requestMappingParams.path = fullPath; requestMappingParams.handler = controller; requestMappingParams.method = m; requestMappingParams.methodReflect = method; requestMappingParams.params = params; requestMappingParams.needPermissionControl = needPermissionControl; addRoute(requestMappingParams); } } } }
大概思路是在註冊時,找到Controller註解的uri路徑字首,還有有RequestMapping註解的函式,將路徑對應上函式方法,然後在請求來時匹配到該路徑就呼叫對應用的方法,下面是匹配到路徑時的處理:
private Response processController(Object object, Map<String, String> urlParams, IHTTPSession session) throws InvocationTargetException, IllegalAccessException {
if (requestMappingParams.needPermissionControl) {
if (!AppNanolets.PermissionEntries.isRemoteAllow(session.getRemoteIpAddress())) {
return Response.newFixedLengthResponse("not allow");
}
}
//匹配請求方法
if (requestMappingParams.method != session.getMethod()) {
return Response.newFixedLengthResponse(Status.INTERNAL_ERROR, "text/plain", "method not supply");
}
ArrayList<Object> params = new ArrayList<>();
Map<String, List<String>> requestParams = session.getParameters();
if (requestParams != null) {
//獲取對應http請求引數
Type[] types = requestMappingParams.methodReflect.getGenericParameterTypes();
for (int i = 0; i < requestMappingParams.params.size(); i++) {
Param p = requestMappingParams.params.get(i);
if (!TextUtils.isEmpty(p.name())) {
List<String> values = requestParams.get(p.name());
if (values != null && values.size() > 0) {
String v = values.get(0);
Type t = types[i];
params.add(valueToObject(t, v));
} else {
params.add(p.value());
}
}
}
}
//呼叫方法
return (Response)requestMappingParams.methodReflect.invoke(object, params.toArray());
}
下面是Controller的一個例項:
@Controller(name = "filemanager")
public class FileManagerHandler {
@RequestMapping(path= "list")
public Response list(@Param(name = "dir", value = "/sdcard") String path) {
if (TextUtils.isEmpty(path)) {
path = Environment.getExternalStorageDirectory().getAbsolutePath();
}
Dir d = new Dir(new java.io.File(path));
String json = JSON.toJSONString(d);
Response response = Response.newFixedLengthResponse(Status.OK, "application/json", json);
response.addHeader("Access-Control-Allow-Origin", "*");
return response;
}
上面的Controller匹配路徑字首是filemanager就是http://host/filemanager/xxx?xxx這樣的的請求都會使用此類來處理,然後有個list方法處理http://host/filemanager/list?xxx請求,請求引數只有一個dir又引數,預設值是/sdcard,就是沒傳參時會使用此值傳入函式。 全部程式碼在github:Enlarge-Android
這裡使用註解的方法是呼叫反射,效能上會有所降低,可以使用另外一種基於註解自動生成程式碼的方法提高效能。使用註解來繫結http請求好處相當明顯,如果不使用註解,那麼要處理http請求時需要類實現或繼承http請求處理類,並重寫請求處理的方法,以達到得到處理的時機再做相關的邏輯處理,這樣的程式碼顯然多了很多也不靈活。