博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
jFinal研究与见解
阅读量:5879 次
发布时间:2019-06-19

本文共 8061 字,大约阅读时间需要 26 分钟。

hot3.png

###jFinal研究与见解

最幸福的事,莫过于看别人写的好框架,代码简单,功能齐全,设计思路清晰,可读性强。。。最痛苦的事,莫过于看别人写的烂代码,功能简单,代码复杂,可读性差。。。jFinal是很少见的国内写地很好的框架,该用别人造好轮子的地方,就不重复造轮子、别人写的不好的地方,就自己写个更好的。 *

网上有很多学习jFinal的资料,当然,任何资料都比不上源码和官方文档,查阅官方文档,请移步,我读的是jfinal-2.0-manual.pdf

jFinal的运行思路非常清晰。官方介绍的启动jFinal有两种方式,第一种,使用jFinal集成的jetty启动;第二种,使用tomcat启动,jetty相对tomcat比较小巧,jetty使用nio监听端口,而tomcat采用bio监听端口。下面,结合代码简单介绍下jFinal的运行。

  1. 容器启动时,读取配置文件web.xml,加载核心过滤器

    jfinal
    com.jfinal.core.JFinalFilter
    configClass
    com.demo.common.DemoConfig
    jfinal
    /*
  2. 过滤器初始化配置类,配置类中配置好路由插件拦截器处理器com.demo.common.DemoConfig关键代码如下

    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关键代码如下:

    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**为框架所做的架构图,图如下:

输入图片说明

####以下是关于此项目的一些个人的见解

  • 关于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 enhance(Class
targetClass) { return (T)Enhancer.enhance(targetClass);}

调用了com.jfinal.aop.Enhancer.enhance(),再深入,看Enhancer..enhance()

public static 
T enhance(Class
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越来越好。

转载于:https://my.oschina.net/u/1164681/blog/504017

你可能感兴趣的文章
ionic 调用手机的打电话功能
查看>>
怎么使用阿里云直播服务应用到现在主流直播平台中
查看>>
Xcode全局替换内容,一键Replace
查看>>
1000 加密算法
查看>>
exif_imagetype() 函数在linux下的php中不存在
查看>>
Ruby的case语句
查看>>
Linux的链接文件-ln命令
查看>>
maven的tomcat插件如何进行debug调试
查看>>
table表头固定
查看>>
截取字符串中两个字符串中的字符串
查看>>
spring xml properties split with comma for list
查看>>
判断点是否在三角形内
查看>>
Android实战简易教程-第二十三枪(基于Baas的用户注冊验证username是否反复功能!)...
查看>>
在odl中怎样实现rpc
查看>>
leetcode 110 Balanced Binary Tree
查看>>
python活用isdigit方法显示系统进程
查看>>
项目开发总结
查看>>
知行合一
查看>>
jmeter插件之jsonpath提取响应结果和做断言
查看>>
发布支持多线程的PowerShell模块 —— MultiThreadTaskRunner
查看>>