歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

JFinal 的初始化

淺析初始化過程

首先要從 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;
        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

boolean 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 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 = 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 controllerKey
        Action 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.htm

JFinal開發8個常見問題 http://www.linuxidc.com/Linux/2015-02/113421.htm

JFinal的詳細介紹:請點這裡
JFinal的下載地址:請點這裡

Copyright © Linux教程網 All Rights Reserved