美文网首页
JFinal实现原理

JFinal实现原理

作者: kakukeme | 来源:发表于2016-11-19 11:22 被阅读1220次

    刚接触Jfinal,后面继续整理;

    Jfinal源码解析
    http://blog.csdn.net/soul_code/article/category/6337939

    JFinal初始化过程浅析
    http://blog.csdn.net/huilangeliuxin/article/details/37961385

    JFinal实现原理
    http://blog.csdn.net/huilangeliuxin/article/details/37961385

    JFinal实现原理

    一切都从web.xml开始说起:

    当服务器初始化的时候会初始化web.xml里面的相关配置信息;

    下面我们来看一个重要的过滤器配置:JFinalFilter。下面是相关的配置信息。

    <filter>
        <filter-name>jfinal</filter-name>
        <filter-class>com.jfinal.core.JFinalFilter</filter-class>
        <async-supported>true</async-supported>
        <init-param>
            <param-name>configClass</param-name>
            <param-value>net.dreamlu.common.WebConfig</param-value>
        </init-param>
    </filter>
    

    这就意味着所有的请求都要经过JFinalFiter过滤了

    从中我们可以看到在web.xml之中配置了一个名为JFinalFilter的过滤器。下面我们来看看JFinalFiter的源码。

    /**
     * JFinal framework filter
     */
    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;
    
        // 系统在初始化Servlet的时候自动调用该方法;
        public void init(FilterConfig filterConfig) throws ServletException {
            // 创建JFianlConfig对象
            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();
    
            // afterJFinalStart 方法
            jfinalConfig.afterJFinalStart();
    
            // 得到项目根路径
            String contextPath = filterConfig.getServletContext().getContextPath();
            contextPathLength = (contextPath == null || "/".equals(contextPath) ? 0 : contextPath.length());
        }
    
        // request和response的作用不用过多介绍了。这个FilterChain的左右主要是用来连续调用下一个Filter时候使用的,
        // 下面给出了FilterChain的作用介绍.
        /**
    
       * A FilterChain is an object provided by the servlet container to the developer
    
       * giving a view into the invocation chain of a filtered request for a resource. Filters
    
       * use the FilterChain to invoke the next filter in the chain, or if the calling filter
    
       * is the last filter in the chain, to invoke the resource at the end of the chain.
    
       *
    
       * @see Filter
    
       * @since Servlet 2.3
    
       **/
        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)
    
                // 得到其ServletPath以及相关的参数
                target = target.substring(contextPathLength);
    
            boolean[] isHandled = {false};
            try {
                // isHandler用来判断该Target是否应该被相应的handler处理;
                // 这是整个Filter的最核心方法
                handler.handle(target, request, response, isHandled);
            }
            catch (Exception e) {
                if (log.isErrorEnabled()) {
                    String qs = request.getQueryString();
                    log.error(qs == null ? target : target + "?" + qs, e);
                }
            }
            // 该Target没被处理,进入下一个handler;
            if (isHandled[0] == false)
                chain.doFilter(request, response);
        }
    
        public void destroy() {
            // beforeJFinalStop方法
            jfinalConfig.beforeJFinalStop();
            // 停止插件
            jfinal.stopPlugins();
        }
    
        // 创建JFianlConfig对象, 读取app配置文件
        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");
        }
    
        static void initLog() {
            log = Log.getLog(JFinalFilter.class);
        }
    }
    

    让我们重点看看这个handler的由来。

    首先由handler = jfinal.getHandler();知道这个handler是由jfinal对象得来的,现在让我们看看jfinal的部分源码:

    /**
     * JFinal
     */
    public final class JFinal {
    
        private Constants constants;
        private ActionMapping actionMapping;
        private Handler handler;    // 定义了其他成员变量;
        private ServletContext servletContext;
        private String contextPath = "";
        private static IServer server;
    
        private static final JFinal me = new JFinal();
    
        private JFinal() {
        }
    
        public static JFinal me() {
            return me;
        }
    
        // 初始化JFinal时候调用的方法(在上面已经提到过这一点)
        boolean init(JFinalConfig jfinalConfig, ServletContext servletContext) {
            this.servletContext = servletContext;
            this.contextPath = servletContext.getContextPath();
    
            initPathUtil();
    
            // 启动插件,初始化日志工厂;
            Config.configJFinal(jfinalConfig);  // start plugin and init log factory in this method
            constants = Config.getConstants();
    
            initActionMapping();
            initHandler();
            initRender();
            initOreillyCos();
            initTokenManager();
    
            return true;
        }
    
        private void initTokenManager() {
            ITokenCache tokenCache = constants.getTokenCache();
            if (tokenCache != null)
                TokenManager.init(tokenCache);
        }
    
        private void initHandler() {
            Handler actionHandler = new ActionHandler(actionMapping, constants);
            handler = HandlerFactory.getHandler(Config.getHandlers().getHandlerList(), actionHandler);
        }
    
        private void initOreillyCos() {
            OreillyCos.init(constants.getBaseUploadPath(), constants.getMaxPostSize(), constants.getEncoding());
        }
    
        private void initPathUtil() {
            String path = servletContext.getRealPath("/");
            PathKit.setWebRootPath(path);
        }
    
        private void initRender() {
            RenderFactory.me().init(constants, servletContext);
        }
    
        private void initActionMapping() {
            actionMapping = new ActionMapping(Config.getRoutes(), Config.getInterceptors());
            actionMapping.buildActionMapping();
            Config.getRoutes().clear();
        }
    
        // 停止插件;从后往前遍历的
        void stopPlugins() {
            List<IPlugin> plugins = Config.getPlugins().getPluginList();
            if (plugins != null) {
                for (int i=plugins.size()-1; i >= 0; i--) {     // stop plugins
                    boolean success = false;
                    try {
                        success = plugins.get(i).stop();
                    }
                    catch (Exception e) {
                        success = false;
                        LogKit.error(e.getMessage(), e);
                    }
                    if (!success) {
                        System.err.println("Plugin stop error: " + plugins.get(i).getClass().getName());
                    }
                }
            }
        }
    
        Handler getHandler() {
            return handler;
        }
    
        public Constants getConstants() {
            return Config.getConstants();
        }
    
        public String getContextPath() {
            return contextPath;
        }
    
        public ServletContext getServletContext() {
            return this.servletContext;
        }
    
        public Action getAction(String url, String[] urlPara) {
            return actionMapping.getAction(url, urlPara);
        }
    
        public List<String> getAllActionKeys() {
            return actionMapping.getAllActionKeys();
        }
    
        // start方法,会在main方法调用;
        public static void start() {
            server = ServerFactory.getServer();
            server.start();
        }
    
        public static void start(String webAppDir, int port, String context, int scanIntervalSeconds) {
            server = ServerFactory.getServer(webAppDir, port, context, scanIntervalSeconds);
            server.start();
        }
    
        public static void stop() {
            server.stop();
        }
    
        /**
         * Run JFinal Server with Debug Configurations or Run Configurations in Eclipse JavaEE
         * args example: WebRoot 80 / 5
         */
        public static void main(String[] args) {
            if (args == null || args.length == 0) {
                server = ServerFactory.getServer();
                server.start();
            }
            else {
                String webAppDir = args[0];
                int port = Integer.parseInt(args[1]);
                String context = args[2];
                int scanIntervalSeconds = Integer.parseInt(args[3]);
                server = ServerFactory.getServer(webAppDir, port, context, scanIntervalSeconds);
                server.start();
            }
        }
    }
    

    由这里我们知道handler是由HandlerFactory的getHandler方法得来的。

    让我们再次看看HandlerFactory的部分源码以探个究竟:

    /**
     * HandlerFactory.
     */
    public class HandlerFactory {
    
        private HandlerFactory() {
    
        }
    
        /**
         * Build handler chain
         *
         * 显然这里返回的是一个actionHandler为首handler chain。
         */
        @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;
        }
    }
    

    显然这里返回的是一个actionHandler为首handler chain。

    让我们再来看看这个:

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

    此处传进去并不是简单的handler,而是他的子类ActionHandler,并且传进去了有两个参数,一个是ActionMapping类的变量,一个是constants。对于后者将就是一些常量的设置所以不进行过多介绍。让我们先看看ActionMapping之后再来看这个ActionHandler。

    /**
     * ActionMapping
     */
    final class ActionMapping {
    
        private static final String SLASH = "/";
        private Routes routes;
        // private Interceptors interceptors;
    
        private final Map<String, Action> mapping = new HashMap<String, Action>();
    
        ActionMapping(Routes routes, Interceptors interceptors) {
            this.routes = routes;
            // this.interceptors = interceptors;
        }
    
        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();
            Set<String> excludedMethodName = buildExcludedMethodName();
            InterceptorManager interMan = InterceptorManager.me();
    
            for (Entry<String, Class<? extends Controller>> entry : routes.getEntrySet()) {
                Class<? extends Controller> controllerClass = entry.getValue();
                Interceptor[] controllerInters = interMan.createControllerInterceptor(controllerClass);
    
                boolean sonOfController = (controllerClass.getSuperclass() == Controller.class);
                Method[] methods = (sonOfController ? controllerClass.getDeclaredMethods() : controllerClass.getMethods());
                for (Method method : methods) {
                    String methodName = method.getName();
                    if (excludedMethodName.contains(methodName) || method.getParameterTypes().length != 0)
                        continue ;
                    if (sonOfController && !Modifier.isPublic(method.getModifiers()))
                        continue ;
    
                    Interceptor[] actionInters = interMan.buildControllerActionInterceptor(controllerInters, controllerClass, method);
                    String controllerKey = entry.getKey();
    
                    ActionKey ak = method.getAnnotation(ActionKey.class);
                    String 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;
                    }
                    else if (methodName.equals("index")) {
                        actionKey = controllerKey;
                    }
                    else {
                        actionKey = controllerKey.equals(SLASH) ? SLASH + methodName : controllerKey + SLASH + methodName;
                    }
    
                    Action action = new Action(controllerKey, actionKey, controllerClass, method, methodName, actionInters, routes.getViewPath(controllerKey));
                    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);
        }
    
        private static final String buildMsg(String actionKey, Class<? extends Controller> controllerClass, Method method) {
            StringBuilder sb = new StringBuilder("The action \"")
                .append(controllerClass.getName()).append(".")
                .append(method.getName()).append("()\" can not be mapped, ")
                .append("actionKey \"").append(actionKey).append("\" is already in use.");
    
            String msg = sb.toString();
            System.err.println("\nException: " + msg);
            return msg;
        }
    
        /**
         * Support four types of url
         * 1: http://abc.com/controllerKey                 ---> 00
         * 2: http://abc.com/controllerKey/para            ---> 01
         * 3: http://abc.com/controllerKey/method          ---> 10
         * 4: http://abc.com/controllerKey/method/para     ---> 11
         * The controllerKey can also contains "/"
         * Example: http://abc.com/uvw/xyz/method/para
         */
        Action getAction(String url, String[] urlPara) {
            Action action = mapping.get(url);
            if (action != null) {
                return action;
            }
    
            // --------
            int i = url.lastIndexOf(SLASH);
            if (i != -1) {
                action = mapping.get(url.substring(0, i));
                urlPara[0] = url.substring(i + 1);
            }
    
            return action;
        }
    
        List<String> getAllActionKeys() {
            List<String> allActionKeys = new ArrayList<String>(mapping.keySet());
            Collections.sort(allActionKeys);
            return allActionKeys;
        }
    }
    

    在ActionMapping中定义了一个路由(routes)和一个Interceptors,这个routes类里面主要的核心是两个Map,内容如下(截取了部分源码过来):

    //每一个访问路径(controllerKey)都对应有一个相应的controller,并作为一对Entry放到map中
    private final Map<String, Class<? extends Controller>> map = new HashMap<String, Class<? extends Controller>>();
    
    //每一个访问路径(controllerKey)都对应一个在项目中的实际存放路径(WEB-INF/index.jsp等等),并作为一对Entry放到viewPathMap中
    
    private final Map<String, String> viewPathMap = new HashMap<String, String>();
    

    因此我们知道这个ActionHandler就是处理一些关于ActionMapping中对应的ControllerKey与Controller.class的事情。

    所以现在既然这些都已经清楚了,我们可以看看ActionHandler的庐山真面目了。

    在ActionHandler中我们可以看到这样一行注释:

    /**
     * handle
     * 1: Action action = actionMapping.getAction(target)
     * 2: new Invocation(...).invoke()
     * 3: render(...)
     */
    

    这就解释了handle方法需要做的事情了,首先是根据ActionMapping获得相应的Action,然后利用反射进行方法的调用,最后把结果映射到相应的页面上去。这就是核心的三个步骤了,接下来让我们详细的读一下这个源码:

    /**
     * ActionHandler
     */
    final class ActionHandler extends Handler {
    
        private final boolean devMode;
        private final ActionMapping actionMapping;
        private static final RenderFactory renderFactory = RenderFactory.me();
        private static final Log log = Log.getLog(ActionHandler.class);
    
        public ActionHandler(ActionMapping actionMapping, Constants constants) {
            this.actionMapping = actionMapping;
            this.devMode = constants.getDevMode();
        }
    
        /**
         * 这里进行了核心的handle方法描述:
         * 这里的target为以下格式:http://localhost:8080/ContextPath/ControllerKey/MethodName/parameters
         *
         */
    
        /**
         * handle
         * 1: Action action = actionMapping.getAction(target)
         * 2: new Invocation(...).invoke()
         * 3: render(...)
         * 首先是根据ActionMapping获得相应的Action,然后利用反射进行方法的调用,最后把结果映射到相应的页面上去;
         */
        public final void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) {
            if (target.indexOf('.') != -1) {  // .love,就过滤了;
                return ;
            }
    
            isHandled[0] = true;
            String[] urlPara = {null};
            Action action = actionMapping.getAction(target, urlPara);   // 1.根据ActionMapping获得相应的Action;
    
            if (action == null) {
                if (log.isWarnEnabled()) {
                    String qs = request.getQueryString();
                    log.warn("404 Action Not Found: " + (qs == null ? target : target + "?" + qs));
                }
                renderFactory.getErrorRender(404).setContext(request, response).render();
                return ;
            }
    
            try {
                Controller controller = action.getControllerClass().newInstance();  // Action所在的控制器;Controller
                controller.init(request, response, urlPara[0]);     // Controller初始化,配置request, response
    
                if (devMode) {
                    if (ActionReporter.isReportAfterInvocation(request)) {
                        new Invocation(action, controller).invoke();    // 利用反射进行方法的调用;
                        ActionReporter.report(controller, action);
                    } else {
                        ActionReporter.report(controller, action);
                        new Invocation(action, controller).invoke();    // 利用反射进行方法的调用;
                    }
                }
                else {
                    new Invocation(action, controller).invoke();    // 2. 然后利用反射进行方法的调用;
                }
    
                Render render = controller.getRender();     // 3. 结果映射到相应的页面上;
                if (render instanceof ActionRender) {
                    String actionUrl = ((ActionRender)render).getActionUrl();
                    if (target.equals(actionUrl))
                        throw new RuntimeException("The forward action url is the same as before.");
                    else
                        handle(actionUrl, request, response, isHandled);
                    return ;
                }
    
                if (render == null)
                    render = renderFactory.getDefaultRender(action.getViewPath() + action.getMethodName());
                render.setContext(request, response, action.getViewPath()).render();
            }
            catch (RenderException e) {
                if (log.isErrorEnabled()) {
                    String qs = request.getQueryString();
                    log.error(qs == null ? target : target + "?" + qs, e);
                }
            }
            catch (ActionException e) {
                int errorCode = e.getErrorCode();
                if (errorCode == 404 && log.isWarnEnabled()) {
                    String qs = request.getQueryString();
                    log.warn("404 Not Found: " + (qs == null ? target : target + "?" + qs));
                }
                else if (errorCode == 401 && log.isWarnEnabled()) {
                    String qs = request.getQueryString();
                    log.warn("401 Unauthorized: " + (qs == null ? target : target + "?" + qs));
                }
                else if (errorCode == 403 && log.isWarnEnabled()) {
                    String qs = request.getQueryString();
                    log.warn("403 Forbidden: " + (qs == null ? target : target + "?" + qs));
                }
                else if (log.isErrorEnabled()) {
                    String qs = request.getQueryString();
                    log.error(qs == null ? target : target + "?" + qs, e);
                }
                e.getErrorRender().setContext(request, response, action.getViewPath()).render();
            }
            catch (Throwable t) {
                if (log.isErrorEnabled()) {
                    String qs = request.getQueryString();
                    log.error(qs == null ? target : target + "?" + qs, t);
                }
                renderFactory.getErrorRender(500).setContext(request, response, action.getViewPath()).render();
            }
        }
    }
    

    到这里,我们简单了看了一下JFinal的实现原理。

    相关文章

      网友评论

          本文标题:JFinal实现原理

          本文链接:https://www.haomeiwen.com/subject/ooaspttx.html