jFinal研究與看法

###jFinal研究與看法

最幸福的事,莫過於看別人寫的好框架,代碼簡單,功能齊全,設計思路清晰,可讀性強。。。最痛苦的事,莫過於看別人寫的爛代碼,功能簡單,代碼複雜,可讀性差。。。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

  1. 容器啓動時,讀取配置文件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>
  2. 過濾器初始化配置類,配置類中配置好路由插件攔截器處理器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) {
     		// 配置處理器
     	}
     }
  3. 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都分別進行初始化緩存

  1. 請求訪問服務器,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);
     }
  2. Handler抽象類接口源碼以下服務器

    public abstract class Handler {
     	//此處即說明Handler是鏈式結構
     	protected Handler nextHandler;
    
     	public abstract void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled);
     }

讀過框架源代碼,便可很好的理解做者**@James Zhan**爲框架所作的架構圖,圖以下:session

輸入圖片說明

####如下是關於此項目的一些我的的看法

  • 關於JFinalConfig

此特性爲jFinal引覺得傲的特性之一,全部的ConstantPlugingInterceptorconfigHandler都在這一個類中配置,這樣能夠達到沒有xml配置文件,不須要像ssh那樣去維護繁瑣的配置文件,以前接觸phpnodejs這些語言的時候,也接觸過這種處理方式,無配置文件,在類中利用route的方式轉發請求。

可是我認爲,小項目如此處理能夠快速開發,可是項目若是大點,繁瑣點,配置類將會變得很大,而且不易讀懂。還有一種解決方案不將全部的配置都放在一個類中,而是根據不一樣的功能和業務,放在不一樣的包中,啓動時將這些類動態加載進來。這樣,開發看到包名便可知道mvc的構成,而不是須要一段一段讀配置的代碼,而且後期加入新的配置也不會對以前已存在配置形成影響。

jFinal配置的初始化也只支持一個類,若是想要使用多個,則不支持。同時routes的配置文檔中說明支持配置多個,便於不一樣開發人員版本維護,constantpluging等不使用這種方式,多是考慮到這些是全局的,個性化的比較少吧。

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());
}
  • 關於Interceptor

jFinal使用很是靈活的方式實現了aop,學習spring的同窗,都會被它的 Aspect、Advice、Joinpoint、Poincut...這些複雜的概念搞暈。 jFinal的攔截器根據級別能夠劃分爲全局攔截器Inject攔截器Class攔截器以及Method攔截器,jFinalaop是靠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.Controllerenhance方法源碼以下:

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.InterceptorBuilderbuild方法:

public static Interceptor[] build(Interceptor[] injectInters, Class<?> targetClass, Method method) {
	Interceptor[] methodInters = createInterceptors(method.getAnnotation(Before.class));
	。。。
}

此處獲取了方法的**@Beforeannotation**,而且根據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攔截器,是在Enhancerenhance方法,提供了直接傳入Interceptor.class的方法,因此能夠提供Inject攔截器的功能 jFinal的攔截器用法很是簡單,可是有一點,未提供動態代理的支持。可是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緩存支持,

jFinal使用ehcache做爲默認的緩存系統,提供了對sql的緩存對業務層的緩存,兩種緩存應該都屬於二級緩存,範圍都是在項目級別的,未提供session級別的緩存。

  • validator,

jFinal的validate做爲攔截器存在,創建在攔截器基礎上,提供了stringdoubledate、等基本類型的校驗,並提供了正則校驗方式,能夠動態配置,可是這裏的問題是,若是一個請求有100個參數,那就必須寫100行校驗的代碼,若是使用配置文件的方式,那可能會便於管理,固然,jFinal的高擴展性,也能讓你本身輕易實現這個功能。

後記:jFinal是不可多得的好框架,源碼簡潔,沒有過多的爲了使用設計模式而使用設計模式,擴展性強。可是缺點也在於它過小了,對於大型項目的支持可能就有點吃不消;而這也算是語言的通病吧,功能強大,就會累贅臃腫,易上手,就不免扛不起大項目。祝jFinal愈來愈好。

相關文章
相關標籤/搜索