JFinal中的Filter,Handler,Interceptor的功能作用

JFinal框架中的web.xml可以实现Filter的解析。

JFinal中的Filter(web.xml,自己写的Filter实现了Filter);

Filter中的详细逻辑可以引入Handler和JFinal中的,Interceptor;

权限问题:

权限问题比较理想的办法是使用 AOP,所以可以选择 Interceptor、Handler或Filter。JFinal提供了 Interceptor与Handler可供选择。

    通常情况下使用 Interceptor 比较合适。

 Interceptor 中可以方便地获得 actionKey、controllerKey来做权限控制的依据,

还可以使用render控制页面跳转以及使用redirect来重定向。

   Handler可以在更高层次来做权限,但失去了 interceptor中在上面提到的好处。

jfinal 的入口是 JFinalFilter,没有使用 servlet , 在 com.jfinal.core 包下面可以找到源代码

Filter:

JFinalFilter和其他的Filter一样,都是实现了Filter接口;

在web.xml中配置Filter可以对所有的请求及响应资源进行拦截过滤,如web.xml中的配置;

filter中的和filter-mapping中的必须一致;
中的类必须是定义的Filter实现类并且填写完成的包名;

是对请求及响应的URL资源拦截过滤。

JFinalFilter类三个方法init(),doFilter(),destroy()及生命周期介绍
1、Filter过滤器都是通过web容器作为实例化基础的,及它们是以web服务器作为容器承托。
2、init和destroy方法在整个生命周期中分别只执行一次:
init是在web容器启动时执行一次,对需要的服务实体进行初始化;
destroy是在web容器关闭是执行一次,对服务实例进行摧毁。

3、doFilter是最重要的方法,

当web应用正常启动之后前台的请求都跳进doFilter方法进行拦截处理,如果需要在Filter进行做文章,即可从此方法入手,doFilter方法中可进行资源链往下跳。

源码分析:

四、源码分析
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());
}
1、首先调用init方法进行初始化,调用createJFinalConfig对自定义的JFinalConfig实例化 ,jfilterConfig.getIntParameter('configClass')就是获取web.xml中filter里配置的init-param参数,即一个配置类路径。
2、jfinal.init()方法完成了 配置参数实体Constants、请求编码Encoding、Controller、Interceptor等等的初始化 ,想Controller等都是存放在一个Map集合里。
contextPath获取webRoot路径
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
  
// 获取 request、response,设置编码
HttpServletRequest request = (HttpServletRequest)req ; HttpServletResponse response = (HttpServletResponse)res ; request.setCharacterEncoding(encoding);
// 初始化的时候可以看出 contextPathLength 为 0 或者为 项目名称的长度
// 比如 target = webapp/xx/yy/zz,则截取后的 target = /xx/yy/zz
String target = request.getRequestURI() ; if ( contextPathLength != 0 ) { target = target.substring( contextPathLength ) ; }
// 在调用了 ActionHandler的 handle 方法之后,isHandled[0] 才会被置为 true。
boolean[] isHandled = {false} ; try {
// 重头戏,handler 链首到链尾 ActionHandler 依次进行处理
handler .handle(target , request , response , isHandled) ; }
catch (Exception e) {    
  if (log.isErrorEnabled()) {         
String qs = request.getQueryString();     
    log.error(qs == null ? target : target + "?" + qs, e);      }   }   
// 若最后 isHandled[0] = false,会执行下一个 Filter。
if (isHandled[ 0 ] == false ) {
   chain.doFilter(request, response);  
 }}
doFilter是接收HTTP资源请求的方法:
1、首先对request请求进行设置编码;
2、然后获取请求路径,并调用handle方法,调用此方法可以根据请求路径作为key去查找init初始化Controller的Map集合里查找对应的Controller实体并调用对应的处理方法;
3、如果请求路径不满足判断条件(即未找到对应的Controller),会调用chain.doFilter按默认条件寻找下一个资源

Handler:

doFilter - Handler 链中每个 handler.handle(...)

JFinal 初始化过程中可以 add JFinal 库中的Handler 或自定义的 Handler。

例如:ContextPathHandler,JFinal 自身扩展的 Handler。

访问项目时就会走过 handler 方法设置 contextPath。这样在前端就可以通过 ${CONTEXT_PATH} 得到项目根路径。

ContextPathHandler,JFinal 自身扩展的 Handler。

public class ContextPathHandler extends Handler {
    
    private String contextPathName;
    
    public ContextPathHandler() {
        contextPathName = "CONTEXT_PATH";
    }
    
    public ContextPathHandler(String contextPathName) {
        if (StrKit.isBlank(contextPathName))
            throw new IllegalArgumentException("contextPathName can not be blank.");
        this.contextPathName = contextPathName;
    }
    
    public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) {
        request.setAttribute(contextPathName, request.getContextPath());
        System.out.println("哈哈哈");
        next.handle(target, request, response, isHandled);
    }
}

FakeStaticHandler,也是 JFinal 自身扩展的 Handler。new FakeStaticHandler 时可定义后缀,访问路径 target 必须是以这个后缀结尾才可以进行下去。

public class FakeStaticHandler extends Handler {
    
    private String viewPostfix;
    
    public FakeStaticHandler() {
        viewPostfix = ".html";
    }
    
    public FakeStaticHandler(String viewPostfix) {
        if (StrKit.isBlank(viewPostfix))
            throw new IllegalArgumentException("viewPostfix can not be blank.");
        this.viewPostfix = viewPostfix;
    }
    
    public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) {
        if ("/".equals(target)) {
            next.handle(target, request, response, isHandled);
            return;
        }
        
        if (target.indexOf('.') == -1) {
            HandlerKit.renderError404(request, response, isHandled);
            return ;
        }
        
        int index = target.lastIndexOf(viewPostfix);
        if (index != -1)
            target = target.substring(0, index);
        next.handle(target, request, response, isHandled);
    }
}

到达 Handler 链尾 ActionHandler 处理

访问交给 Handler 链尾 ActionHandler调用 handle 方法进行处理:

根据访问路径 target 得到 Action,由 Action 得到 Controller

接着 new Invocation(action, controller).invoke() 进入责任链

反射机制调用 Controller 的方法处理(此方法可根据 Action 得到)。


public final void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) {
    if (target.indexOf('.') != -1) {
        return ;
    }
    
    isHandled[0] = true;
    String[] urlPara = {null};
    
    // actionMapping 根据 target 得到对应的 action
    Action action = actionMapping.getAction(target, urlPara);
    
    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 {
        // 由 action 得到对应的 Controller
        Controller controller = action.getControllerClass().newInstance();
        
        // Controller 初始化
        controller.init(request, response, urlPara[0]);
        
        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 {
            // 调用 Controller 相应的处理方法
         
  new Invocation(action, controller).invoke();
        }
        
        // 获取对应的 Render,如果是一个 ActionRender,就再交给 handler 处理;如果 Render == null会按照默认Render处理;
        Render render = controller.getRender();
        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 实现类  数据写入页面
        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();
    }
}

Interceptor:拦截器链如何处理数据


package com.jfinal.aop;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import com.jfinal.core.Action;
import com.jfinal.core.Controller;
import net.sf.cglib.proxy.MethodProxy;

/**
 * Invocation is used to invoke the interceptors and the target method
 */
@SuppressWarnings("unchecked")
public class Invocation {
   
   private Action action;
   private static final Object[] NULL_ARGS = new Object[0];   // Prevent new Object[0] by jvm for paras of action invocation.
   
   boolean useInjectTarget;
   private Object target;
   private Method method;
   private Object[] args;
   private MethodProxy methodProxy;
   private Interceptor[] inters;
   private Object returnValue = null;
   
   private int index = 0;
   
   // InvocationWrapper need this constructor
   protected Invocation() {
      this.action = null;
   }
   
   public Invocation(Action action, Controller controller) {
      this.action = action;
      this.inters = action.getInterceptors();
      this.target = controller;
      this.args = NULL_ARGS;
   }
   
   public Invocation(Object target, Method method, Object[] args, MethodProxy methodProxy, Interceptor[] inters) {
      this.action = null;
      this.target = target;
      this.method = method;
      this.args = args;
      this.methodProxy = methodProxy;
      this.inters = inters;
   }
   
   public void invoke() {
      if (index < inters.length) {
         inters[index++].intercept(this);
      }
      else if (index++ == inters.length) {   // index++ ensure invoke action only one time
         try {
            // Invoke the action
            if (action != null) {
               returnValue = action.getMethod().invoke(target, args);
            }
            // Invoke the method
            else {
               // if (!Modifier.isAbstract(method.getModifiers()))
                  // returnValue = methodProxy.invokeSuper(target, args);
               if (useInjectTarget)
                  returnValue = methodProxy.invoke(target, args);
               else
                  returnValue = methodProxy.invokeSuper(target, args);
            }
         }
         catch (InvocationTargetException e) {
            Throwable t = e.getTargetException();
            throw t instanceof RuntimeException ? (RuntimeException)t : new RuntimeException(e);
         }
         catch (RuntimeException e) {
            throw e;
         }
         catch (Throwable t) {
            throw new RuntimeException(t);
         }
      }
   }
   
   public Object getArg(int index) {
      if (index >= args.length)
         throw new ArrayIndexOutOfBoundsException();
      return args[index];
   }
   
   public void setArg(int index, Object value) {
      if (index >= args.length)
         throw new ArrayIndexOutOfBoundsException();
      args[index] = value;
   }
   
   public Object[] getArgs() {
      return args;
   }
   
   /**
    * Get the target object which be intercepted
    * <pre>
    * Example:
    * OrderService os = getTarget();
    * </pre>
    */
   public <T> T getTarget() {
      return (T)target;
   }
   
   /**
    * Return the method of this action.
    * <p>
    * You can getMethod.getAnnotations() to get annotation on action method to do more things
    */
   public Method getMethod() {
      if (action != null)
         return action.getMethod();
      return method;
   }
   
   /**
    * Return the method name of this action's method.
    */
   public String getMethodName() {
      if (action != null)
         return action.getMethodName();
      return method.getName();
   }
   
   /**
    * Get the return value of the target method
    */
   public <T> T getReturnValue() {
      return (T)returnValue;
   }
   
   /**
    * Set the return value of the target method
    */
   public void setReturnValue(Object returnValue) {
      this.returnValue = returnValue;
   }
   
   // ---------
   
   /**
    * Return the controller of this action.
    */
   public Controller getController() {
      if (action == null)
         throw new RuntimeException("This method can only be used for action interception");
      return (Controller)target;
   }
   
   /**
    * Return the action key.
    * actionKey = controllerKey + methodName
    */
   public String getActionKey() {
      if (action == null)
         throw new RuntimeException("This method can only be used for action interception");
      return action.getActionKey();
   }
   
   /**
    * Return the controller key.
    */
   public String getControllerKey() {
      if (action == null)
         throw new RuntimeException("This method can only be used for action interception");
      return action.getControllerKey();
   }
   
   /**
    * Return view path of this controller.
    */
   public String getViewPath() {
      if (action == null)
         throw new RuntimeException("This method can only be used for action interception");
      return action.getViewPath();
   }
   
   /**
    * return true if it is action invocation.
    */
   public boolean isActionInvocation() {
      return action != null;
   }
}
 
 
package com.tspace.common.interceptor;

import com.jfinal.aop.Interceptor;
import com.jfinal.aop.Invocation;
import com.jfinal.core.Controller;
import com.tspace.common.util.UrlUtil;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;

/**
 * 权限处理拦截器
 * @author  2016.2.5
 */
public class AuthInterceptor implements Interceptor {

   @Override
   public void intercept(Invocation inv) {
      //TODO 注解标识不拦截
      //Annotation[] as = inv.getMethod().getAnnotations();
      //System.out.println("********Annotation: " + as.length);
      
      Controller c = inv.getController();
      HttpServletRequest request = c.getRequest();
      
      int contextLength = request.getContextPath().length();
      String currUrl = request.getRequestURI().substring(contextLength);
      System.out.println(UrlUtil.formatBaseUrl(currUrl));
      
      List<String> noAuthUrl = c.getSessionAttr("noAuthUrl");
      if(noAuthUrl != null) {
         //页面权限处理,拦截action/method链接的所有/action/*页面
         for (String url : noAuthUrl) {
            if(UrlUtil.formatBaseUrl(currUrl).equals(UrlUtil.formatBaseUrl(url))) {
               c.renderText("没有权限访问该页面!");
               return;
            }
         }
         //按钮权限
         Map<String, Object> authBtn = c.getSessionAttr("noAuthBtnUrl");
         // TODO 强制类型装换看看是不是有好的解决方案
         @SuppressWarnings("unchecked")
         List<String> noAuthBtnUrl = (List<String>) authBtn.get("btnUrlList");
         @SuppressWarnings("unchecked")
         Map<String, String> noAuthBtnMap = (Map<String, String>) authBtn.get("pageBtnMap");
         request.setAttribute("noAuthBtn", noAuthBtnMap.get(currUrl));
         for (String btnUrl : noAuthBtnUrl) {
            if(currUrl.equals(btnUrl)) {
               c.renderText("没有权限访问该页面!");
               return;
            }
         }
      }
      inv.invoke();
   }
}

new Invocation(action, controller).invoke()由action 得到拦截器数组,如果数组为空,就可以利用反射机制进入 Controller 的方法进行处理的了

不为空,会按照顺序执行拦截器,最后通过反射进入Controller方法进行处理。

如果拦截器数组不为空,

就会遇到拦截器1 的拦截 - inters[0].intercept(this),

数组下标 +1,拦截器1 继续调用这个 Invocation 实例的 invoke() 方法,并且会在前后加上一些目的性操作;

若下标未越界,接着会遇到拦截器2 的拦截 - inters[1].intercept(this),

数组下标 +1,拦截器2 继续调用这个 Invocation 实例的 invoke() 方法,并且会在前面加上一些目的性操作;

如此继续直到下标越界,接着反射机制进入  Controller 的方法进行处理。

render(...)

接着上面 ActionHandler.handle 继续,在 Controller 方法处理的最后,

往往要调用 render 方法来实例化 render 变量。

例如,我自定义的一个 IndexController,在最后一步调用 render("next.html")

public void getMenu() {
   //admin用户拥有所有页面
   if("admin".equals(getSessionUser().get("usr_name"))) {
      renderJson(Menu.me.findAll());
   }else {
      Object menuList = getSessionAttr("menuList");
      renderJson(menuList);
   }
}

用到父类 Controller 的 render 方法,

通过 renderFactory.getRender 得到 render 实例。

还能使用 renderJosn、renderText 等多种方法


Filter:过滤器;

Handler:处理器;

Interceptor:拦截器


猜你喜欢

转载自blog.csdn.net/weixin_36810906/article/details/80450836