文档章节

JFinal几个组件的总结性发言

hengqihengyi
 hengqihengyi
发布于 2016/04/04 22:52
字数 1726
阅读 61
收藏 0

JFinal是一个轻量级的MVC、ORM、AOP框架,作为后端开发的微内核全方位扩展的新一代框架,其易用性和开发效率都是难以比肩的,其插件体系和Db+activeRecord架构也非常好。它主要由JFinalFilter、JFinalConfig、Handler、Interceptor、Controller、Render、Plugin等部分构成。每个部分都是基于接口实现的,支持完整的自定义,使用灵活,扩展性强。

其基本运行原理:

从一次新增页面的add操作请求来看:

1. 首先是客户端点击新增按钮,提交一个新增请求,请求中会带上服务端处理地址url

2. 所有请求都会被JFinalFilter拦截,然后调用Handler进行详细处理

3. Handler是一个链条形式的调用,包括0-n个自定义Handler,以及最后一个ActionHandler,依次执行,当然可以自定义跳出。

4. 进入ActionHandler后,首先会根据请求的target从缓存的ActionMapping中映射获取到具体操作对应的Action对象,这个对象里面封装了方法名、方法上面的拦截器,方法所在的Controllercontroller上面的拦截器等。然后根据Action以及Controller实例构造出ActionInvocation。

5. 接下来通过ActionInvocation的invoke进行具体处理,这是一个明显的Command模式的实现。首先是拦截器的调用,拦截器调用中会重新回调ActionInvocation的invoke,当拦截器调用完毕后,会调用当前操作的method

6. 当进入具体controller的新增方法add时,调用基类的getModel(Systemparam.class);这个方法会从request中解析出所需要的数据,通过反射设置给具体的Model实体类,最终通过ActiveRecord来进行数据存储

7. 最后是页面渲染rerender


Controller的主要作用是从Request中获取参数,往Request和Session中放入属性、Cookie,使用ModelInjector的inject方法从Request中获取model{网页中的格式为user.name,user.age},该方法有两个分支,如果model继承于Model则调用set(attrName,attrValue),否则就是普通的JavaBean,调用setAttrName(attr)。

Controller调用cos框架获取上传的文件,封装成MultiPartRequest(继承于HttpServletRequestWrapper),该类可以获取文件或者参数。


Handler是处理器,构成责任链,因为在handle方法中获取到了Request和Response,所以它具备对请求的完全控制权。在JFinalFilter的init方法中,构建了这个链条,最后一个handler是ActionHandler,并获取到第一个handler,其配置由JFinalConfig的configHandler完成。


Action就代表Controller中的一个方法,包括Controller这个Class,controllerKey,actionKey,Method,methodName,interceptors,viewPath,是一个简单的只读JavaBean。

ActionHandler中的handle方法中调用controller的init方法,通过Invocation调用controller之中的Interceptor,然后调用controller的getRender获取Render,之后判断是不是ActionRender,避免调用相同的controller的相同方法造成死循环。因为在一般的controller的方法中都会调用controller中的render()方法系列,将controller中的render引用为某render,故在ActionHandler的handler方法最后调用render.render()就会调用不同render的render方法。在配置Constans的时候调动setMainRenderFactory,就能设置程序的主Render工厂,以后获取的就是这个工厂生产的Render,这就是典型的工厂方法模式。


Render代表渲染器,拥有request和response的引用,所以render()方法中就能作不同的处理,例如FileRender就会调用response的setHeader(“Content-disposition”,“attachment;filename=...”)等以实现文件下载;BeetlRender就会调用加载模板和绑定属性哪些方法(见以下分析)。


关于自动扫描classpath和jar包中的AutoBindRoutes的机制:

首先JFinalConfig中的configRoute(Routes me)方法中调用me.add(new AutoBindRoutes());其源码为

public Routes add(Routes routes) {
if (routes != null) {
routes.config();// very important!!!
map.putAll(routes.map);
viewPathMap.putAll(routes.viewPathMap);
}
return this;
}

使得Routes的map和viewPathMap中添加了AutoBindRoutes的map和viewPathMap,这个方法中,调用了routes.config方法,这个方法就要求你自己配置你的map和viewPathMap,这个是典型的模板方法模式。AutoBindRoutes继承于Routes,其config方法中,

public void config()
 {
     List controllerClasses =ClassSearcher.of(Controller.class).includeAllJarsInLib(this.includeAllJarsInLib).injars(this.includeJars).search();
     ControllerBind
 controllerBind = null;
     for (Class controller : controllerClasses)
       if (!this.excludeClasses.contains(controller))
      {
        controllerBind = (ControllerBind)controller.getAnnotation(ControllerBind.class);
        if (controllerBind == null) {
          if (this.autoScan)
         {
            add(controllerKey(controller), controller);
          } } else if (StrKit.isBlank(controllerBind.viewPath())) {
           add(controllerBind.controllerKey(), controller);
         } else {
           add(controllerBind.controllerKey(), controller, controllerBind.viewPath());
        }
      }
 }

首先委托ClassSearcher去ClassPath和Jar包中去扫描Controller的子类(要求以Controller结尾,是COC原则),然后检查该类上是否含有ControllerBind(强制修改路径的)注解,有的话就按照ControllerBind的路径执行,否则按照默认的规则执行。ClassSearcher.search方法就自己想吧,代码不是很复杂。

至此,AutoBindRoutes中含有了Controller的信息,在JFinalFilter的init方法中

if (jfinal.init(jfinalConfig, filterConfig.getServletContext()) == false)
throw new RuntimeException("JFinal init error!");

init方法里面

boolean init(JFinalConfig jfinalConfig, ServletContext servletContext) {
this.servletContext = servletContext;
this.contextPath = servletContext.getContextPath();

initPathUtil();

Config.configJFinal(jfinalConfig);// start plugin and init logger factory in this method
constants = Config.getConstants();

initActionMapping();
initHandler();
initRender();
initOreillyCos();
initTokenManager();

return true;
}

configJFinal方法中

static void configJFinal(JFinalConfig jfinalConfig) {
jfinalConfig.configConstant(constants);initLoggerFactory();
jfinalConfig.configRoute(routes);
jfinalConfig.configPlugin(plugins);startPlugins();// very important!!!
jfinalConfig.configInterceptor(interceptors);
jfinalConfig.configHandler(handlers);
}

这个方法中调用JFinalConfig的configRoutes就会调用前面说的那些方法。此时Config类中就保留了Routes信息。

initActionMapping方法中

private void initActionMapping() {
actionMapping = new ActionMapping(Config.getRoutes(), Config.getInterceptors());
actionMapping.buildActionMapping();
}

就用Config的getRoutes获取Routes构造出actionMapping,然后调用buildActionMapping

void buildActionMapping() {
mapping.clear();
Set<String> excludedMethodName = buildExcludedMethodName();
ActionInterceptorBuilder interceptorBuilder = new ActionInterceptorBuilder();
Interceptor[] globalInters = interceptors.getGlobalActionInterceptor();
interceptorBuilder.addToInterceptorsMap(globalInters);
for (Entry<String, Class<? extends Controller>> entry : routes.getEntrySet()) {
Class<? extends Controller> controllerClass = entry.getValue();
Interceptor[] controllerInters = interceptorBuilder.buildControllerInterceptors(controllerClass);

boolean sonOfController = (controllerClass.getSuperclass() == Controller.class);
Method[] methods = (sonOfController ? controllerClass.getDeclaredMethods() : controllerClass.getMethods());
for (Method method : methods) {
String methodName = method.getName();
if (excludedMethodName.contains(methodName) || method.getParameterTypes().length != 0)
continue ;
if (sonOfController && !Modifier.isPublic(method.getModifiers()))
continue ;

Interceptor[] methodInters = interceptorBuilder.buildMethodInterceptors(method);
Interceptor[] actionInters = interceptorBuilder.buildActionInterceptors(globalInters, controllerInters, methodInters, method);
String controllerKey = entry.getKey();

ActionKey ak = method.getAnnotation(ActionKey.class);
String actionKey;
if (ak != null) {
actionKey = ak.value().trim();
if ("".equals(actionKey))
throw new IllegalArgumentException(controllerClass.getName() + "." + methodName + "(): The argument of ActionKey can not be blank.");

if (!actionKey.startsWith(SLASH))
actionKey = SLASH + actionKey;
}
else if (methodName.equals("index")) {
actionKey = controllerKey;
}
else {
actionKey = controllerKey.equals(SLASH) ? SLASH + methodName : controllerKey + SLASH + methodName;
}

Action action = new Action(controllerKey, actionKey, controllerClass, method, methodName, actionInters, routes.getViewPath(controllerKey));
if (mapping.put(actionKey, action) != null)
throw new RuntimeException(buildMsg(actionKey, controllerClass, method));
}
}

// support url = controllerKey + urlParas with "/" of controllerKey
Action actoin = mapping.get("/");
if (actoin != null)
mapping.put("", actoin);
}

这里面就涉及Before、Clear、ActionKey的执行逻辑(略)。


AmazeUI是一个前端框架,主要提供一些组件和布局。

beetl是新一代Java模板引擎,在后端执行,通过ResoureLoader和Configeration获取模板,编译后通过绑定数据渲染出需要的页面。与JFinal的整合就是通过注册一个BeetlRenderFactory(me.setMainRenderFactory(new BeetlRenderFactory());),如此在渲染的时候就生成BeetlRender进行渲染,当然也会执行beetl的一些步骤。通过看源码知道,BeetlRenderFactory.getRender方法返回一个BeetlRender,他继承于Render,在render方法中

this.response.setContentType(contentType);
WebRender webRender = new WebRender(this.gt);
webRender.render(this.view, this.request, this.response, new Object[0]);

这个render方法中

Enumeration attrs = request.getAttributeNames();
while (attrs.hasMoreElements())
 {
String attrName = (String)attrs.nextElement();
template.binding(attrName, request.getAttribute(attrName));
}

WebVariable webVariable = new WebVariable();
webVariable.setRequest(request);
webVariable.setResponse(response);
template.binding("session", new SessionWrapper(request.getSession(false)));

template.binding("servlet", webVariable);
template.binding("request", request);
template.binding("ctxPath", request.getContextPath());

这就是在页面中能够使用request中的数据的奥秘。


© 著作权归作者所有

hengqihengyi
粉丝 0
博文 4
码字总数 3835
作品 0
成都
程序员
私信 提问
JFinal Extensions 2.0 发布,JFinal 扩展

Jfinal-Ext转眼间已经发布半年了。我对jfinal-ext的定位是 一个对jfinal周边设施进行完善的项目,如整合常用的第三方库。目前大部分是我个人的经验总结通过jfinal进行扩展作为技术积累,只有...

绝望的八皮
2013/04/15
4.2K
16
COS 2017.5 发布,Java 文件上传组件

老牌轻量级 java web 文件上传组件 cos 已多年未更新过,cos-2017.5 版本根据 JFinal 用户反馈比较多的几个需求进行了升级: 1:支持文件上传表单域使用同名的 name属性 上例中的 input 表单...

JFinal
2017/05/31
3.1K
38
建议以依赖的方式,而非copy的方式使用jfinal代码

Springblade 的技术组合是 spring+springmvc+beetl+beetlsql+shiro,刚看了一下源码,其中大量直接 copy 的 jfinal 源码,例如 render 模块、json 模块相当于整模块原封不动地 copy 使用。项...

JFinal
2016/09/05
1K
15
关于jfinal的Model组件的一点小纠结

写jfinal有一段时间了,里面的Controller 还是写的非常赞,轻松处理请求业务。但是有个小小纠结的就是jfinal的 Model 组件,虽然看起来节省了代码,但是对于稍微复杂的字段超过4-5个的model...

LICHUAN
2013/08/09
3.6K
9
JFinal OA的相关问题

JFinal OA我也在本地跑了下,但是有两个问题求助大家: JFinal OA的前端框架有没有EXT版本? JFinal OA的流程组件能否集成activiti ?

部分红薯进城
2013/07/05
1K
3

没有更多内容

加载失败,请刷新页面

加载更多

JMM内存模型(一)&volatile关键字的可见性

在说这个之前,我想先说一下计算机的内存模型: CPU在执行的时候,肯定要有数据,而数据在内存中放着呢,这里的内存就是计算机的物理内存,刚开始还好,但是随着技术的发展,CPU处理的速度越...

走向人生巅峰的大路
24分钟前
57
0
你对AJAX认知有多少(2)?

接着昨日内容,我们几天继续探讨ajax的相关知识点 提到ajax下面几个问题又是必须要了解的啦~~~ 8、在浏览器端如何得到服务器端响应的XML数据。 通过XMLHttpRequest对象的responseXMl属性 9、 ...

理性思考
34分钟前
4
0
正则表达式基础(一)

1.转义 转义的作用: 当某个字符在表达式中具有特殊含义,例如字符串引号中出现了引号,为了可以使用这些字符本身,而不是使用其在表达式中的特殊含义,则需要通过转义符“\”来构建该字符转...

清自以敬
37分钟前
4
0
idea中@Data标签getset不起作用

背景:换电脑以后在idea中有@data注解都不生效 解决办法:idea装个插件 https://blog.csdn.net/seapeak007/article/details/72911529...

栾小糖
42分钟前
5
0
Apache Kudu 不能删除不存在的数据

使用Apache Kudu客户端,对KafkaConnect Sink 进行扩展。 使用的Apache Kudu 的Java 客户端。突然有天发现作业无法提交,一直报错。 后来才发现这是Kudu自身的一种校验机制。为了忽略这种校验...

吐槽的达达仔
53分钟前
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部