JFinal初始化过程浅析

<span style="font-family: Arial, Helvetica, sans-serif;  background-color: rgb(255, 255, 255);"><strong>废话</strong></span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">:</span>

前段时间粗略看了一下jfinal的源码,然后今天在群里突然有人问jfinal的IOC实现原理是什么,竟然没有一点印象,丢人的节奏啊。然后一查,原来jfinal支持IOC要集成ioc插件,自己并没有实现IOC,意识到自己应该总结一下了,不然脸都丢完了。哈哈

可能是废话

所有的容器都是从初始化开始的。所以看源码的初始化肯定也是从它的web.xml的那个filter钻进去的。

<filter>
		<filter-name>jfinal</filter-name>
		<filter-class>com.jfinal.core.JFinalFilter</filter-class>
		<init-param>
			<param-name>configClass</param-name>
			<param-value>demo.DemoConfig</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>jfinal</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
这是jfinal在web.xml中的配置。在servlet容器初始化的时候会加载JFinalFilter,当然JFinalFilter肯定是实现了Servlet的Filter接口的。我们ctrl+鼠标左键跟进去看一下JFinalFilter里边都干了什么。


上图是JFinalFilter的属性和方法列表。可以看到这几个属性Handler,encoding,JFinalConfig,Constants,JFinal。为什么在初始化的Filter里定义这几个属性呢,等一会儿我们自然会分析。我们先看JFinalFilter的init方法。

<span style="white-space: pre;">第一句</span>createJFinalConfig(filterConfig.getInitParameter("configClass"));
filterConfig.getInitParameter("configClass")
这个configClass是什么呢?相信大家都看开发 文档的。这个就是我们在web.xml中配置的初始化参数,value是我们写的继承自JFinalConfig的类。

继承JFinalConfig要实现以下方法,这些方法的作用我写了注释。

/**
	 * 此方法用来配置常量值,如devMode(开发模式),viewType(视图类型)等
	 */
	@Override
	public void configConstant(Constants me) {
		me.setDevMode(true);
	}

	/**
	 * 此方法用来配置访问路由
	 */
	@Override
	public void configRoute(Routes me) {
		me.add("/hello", HelloController.class);
		me.add(new FrontRoute());
	}

	/**
	 * 此方法用来配置plugin
	 */
	@Override
	public void configPlugin(Plugins me) {
		loadPropertyFile("jdbc.txt");
		C3p0Plugin c3p0Plugin = new C3p0Plugin(getProperty("url"),
				getProperty("username"),getProperty("password"));
		me.add(c3p0Plugin);
		ActiveRecordPlugin active = new ActiveRecordPlugin(c3p0Plugin);
		me.add(active);
		active.addMapping("user", User.class);
	}

	/**
	 * 此方法用来配置intercept,在此处配置的拦截器将会对所有的请求进行拦截,除非使用@ClearInterceptor在Controller里清楚
	 */
	@Override
	public void configInterceptor(Interceptors me) {

	}

	/**
	 * 此方法用来配置Handle,handle可以接管所有web请求,并对应用拥有完全的控制权,可以很方便的实现更高层的功能性扩展
	 */
	@Override
	public void configHandler(Handlers me) {

	}

可以看到,这5个方法都是用来配置一些东西。很明显,JFinal作为精小的框架,在初始化的时候配置的东西肯定是整个环境需要(如constants)的或者是处理一个请求所必须的(如路由,插件,自定义全局拦截器,自定义处理操作)

我们继续回到createJFinalConfig(filterConfig.getInitParameter("configClass"));跟进去看它都做了什么

private void createJFinalConfig(String configClass) {
		if (configClass == null)
			throw new RuntimeException("Please set configClass parameter of JFinalFilter in web.xml");
		
		try {
			Object temp = Class.forName(configClass).newInstance();
			if (temp instanceof JFinalConfig)
				jfinalConfig = (JFinalConfig)temp;
			else
				throw new RuntimeException("Can not create instance of class: " + configClass + ". Please check the config in web.xml");
		} catch (InstantiationException e) {
			throw new RuntimeException("Can not create instance of class: " + configClass, e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException("Can not create instance of class: " + configClass, e);
		} catch (ClassNotFoundException e) {
			throw new RuntimeException("Class not found: " + configClass + ". Please config it in web.xml", e);
		}
	}
通过反射初始化一个JFinalConfig对象。

然后init方法中的第二句jfinal.init(jfinalConfig, filterConfig.getServletContext())

看一下JFinal对象的初始化都做了什么

boolean init(JFinalConfig jfinalConfig, ServletContext servletContext) {
		this.servletContext = servletContext;
		this.contextPath = servletContext.getContextPath();
		
		initPathUtil();
		
		Config.configJFinal(jfinalConfig);	// start plugin and init logger factory in this method
		constants = Config.getConstants();
		
		initActionMapping();
		initHandler();
		initRender();
		initOreillyCos();
		initI18n();
		initTokenManager();
		
		return true;
	}

第一、初始化 path,既初始化path工具类中的webRootPath(项目根路径)

private void initPathUtil() {
		String path = servletContext.getRealPath("/");
		PathKit.setWebRootPath(path);
	}

 第二、一些基本配置

/*
	 * Config order: constant, route, plugin, interceptor, handler
	 */
	static void configJFinal(JFinalConfig jfinalConfig) {
		jfinalConfig.configConstant(constants);				initLoggerFactory();
		jfinalConfig.configRoute(routes);
		jfinalConfig.configPlugin(plugins);					startPlugins();	// very important!!!
		jfinalConfig.configInterceptor(interceptors);
		jfinalConfig.configHandler(handlers);
	}


这里就是调用我们我们继承自JfinalConfig的那个类的方法了,configConstant就没有什么说了,就是加载配置文件,然后读取里边的值设置给Constant相应的属性。

initLoggerFactory就是初始化日志工厂,这里可以自定义使用喜欢的日志工具,如果没有自定义的话则会默认使用Log4j。自定义我也不知道在哪儿自定义,不过在configConstant中加上这句代码Logger.setLoggerFactory((ILogerFactory) 初始化)应该就ok了吧。如果不多希望您指正一下

configRoute配置路由

配置路由主要是干什么用的呢?就是映射URL地址和controller中的具体的方法。看我们自己定义的DemoConfig中的me.add("/hello", HelloController.class);。跟进去往下看

public Routes add(String controllerkey, Class<? extends Controller> controllerClass) {
		return add(controllerkey, controllerClass, controllerkey);
	}
继续往里边跟,代码太长就不贴了,主要就是判断参数的合法性维护参数的正确性,然后将controllerKey作为键和controllerClass值放进一个map里,将controllerKey作为键和处理后的controllerKey作为值放进一个map里。有什么用?我现在还不知道,不过肯定是后边要用的。

configPlugin配置插件,没有什么好说的将你需要的插件统一放进一个list里,然后就startPlugins()了。

configInterceptor和configHandle也没有什么,和plugin一样将你自定义的interceptor和handle放进各自的list以备后边使用。

接下来几个动作才是重头戏,代码如下

initActionMapping();
		initHandler();
		initRender();
		initActiveRecord();
		initOreillyCos();
		initI18n();
		initTokenManager();

我们一个一个看,

一、先是actionMapping

private void initActionMapping() {
		actionMapping = new ActionMapping(Config.getRoutes(), Config.getInterceptors());
		actionMapping.buildActionMapping();
	}
Config.getRoutes(),Config.getInterceptors()就是拿到我们刚刚在上边说的那个configJfinal里初始化的routes和interceptors。然后buildActionMapping()

跟进去老长一个方法了。哈哈不过做的事情还是很清晰的,我们看看它做了什么吧
这个方法还真是蛋疼,看了半天才看懂。收回上边说的那句话!分析代码吧!

mapping.clear();准备工作要做好,把箱子给我腾空了,我要装东西了。buildExcludedMethodName();将不需要处理的方法给列出来。这里要讲一下,因为我们自己的controller是继承自Controller类的,但是我们自己定义的interceptor只需要拦截自己的方法(url可能请求的资源)就ok了。但是通过反射怎么过滤掉父类的方法呢?这里好像有点问题哎,我再看看。。。



看上边两张图片,第一个图片中红框内,拿到“不包括的方法名”。这个方法内容就是第二张图片里的,将所有参数长度为0的方法返回。再看第二个红框:不包含在excludedMethodName中并且参数长度为0的进入if语句块。这个判断的最终目的也就是因为DemoController是继承自Controller,而自定义Interceptor只需要加在DemoController的方法上,所以这里过滤掉它继承自Controller的方法。但是为什么不直接拿到Controller的所有方法,if判断里做过滤呢?还要这么绕。不知道是不是自己的理解不到位,大家给指正一下。

扯了这么多继续说代码吧 。这两句是重要的

Interceptor[] methodInters = interceptorBuilder.buildMethodInterceptors(method);
					Interceptor[] actionInters = interceptorBuilder.buildActionInterceptors(defaultInters, controllerInters, controllerClass, methodInters, method);
					

拿到为该方法定义的interceptor,将系统默认的、全局的(action)、为该方法定义的interceptor合在一起。接下来就是判断路由,实例化action,放进路由和action映射的map里。

二、initHandler

private void initHandler() {
		Handler actionHandler = new ActionHandler(actionMapping, constants);
		handler = HandlerFactory.getHandler(Config.getHandlers().getHandlerList(), actionHandler);
	}

将actionMapping放进Handler中,然后好处理它,哈哈。看看HandlerFactory的getHandler方法中都做了什么

public static Handler getHandler(List<Handler> handlerList, Handler actionHandler) {
		Handler result = actionHandler;
		
		for (int i=handlerList.size()-1; i>=0; i--) {
			Handler temp = handlerList.get(i);
			temp.nextHandler = result;
			result = temp;
		}
		
		return result;
	}
将你自己定义的handler一个一个的加到系统定义的Handler后边,对,就是这么简单。

下边的步骤先大致说一下是干什么的吧,写博客还是有点累。下次再把下面几个步骤分析一下,然后再分析一下一次请求所做的操作。

三、initRender

这里初始化视图类型

四、初始化上传组件

五、国际化

六、初始化令牌管理




你可能感兴趣的:(源码,jFinal)