最幸福的事,莫过于看别人写的好框架,代码简单,功能齐全,设计思路清晰,可读性强。。。最痛苦的事,莫过于看别人写的烂代码,功能简单,代码复杂,可读性差。。。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愈来愈好。