58同城开源的轻量级web框架 https://github.com/58code/Argo
随着公司规模的不断扩大,项目越来越多了,单个项目投入的人也多了起来,每个程序员都有自己的一套编码风格。身为一个处女座程序员,深深感觉到无法忍受一团乱麻似的工程。于是就需要一套强有力的规范,而且规范最好能够分级,最低层的规范最为严格,导致大家写出的代码都能差不多,也就减少人员交叉过渡的成本,越靠近业务顶层的规范就越松散、又根据业务相互隔离、可插拔,这样一来,即使无法维护,重写的成本也会降低。
一个自己定义的web框架所要完成的任务恰好包括了从低到高的全部过程,如果您的公司已经完成了服务化架构,那么web也就剩下从中高层到顶层的过程。Argo就是只关注解耦后的业务层web框架,服务化框架(分布式通讯中间件)是另一个开源项目,叫Gaea。
58开源的官方微博http://weibo.com/58code
controller一个必不可少的功能:拦截器。
上一篇中我有一个地方一带而过,就是Router 的默认实现DefaultRouter
this.actions = buildActions(argo, controllerClasses, staticAction);
DefaultRouter的构造方法有注解@Inject,实例化是通过Guice
@Inject
public DefaultRouter(Argo argo, @ArgoSystem Set<Class<? extends ArgoController>> controllerClasses, @StaticActionAnnotation Action staticAction) {
this.argo = argo;
argo.getLogger().info("initializing a %s(implements Router)", this.getClass());
this.actions = buildActions(argo, controllerClasses, staticAction);
argo.getLogger().info("%s(implements Router) constructed.", this.getClass());
}
那么这些参数是从哪来的,就是之前提到的com.bj58.argo.inject.ArgoModule,Argo的绑定关系都能在这里找到。
第一个参数Argo的提供实例为
@Provides
@Singleton
private Argo provideArgo() {
return argo;
}
第二个参数带注解的 @ArgoSystem Set<Class<? extends ArgoController>> controllerClasses 提供实例为
@Provides
@ArgoSystem
@Singleton
private Set<Class<? extends ArgoController>> provideControllerClasses() {
return argo.getControllerClasses();
}
第三个参数带注解的 @StaticActionAnnotation Action staticAction ,绑定在configure()方法中。顺便提一下,这个StaticFilesAction是处理静态文件的,跟tomcat中的DefaultServlet一样,只是有个指定的读取路径
bind(Action.class).annotatedWith(StaticActionAnnotation.class)
.to(StaticFilesAction.class);
好了,DefaultRouter的实参来源也知道了,构造方法中buildActions方法的调用,还有一层buildActions方法调用,我们来看看这个
List<Action> buildActions(Set<ArgoController> controllers, Action staticAction) {
List<Action> actions = Lists.newArrayList();
actions.add(staticAction);
for (ArgoController controller : controllers) {
ControllerInfo controllerInfo = new ControllerInfo(controller);
List<ActionInfo> subActions = controllerInfo.analyze();
for(ActionInfo newAction : subActions)
merge(actions, MethodAction.create(newAction));
}
return ImmutableList.copyOf(actions);
}
这个段代码看出,全局变量 private final List<Action> actions; 就是具体的Action集合
代码详细描述了首先把静态资源action加入全局actions中,再把从controller类中解析出的ActionInfo集合合并到actions中。其中 merge(actions, MethodAction.create(newAction)); 这段的MethodAction,就是方法action。
@Override
public ActionResult route(BeatContext beat) {
RouteBag bag = RouteBag.create(beat);
for(Action action : actions) {
RouteResult routeResult = action.matchAndInvoke(bag);
if (routeResult.isSuccess())
return routeResult.getResult();
}
return ActionResult.NULL;
}
上面这段代码是处理请求过程,执行action的matchAndInvoke方法,静态文件action就是读取指定目录的文件返回,方法action需要看看
@Override
public RouteResult matchAndInvoke(RouteBag bag) {
if (!actionInfo.matchHttpMethod(bag))
return RouteResult.unMatch();
Map<String, String> uriTemplateVariables = Maps.newHashMap();
boolean match = actionInfo.match(bag, uriTemplateVariables);
if (!match)
return RouteResult.unMatch();
// PreIntercept
for(PreInterceptor preInterceptor : actionInfo.getPreInterceptors()) {
ActionResult actionResult = preInterceptor.preExecute(bag.getBeat());
if (ActionResult.NULL != actionResult)
return RouteResult.invoked(actionResult);
}
ActionResult actionResult = actionInfo.invoke(uriTemplateVariables);
// PostIntercept
for(PostInterceptor postInterceptor : actionInfo.getPostInterceptors()) {
actionResult = postInterceptor.postExecute(bag.getBeat(), actionResult);
}
return RouteResult.invoked(actionResult);
}
PreInterceptor是前置拦截器,PostInterceptor是后置拦截器,ActionInfo封装时就根据注解把拦截器都加进去了,执行请求的时候再拿出来按顺序走一遍。拦截器的用法也很简单。