浅析初始化过程 首先要从 web 容器进行初始化<?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"><filter><filter-name>jfinal</filter-name><filter-class>com.jfinal.core.JFinalFilter</filter-class><init-param><param-name>configClass</param-name><param-value>com.fw.config.MppConfig</param-value></init-param></filter><filter-mapping><filter-name>jfinal</filter-name><url-pattern>/*</url-pattern></filter-mapping></web-app> 从 web.xml 可以看出,容器初始化的时候会加载 JFinalFilter 这个类并且调用其 init 方法。public final class JFinalFilter implements Filter {private Handler handler;private String encoding;private JFinalConfig jfinalConfig;private Constants constants;private static final JFinal jfinal = JFinal.me();private static Log log;private int contextPathLength;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());}private void createJFinalConfig(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 instanceof JFinalConfig)jfinalConfig = (JFinalConfig)temp;elsethrow 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.initboolean init(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 void initPathUtil() {String path = servletContext.getRealPath("/");PathKit.setWebRootPath(path);}第二、Config.configJFinal 加载 JFinalConfig 实例,进行一些配置。 static void configJFinal(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 extends JFinalConfig { //配置常量 public void configConstant(Constants me) { PropKit.use("jdbc.properties"); me.setDevMode(PropKit.getBoolean("devMode", false)); } //配置路由 public void configRoute(Routes me) { me.add(new RoutesMapping()); } public static C3p0Plugin createC3p0Plugin() { return new C3p0Plugin(PropKit.get("jdbcUrl"), PropKit.get("user"), PropKit.get("password").trim()); } //配置插件 public void configPlugin(Plugins me) { // 配置C3p0数据库连接池插件 C3p0Plugin C3p0Plugin = createC3p0Plugin(); me.add(C3p0Plugin); // 配置ActiveRecord插件 ActiveRecordPlugin arp = new ActiveRecordPlugin(C3p0Plugin); me.add(arp); // 配置属性名(字段名)大小写不敏感容器工厂 Oracle arp.setContainerFactory(new CaseInsensitiveContainerFactory()); //缓存插件 me.add(new EhCachePlugin()); // 所有配置在 MappingKit 中搞定 ModelMapping.mapping(arp); } //配置全局拦截器 public void configInterceptor(Interceptors me) { me.add(new SessionInViewInterceptor());//session拦截器,用于在View模板中取出session值 } //配置处理器,接管所有 web 请求 public void configHandler(Handlers me) { me.add(new ContextPathHandler("contextPath"));//设置上下文路径 } //系统启动完成后回调,如创建调度线程 public void afterJFinalStart(){} //系统关闭前回调,如写回缓存 public void beforeJFinalStop(){} }View Code第三、初始化 ActionMapping、Handler、Render等。 initActionMapping();initHandler();initRender();initOreillyCos();initTokenManager();Init ActionMapping private void initActionMapping() {actionMapping = new ActionMapping(Config.getRoutes(), Config.getInterceptors());actionMapping.buildActionMapping();Config.getRoutes().clear();}第一、创建 ActionMapping,映射所有访问路径和其对应的Action,填充得到一个 HashMap。下面是创建 ActionMapping 代码和注释,涉及到其他类的源码请自行查看。final class ActionMapping {private static final String SLASH = "/";private Routes routes;// ActionMapping 映射private final Map<String, Action> mapping = new HashMap<String, Action>();// 构造方法,routes 参数传进来ActionMapping(Routes routes, Interceptors interceptors) {this.routes = routes;}private Set<String> buildExcludedMethodName() {Set<String> excludedMethodName = new HashSet<String>();Method[] methods = Controller.class.getMethods();for (Method m : methods) {if (m.getParameterTypes().length == 0)excludedMethodName.add(m.getName());}return excludedMethodName;}void buildActionMapping() {// 初始化,我将要向里面塞东西了,要清空一下mapping.clear();// 这个方法返回的是 Controller接口的所有方法集合。Set<String> excludedMethodName = buildExcludedMethodName();// 得到 InterceptorManager 的实例InterceptorManager interMan = InterceptorManager.me();// 遍历一个 Entry 集合。Entry 的 key 为访问路径,value 为其对应的 Controller。for (Entry<String, Class<? extends Controller>> entry : routes.getEntrySet()) {// 得到访问路径对应的 Controller classClass<? 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 = controllerKeyelse if (methodName.equals("index")) {actionKey = controllerKey;}// ActionKey为空,methodName 不为 index的情况下:actionKey = controllerKey +"/" + methodNameelse {actionKey = controllerKey.equals(SLASH) ? SLASH + methodName : controllerKey + SLASH + methodName;}// 组合装配成一个 ActionAction action = new Action(controllerKey, actionKey, controllerClass, method, methodName, actionInters, routes.getViewPath(controllerKey));// 填充 HashMap(访问路径为 key,Action 为 value)if (mapping.put(actionKey, action) != null)throw new RuntimeException(buildMsg(actionKey, controllerClass, method));}}// support url = controllerKey + urlParas with "/" of controllerKeyAction action = mapping.get("/");if (action != null)mapping.put("", action);}// ...}View Code第二、Config.routes 已经用过了,以后也不会再用上了,清空路由的所有信息。Init Handler private void initHandler() {Handler actionHandler = new ActionHandler(actionMapping, constants);handler = HandlerFactory.getHandler(Config.getHandlers().getHandlerList(), actionHandler);}第一、创建一个 ActionHandler 实例 actionHandler。第二、HandlerFactory.getHandler 创建一个 Handler 链,链尾是 actionHandler,并且返回链首。public class HandlerFactory {private HandlerFactory() {}/** * 创建一个 handler 链条,最后返回的 result 是 handler 链的头部,链尾是 ActionHandler */@SuppressWarnings("deprecation")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.next = result;temp.nextHandler = result;result = temp;}return result;}} Init Render private void initRender() {RenderFactory.me().init(constants, servletContext);}继续跟~ public class RenderFactory { public void init(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 = new FreeMarkerRenderFactory(); } else if (defaultViewType == ViewType.JSP) { mainRenderFactory = new JspRenderFactory(); } else if (defaultViewType == ViewType.VELOCITY) { mainRenderFactory = new VelocityRenderFactory(); } else { throw new RuntimeException("View Type can not be null."); } } // 创建 errorRenderFactory if (errorRenderFactory == null) { errorRenderFactory = new ErrorRenderFactory(); } if (xmlRenderFactory == null) { xmlRenderFactory = new XmlRenderFactory(); } } private void initFreeMarkerRender(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 extends Render { private static final String contentType = "text/html; charset=" + getEncoding(); private static final Configuration config = new Configuration(); static void init(ServletContext servletContext, Locale locale, int template_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 void initOreillyCos() {OreillyCos.init(constants.getBaseUploadPath(), constants.getMaxPostSize(), constants.getEncoding());}很简单,附上相关代码:public class OreillyCos { // 先加载上传组件 public static void init(String uploadPath, int maxPostSize, 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, int maxPostSize, 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 extends HttpServletRequestWrapper { private static String baseUploadPath; private static int maxPostSize; private static String encoding; static void init(String baseUploadPath, int maxPostSize, String encoding) { MultipartRequest.baseUploadPath= baseUploadPath; MultipartRequest.maxPostSize= maxPostSize; MultipartRequest.encoding= encoding; } // ... }View Code Init Token令牌 private void initTokenManager() {ITokenCache tokenCache = constants.getTokenCache();if (tokenCache != null)TokenManager.init(tokenCache);}先给个代码吧:public class TokenManager {private static ITokenCache tokenCache;private static Random random = new Random();private TokenManager() {}public static void init(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 void run() {removeTimeOutToken();}}, halfTimeOut, halfTimeOut);}// ...} ----- End ----- Jfinal学习之路---Controller使用 http://www.linuxidc.com/Linux/2014-07/104323.htmJFinal开发8个常见问题 http://www.linuxidc.com/Linux/2015-02/113421.htmJFinal的详细介绍 :请点这里JFinal的下载地址 :请点这里本文永久更新链接地址 :http://www.linuxidc.com/Linux/2016-09/135258.htm
收藏该网址