浅析初始化过程
首先要从 web 容器进行初始化
jfinal
com.jfinal.core.JFinalFilter
configClass
com.fw.config.MppConfig
jfinal
/*
从 web.xml 可以看出,容器初始化的时候会加载 JFinalFilter 这个类并且调用其 init 方法。
public final class JFinalFilter implementsFilter {privateHandler handler;privateString encoding;privateJFinalConfig jfinalConfig;privateConstants constants;private static final JFinal jfinal =JFinal.me();private staticLog log;private intcontextPathLength;public void init(FilterConfig filterConfig) throwsServletException {
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());
}private voidcreateJFinalConfig(String configClass) {if (configClass == null)throw new RuntimeException("Please set configClass parameter of JFinalFilter in web.xml");
Object temp= null;try{
temp=Class.forName(configClass).newInstance();
}catch(Exception e) {throw new RuntimeException("Can not create instance of class: " +configClass, e);
}if (temp instanceofJFinalConfig)
jfinalConfig=(JFinalConfig)temp;else
throw new RuntimeException("Can not create instance of class: " + configClass + ". Please check the config in web.xml");
}//...
}
init 方法中的参数正是 web.xml 中的 JFinalFilter 的初始化参数,即 com.fw.config.MppConfig,它继承于 JFinalConfig,在 createJFinalConfig 中利用该参数使用反射机制得到 JFinalConfig 的实例。
jfinal.init
booleaninit(JFinalConfig jfinalConfig, ServletContext servletContext) {this.servletContext =servletContext;this.contextPath =servletContext.getContextPath();
initPathUtil();
Config.configJFinal(jfinalConfig);
constants=Config.getConstants();
initActionMapping();
initHandler();
initRender();
initOreillyCos();
initTokenManager();return true;
}
第一、initPathUtil,初始化 Path工具类的 webRootPath(即项目根路径)。
private voidinitPathUtil() {
String path= servletContext.getRealPath("/");
PathKit.setWebRootPath(path);
}
第二、Config.configJFinal 加载 JFinalConfig 实例,进行一些配置。
static voidconfigJFinal(JFinalConfig jfinalConfig) {
jfinalConfig.configConstant(constants); initLogFactory();
jfinalConfig.configRoute(routes);
jfinalConfig.configPlugin(plugins); startPlugins();//very important!!!
jfinalConfig.configInterceptor(interceptors);
jfinalConfig.configHandler(handlers);
}
配置常量,初始化 Log 工具类,配置路由,配置插件,开启插件,配置拦截器,配置 handler。
下面看看我项目中的 JFinalConfig 实例。
public class MppConfig extendsJFinalConfig {//配置常量
public voidconfigConstant(Constants me) {
PropKit.use("jdbc.properties");
me.setDevMode(PropKit.getBoolean("devMode", false));
}//配置路由
public voidconfigRoute(Routes me) {
me.add(newRoutesMapping());
}public staticC3p0Plugin createC3p0Plugin() {return new C3p0Plugin(PropKit.get("jdbcUrl"), PropKit.get("user"), PropKit.get("password").trim());
}//配置插件
public voidconfigPlugin(Plugins me) {//配置C3p0数据库连接池插件
C3p0Plugin C3p0Plugin =createC3p0Plugin();
me.add(C3p0Plugin);//配置ActiveRecord插件
ActiveRecordPlugin arp = newActiveRecordPlugin(C3p0Plugin);
me.add(arp);//配置属性名(字段名)大小写不敏感容器工厂 Oracle
arp.setContainerFactory(newCaseInsensitiveContainerFactory());//缓存插件
me.add(newEhCachePlugin());//所有配置在 MappingKit 中搞定
ModelMapping.mapping(arp);
}//配置全局拦截器
public voidconfigInterceptor(Interceptors me) {
me.add(new SessionInViewInterceptor());//session拦截器,用于在View模板中取出session值
}//配置处理器,接管所有 web 请求
public voidconfigHandler(Handlers me) {
me.add(new ContextPathHandler("contextPath"));//设置上下文路径
}//系统启动完成后回调,如创建调度线程
public voidafterJFinalStart(){}//系统关闭前回调,如写回缓存
public voidbeforeJFinalStop(){}
}View Code
第三、初始化 ActionMapping、Handler、Render等。
initActionMapping();
initHandler();
initRender();
initOreillyCos();
initTokenManager();
Init ActionMapping
private voidinitActionMapping() {
actionMapping= newActionMapping(Config.getRoutes(), Config.getInterceptors());
actionMapping.buildActionMapping();
Config.getRoutes().clear();
}
第一、创建 ActionMapping,映射所有访问路径和其对应的Action,填充得到一个 HashMap。下面是创建 ActionMapping 代码和注释,涉及到其他类的源码请自行查看。
final classActionMapping {private static final String SLASH = "/";privateRoutes routes;//ActionMapping 映射
private final Map mapping = new HashMap();//构造方法,routes 参数传进来
ActionMapping(Routes routes, Interceptors interceptors) {this.routes =routes;
}private SetbuildExcludedMethodName() {
Set excludedMethodName = new HashSet();
Method[] methods= Controller.class.getMethods();for(Method m : methods) {if (m.getParameterTypes().length == 0)
excludedMethodName.add(m.getName());
}returnexcludedMethodName;
}voidbuildActionMapping() {//初始化,我将要向里面塞东西了,要清空一下
mapping.clear();//这个方法返回的是 Controller接口的所有方法集合。
Set excludedMethodName =buildExcludedMethodName();//得到 InterceptorManager 的实例
InterceptorManager interMan =InterceptorManager.me();//遍历一个 Entry 集合。Entry 的 key 为访问路径,value 为其对应的 Controller。
for (Entry>entry : routes.getEntrySet()) {//得到访问路径对应的 Controller class
Class extends Controller> controllerClass =entry.getValue();//如果 Controller class没有拦截器注解,则返回一个空数组。反之返回这个类所有拦截器组成的数组
Interceptor[] controllerInters =interMan.createControllerInterceptor(controllerClass);boolean sonOfController = (controllerClass.getSuperclass() == Controller.class); //这里必定为 true//getDeclaredMethods 得到这个类的所有方法以及其接口的所有方法,不包括继承的方法
Method[] methods = (sonOfController ?controllerClass.getDeclaredMethods() : controllerClass.getMethods());for(Method method : methods) {
String methodName=method.getName();//若这个方法是继承自 Controller的方法 或 该方法有参数,过滤掉
if (excludedMethodName.contains(methodName) || method.getParameterTypes().length != 0)continue;//若这个方法不是 public 方法,过滤掉
if (sonOfController && !Modifier.isPublic(method.getModifiers()))continue;//想进行到这里,这个方法必须满足:不是继承自 Controller、不能有参数、必须是 public 方法//得到包含所有拦截器的数组(包括全局的拦截器,类级别的拦截器、方法级别的拦截器)
Interceptor[] actionInters =interMan.buildControllerActionInterceptor(controllerInters, controllerClass, method);
String controllerKey=entry.getKey();
ActionKey ak= method.getAnnotation(ActionKey.class);
String actionKey;//ActionKey 不为空的话为设置自定义的访问路径(说明有方法被注有 @ActionKey 注解)
if (ak != null) {
actionKey=ak.value().trim();if ("".equals(actionKey))throw new IllegalArgumentException(controllerClass.getName() + "." + methodName + "(): The argument of ActionKey can not be blank.");if (!actionKey.startsWith(SLASH))
actionKey= SLASH +actionKey;
}//ActionKey为空,methodName 为 index的情况下:actionKey = controllerKey
else if (methodName.equals("index")) {
actionKey=controllerKey;
}//ActionKey为空,methodName 不为 index的情况下:actionKey = controllerKey +"/" + methodName
else{
actionKey= controllerKey.equals(SLASH) ? SLASH + methodName : controllerKey + SLASH +methodName;
}//组合装配成一个 Action
Action action = newAction(controllerKey, actionKey, controllerClass, method, methodName, actionInters, routes.getViewPath(controllerKey));//填充 HashMap(访问路径为 key,Action 为 value)
if (mapping.put(actionKey, action) != null)throw newRuntimeException(buildMsg(actionKey, controllerClass, method));
}
}//support url = controllerKey + urlParas with "/" of controllerKey
Action action = mapping.get("/");if (action != null)
mapping.put("", action);
}//...
}
View Code
第二、Config.routes 已经用过了,以后也不会再用上了,清空路由的所有信息。
Init Handler
private voidinitHandler() {
Handler actionHandler= newActionHandler(actionMapping, constants);
handler=HandlerFactory.getHandler(Config.getHandlers().getHandlerList(), actionHandler);
}
第一、创建一个 ActionHandler 实例 actionHandler。
第二、HandlerFactory.getHandler 创建一个 Handler 链,链尾是 actionHandler,并且返回链首。
public classHandlerFactory {privateHandlerFactory() {}/*** 创建一个 handler 链条,最后返回的 result 是 handler 链的头部,链尾是 ActionHandler*/@SuppressWarnings("deprecation")public static Handler getHandler(ListhandlerList, Handler actionHandler) {
Handler result=actionHandler;for (int i=handlerList.size()-1; i>=0; i--) {
Handler temp=handlerList.get(i);
temp.next=result;
temp.nextHandler=result;
result=temp;
}returnresult;
}
}
Init Render
private voidinitRender() {
RenderFactory.me().init(constants, servletContext);
}
继续跟~
public classRenderFactory {public voidinit(Constants constants, ServletContext servletContext) {this.constants =constants;this.servletContext =servletContext;//初始化 Render
Render.init(constants.getEncoding(), constants.getDevMode()); //初始化编码和开发模式
initFreeMarkerRender(servletContext); //初始化 FreeMarkerRender
initVelocityRender(servletContext);
initJspRender(servletContext);
initFileRender(servletContext);//创建 mainRenderFactory
if (mainRenderFactory == null) {
ViewType defaultViewType=constants.getViewType();if (defaultViewType ==ViewType.FREE_MARKER) {
mainRenderFactory= newFreeMarkerRenderFactory();
}else if (defaultViewType ==ViewType.JSP) {
mainRenderFactory= newJspRenderFactory();
}else if (defaultViewType ==ViewType.VELOCITY) {
mainRenderFactory= newVelocityRenderFactory();
}else{throw new RuntimeException("View Type can not be null.");
}
}//创建 errorRenderFactory
if (errorRenderFactory == null) {
errorRenderFactory= newErrorRenderFactory();
}if (xmlRenderFactory == null) {
xmlRenderFactory= newXmlRenderFactory();
}
}private voidinitFreeMarkerRender(ServletContext servletContext) {try{
Class.forName("freemarker.template.Template"); //加载 freemarker 模板
FreeMarkerRender.init(servletContext, Locale.getDefault(), constants.getFreeMarkerTemplateUpdateDelay());
}catch(ClassNotFoundException e) {//若加载模板失败,比如没有引入 jar 包
LogKit.logNothing(e);
}
}//...
}public class FreeMarkerRender extendsRender {private static final String contentType = "text/html; charset=" +getEncoding();private static final Configuration config = newConfiguration();static void init(ServletContext servletContext, Locale locale, inttemplate_update_delay) {//初始化 freemarker 配置:config = new Configuration();//设置 freemarker 页面的存放位置为项目根路径
config.setServletContextForTemplateLoading(servletContext, "/");//设置开发模式下更新延迟时间
if(getDevMode()) {
config.setTemplateUpdateDelay(0);
}else{
config.setTemplateUpdateDelay(template_update_delay);
}//- Set an error handler that prints errors so they are readable with//a HTML browser.//config.setTemplateExceptionHandler(TemplateExceptionHandler.HTML_DEBUG_HANDLER);
config.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);//- Use beans wrapper (recommmended for most applications)
config.setObjectWrapper(ObjectWrapper.BEANS_WRAPPER);
config.setDefaultEncoding(getEncoding());//设置默认编码
config.setOutputEncoding(getEncoding()); //设置输出编码//- Set the default locale
config.setLocale(locale /*Locale.CHINA*/ ); //设置时区
config.setLocalizedLookup(false);//去掉int型输出时的逗号, 例如: 123,456//config.setNumberFormat("#");//config.setNumberFormat("0"); 也可以
config.setNumberFormat("#0.#####");
config.setDateFormat("yyyy-MM-dd");
config.setTimeFormat("HH:mm:ss");
config.setDateTimeFormat("yyyy-MM-dd HH:mm:ss");
}//...
}
Init 上传组件
private voidinitOreillyCos() {
OreillyCos.init(constants.getBaseUploadPath(), constants.getMaxPostSize(), constants.getEncoding());
}
很简单,附上相关代码:
public classOreillyCos {//先加载上传组件
public static void init(String uploadPath, intmaxPostSize, String encoding) {if(StrKit.isBlank(uploadPath)) {throw new IllegalArgumentException("uploadPath can not be blank.");
}try{
Class.forName("com.oreilly.servlet.MultipartRequest");
doInit(uploadPath, maxPostSize, encoding);
}catch(ClassNotFoundException e) {
LogKit.logNothing(e);
}
}private static void doInit(String uploadPath, intmaxPostSize, String encoding) {
uploadPath=uploadPath.trim();
uploadPath= uploadPath.replaceAll("\\\\", "/");
String baseUploadPath;//判断上传路径是否是绝对路径,如果不是把它加工成绝对路径
if(PathKit.isAbsolutelyPath(uploadPath)) {
baseUploadPath=uploadPath;
}else{
baseUploadPath= PathKit.getWebRootPath() + File.separator +uploadPath;
}//remove "/" postfix
if (baseUploadPath.equals("/") == false) {if (baseUploadPath.endsWith("/")) {
baseUploadPath= baseUploadPath.substring(0, baseUploadPath.length() - 1);
}
}
MultipartRequest.init(baseUploadPath, maxPostSize, encoding);
}//...
}public class MultipartRequest extendsHttpServletRequestWrapper {private staticString baseUploadPath;private static intmaxPostSize;private staticString encoding;static void init(String baseUploadPath, intmaxPostSize, String encoding) {
MultipartRequest.baseUploadPath=baseUploadPath;
MultipartRequest.maxPostSize=maxPostSize;
MultipartRequest.encoding=encoding;
}//...
}
View Code
Init Token令牌
private voidinitTokenManager() {
ITokenCache tokenCache=constants.getTokenCache();if (tokenCache != null)
TokenManager.init(tokenCache);
}
先给个代码吧:
public classTokenManager {private staticITokenCache tokenCache;private static Random random = newRandom();privateTokenManager() {
}public static voidinit(ITokenCache tokenCache) {if (tokenCache == null)return;
TokenManager.tokenCache=tokenCache;long halfTimeOut = Const.MIN_SECONDS_OF_TOKEN_TIME_OUT * 1000 / 2; //Token最小过期时间的一半时间作为任务运行的间隔时间
new Timer().schedule(new TimerTask() {public voidrun() {removeTimeOutToken();}},
halfTimeOut,
halfTimeOut);
}//...
}
----- End -----
JFinal的详细介绍:请点这里
JFinal的下载地址:请点这里