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愈来愈好。

相关文章
相关标签/搜索