最幸福的事,莫過於看別人寫的好框架,代碼簡單,功能齊全,設計思路清晰,可讀性強。。。最痛苦的事,莫過於看別人寫的爛代碼,功能簡單,代碼複雜,可讀性差。。。jFinal是不多見的國內寫地很好的框架,該用別人造好輪子的地方,就不重複造輪子、別人寫的很差的地方,就本身寫個更好的。 *php
網上有不少學習jFinal的資料,固然,任何資料都比不上源碼和官方文檔,查閱官方文檔,請移步http://www.jfinal.com/,我讀的是jfinal-2.0-manual.pdfnode
jFinal的運行思路很是清晰。官方介紹的啓動jFinal有兩種方式,第一種,使用jFinal集成的jetty啓動;第二種,使用tomcat啓動,jetty相對tomcat比較小巧,jetty使用nio監聽端口,而tomcat採用bio監聽端口。下面,結合代碼簡單介紹下jFinal的運行。web
容器啓動時,讀取配置文件web.xml,加載核心過濾器spring
<filter> <filter-name>jfinal</filter-name> <filter-class>com.jfinal.core.JFinalFilter</filter-class> <init-param> <param-name>configClass</param-name> <param-value>com.demo.common.DemoConfig</param-value> </init-param> </filter> <filter-mapping> <filter-name>jfinal</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
過濾器初始化配置類,配置類中配置好路由、插件、攔截器、處理器。 com.demo.common.DemoConfig關鍵代碼以下sql
public class DemoConfig extends JFinalConfig { public void configRoute(Routes me) { // 配置路由 } public void configPlugin(Plugins me) { // 配置插件 } public void configInterceptor(Interceptors me) { // 配置全局攔截器 } public void configHandler(Handlers me) { // 配置處理器 } }
com.jfinal.core.JFinal中對Handler進行初始化,關鍵代碼以下設計模式
private void initHandler() { Handler actionHandler = new ActionHandler(actionMapping, constants); handler = HandlerFactory.getHandler(Config.getHandlers().getHandlerList(), actionHandler); }
從代碼中能夠看出,初始化Handler的時候,先構造了一個ActionHandler對象,而後再將用戶自定義的Handler加入,而ActionHandler中,將攔截器,controler和rander以及plugin都分別進行初始化緩存
請求訪問服務器,jFinalFilter拿到請求的控制權,將其交給handler鏈進行處理,handler鏈一層層處理,最終將結果返回給Filter com.jfinal.core.JFinalFilter關鍵代碼以下:tomcat
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest)req; HttpServletResponse response = (HttpServletResponse)res; request.setCharacterEncoding(encoding); String target = request.getRequestURI(); if (contextPathLength != 0) target = target.substring(contextPathLength); boolean[] isHandled = {false}; try { //handler爲鏈式的結構,此處交給多層handler進行處理 handler.handle(target, request, response, isHandled); } catch (Exception e) { if (log.isErrorEnabled()) { String qs = request.getQueryString(); log.error(qs == null ? target : target + "?" + qs, e); } } if (isHandled[0] == false) chain.doFilter(request, response); }
Handler抽象類接口源碼以下服務器
public abstract class Handler { //此處即說明Handler是鏈式結構 protected Handler nextHandler; public abstract void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled); }
讀過框架源代碼,便可很好的理解做者**@James Zhan**爲框架所作的架構圖,圖以下:session
####如下是關於此項目的一些我的的看法
此特性爲jFinal引覺得傲的特性之一,全部的Constant、Pluging、Interceptor、configHandler都在這一個類中配置,這樣能夠達到沒有xml配置文件,不須要像ssh那樣去維護繁瑣的配置文件,以前接觸php、nodejs這些語言的時候,也接觸過這種處理方式,無配置文件,在類中利用route的方式轉發請求。
可是我認爲,小項目如此處理能夠快速開發,可是項目若是大點,繁瑣點,配置類將會變得很大,而且不易讀懂。還有一種解決方案不將全部的配置都放在一個類中,而是根據不一樣的功能和業務,放在不一樣的包中,啓動時將這些類動態加載進來。這樣,開發看到包名便可知道mvc的構成,而不是須要一段一段讀配置的代碼,而且後期加入新的配置也不會對以前已存在配置形成影響。
jFinal配置的初始化也只支持一個類,若是想要使用多個,則不支持。同時routes的配置文檔中說明支持配置多個,便於不一樣開發人員版本維護,constant、pluging等不使用這種方式,多是考慮到這些是全局的,個性化的比較少吧。
com.jfinal.core.JFinalFilter初始化代碼以下
public void init(FilterConfig filterConfig) throws ServletException { //本行代碼讀取配置類,只能配置一個類,能夠優化爲讀取多個或者模糊匹配加載某系列包 createJFinalConfig(filterConfig.getInitParameter("configClass")); if (jfinal.init(jfinalConfig, filterConfig.getServletContext()) == false) throw new RuntimeException("JFinal init error!"); handler = jfinal.getHandler(); constants = Config.getConstants(); encoding = constants.getEncoding(); jfinalConfig.afterJFinalStart(); String contextPath = filterConfig.getServletContext().getContextPath(); contextPathLength = (contextPath == null || "/".equals(contextPath) ? 0 : contextPath.length()); }
jFinal使用很是靈活的方式實現了aop,學習spring的同窗,都會被它的 Aspect、Advice、Joinpoint、Poincut...這些複雜的概念搞暈。 jFinal的攔截器根據級別能夠劃分爲全局攔截器、Inject攔截器、Class攔截器以及Method攔截器,jFinal的aop是靠Iterceptor實現的嗎?咱們看下源碼,Iterceptor只是個普通的接口,代碼以下:
com.jfinal.aop.Interceptor
public interface Interceptor { void intercept(Invocation inv); }
能夠看到,這個接口並無任何特殊之處,那麼aop究竟是如何實現的呢?官方給出使用示例以下。
public class OrderService { // 配置事務攔截器 @Before(Tx.class) public void payment(int orderId, int userId) { // service code here } } // 定義控制器,控制器提供了enhance系列方法可對目標進行AOP加強 public class OrderController extends Controller { public void payment() { // 使用 enhance方法對業務層進行加強,使其具備AOP能力 OrderService service = enhance(OrderService.class); // 調用payment方法時將會觸發攔截器 service.payment(getParaToInt("orderId"), getParaToInt("userId")); } }
能夠看到,特殊之處在於**@Before註解和enhance()**方法,那麼它們究竟作了什麼?咱們繼續深刻研究。
com.jfinal.core.Controller的enhance方法源碼以下:
public <T> T enhance(Class<T> targetClass) { return (T)Enhancer.enhance(targetClass); }
調用了com.jfinal.aop.Enhancer.enhance(),再深刻,看Enhancer..enhance()
public static <T> T enhance(Class<T> targetClass) { return (T)net.sf.cglib.proxy.Enhancer.create(targetClass, new Callback()); }
發現是調用了cglib的方法,動態建立了一個代理對象,而且新建了一個Callback(),進入**Callback()**看源碼。
com.jfinal.aop.Callback實現了cglib的MethodInterceptor接口,intercept方法以下
public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { if (excludedMethodName.contains(method.getName())) { if (method.getName().equals("finalize")) return methodProxy.invokeSuper(target, args); return this.injectTarget != null ? methodProxy.invoke(this.injectTarget, args) : methodProxy.invokeSuper(target, args); } if (this.injectTarget != null) { target = this.injectTarget; Interceptor[] finalInters = InterceptorBuilder.build(injectInters, target.getClass(), method); Invocation invocation = new Invocation(target, method, args, methodProxy, finalInters); invocation.useInjectTarget = true; invocation.invoke(); return invocation.getReturnValue(); } else { Interceptor[] finalInters = InterceptorBuilder.build(injectInters, target.getClass(), method); Invocation invocation = new Invocation(target, method, args, methodProxy, finalInters); invocation.useInjectTarget = false; invocation.invoke(); return invocation.getReturnValue(); } }
發現中間有一行這樣的代碼:InterceptorBuilder.build(injectInters, target.getClass(), method);
進入com.jfinal.aop.InterceptorBuilder看build方法:
public static Interceptor[] build(Interceptor[] injectInters, Class<?> targetClass, Method method) { Interceptor[] methodInters = createInterceptors(method.getAnnotation(Before.class)); 。。。 }
此處獲取了方法的**@Before的annotation**,而且根據annotation建立Interceptor。那麼一切就明瞭了,咱們再回到最開始的地方,加上註釋
public class OrderService { // 配置事務攔截器 @Before(Tx.class) public void payment(int orderId, int userId) { // service code here } } // 定義控制器,控制器提供了enhance系列方法可對目標進行AOP加強 public class OrderController extends Controller { public void payment() { // 使用 enhance方法對業務層進行加強,使其具備AOP能力 //此處,建立代理類,而且在回調方法中獲取@before的配置,並根據配置構造攔截器 OrderService service = enhance(OrderService.class); // 調用payment方法時將會觸發攔截器 // 此處使用的是OrderService的代理類,而且調用時候回調函數中調用攔截器方法執行攔截。 service.payment(getParaToInt("orderId"), getParaToInt("userId")); } }
對於Inject攔截器,是在Enhancer的enhance方法,提供了直接傳入Interceptor.class的方法,因此能夠提供Inject攔截器的功能 jFinal的攔截器用法很是簡單,可是有一點,未提供動態代理的支持。可是jFinal提供的擴展方式如此簡單,只須要在此基礎上本身稍加改造便可實現。
jFinal對事務的支持採用聲明式事務,須要使用到事務的類,配置攔截器**@Before(Tx.class)**便可。
com.jfinal.plugin.activerecord.tx.Tx核心代碼以下:
public void intercept(Invocation inv) { ... Boolean autoCommit = null; try { conn = config.getConnection(); autoCommit = conn.getAutoCommit(); config.setThreadLocalConnection(conn); conn.setTransactionIsolation(getTransactionLevel(config)); // conn.setTransactionIsolation(transactionLevel); conn.setAutoCommit(false); inv.invoke(); conn.commit(); } ...
}
實現代碼很是簡單易懂。
jFinal使用ehcache做爲默認的緩存系統,提供了對sql的緩存和對業務層的緩存,兩種緩存應該都屬於二級緩存,範圍都是在項目級別的,未提供session級別的緩存。
jFinal的validate做爲攔截器存在,創建在攔截器基礎上,提供了string、double、date、等基本類型的校驗,並提供了正則校驗方式,能夠動態配置,可是這裏的問題是,若是一個請求有100個參數,那就必須寫100行校驗的代碼,若是使用配置文件的方式,那可能會便於管理,固然,jFinal的高擴展性,也能讓你本身輕易實現這個功能。
後記:jFinal是不可多得的好框架,源碼簡潔,沒有過多的爲了使用設計模式而使用設計模式,擴展性強。可是缺點也在於它過小了,對於大型項目的支持可能就有點吃不消;而這也算是語言的通病吧,功能強大,就會累贅臃腫,易上手,就不免扛不起大項目。祝jFinal愈來愈好。