jetty之ContextHandler

来源:互联网 发布:seo站长工具查询 编辑:程序博客网 时间:2024/06/05 11:37

这篇文章要分析的类型是非常重要的一个类型...

ContextHandler,从名字上来看就是上下文的handler...

这里普及一下知识:每一个WebApp都对应相应一个context,那么也就对应一个contextHandler当servlet容器收到外部的http请求之后,会根据其请求的path信息来找到相应的webapplication来处理,也就是要找到对应的contextHandler来处理 ,这里也就知道了contextHandler的最重要的作用,那就是将属于当前web的http请求交由内部对应的servlet来处理。。。

由于这个类型的代码比较多,那么就不贴出所有的代码了。。。将其中一些重要的属性和方法抽出来分析一下就好了。。。(ContextHandler继承自HandlerWrapper)

首先来看一个线程变量的定义:

private static ThreadLocal __context=new ThreadLocal();  //当前的线程变量,用于保存到当前的servletContext

这个线程变量用于储存当前的servletContext,这个是在servlet标准中定义的接口,jetty中有自己的实现。。其定义就在ContextHandler类型中,一个内部类。。。

接下来是另外的属性定义:

    protected SContext _scontext;   //相应的servletcontext        private AttributesMap _attributes;  //属性,    private AttributesMap _contextAttributes;  //context的属性    private ClassLoader _classLoader;  //用于加载app的class    private String _contextPath="/";  //例如/manager什么的    private Map _initParams;  //初始化参数    private String _displayName;    private Resource _baseResource;    //app部署的根目录 file:/C:/Users/js/AppData/Local/Temp/Jetty_127_0_0_1_80_manager.war__manager__.24ly2g/webapp/    private MimeTypes _mimeTypes;  //文件类型对应    private Map _localeEncodingMap;    private String[] _welcomeFiles;  //首页的路径    private ErrorHandler _errorHandler;   //错误handler    private String[] _vhosts;  //虚拟主机名    private Set _connectors;   //其所用的connector    private EventListener[] _eventListeners;    private Logger _logger;    private boolean _shutdown;    private boolean _allowNullPathInfo;    private int _maxFormContentSize=Integer.getInteger("org.mortbay.jetty.Request.maxFormContentSize",200000).intValue();    private boolean _compactPath=false;    //一些lisntener的定义    private Object _contextListeners;    //contextListener    private Object _contextAttributeListeners;    private Object _requestListeners;    private Object _requestAttributeListeners;    private Set _managedAttributes;

这里的scontext就是servlet的servlContext,contextPath是当前web的path,我们如果在servlet容器中启动多个app,那么每个webapplication的名字就是它的contextPath。。。。另外还有一些其他的属性,例如当前部署的根目录,一些初始化参数什么的。。。。

还有就是一些listener,例如contextListener什么的。。。

那么接下来我们来看看它的最重要的方法,handle方法:

    //handle方法,用于处理远程的http请求,这里的target就是http请求的path,例如/manager/hello,这里其实是用于判断当前请求的path什么的,如果属于当前的webapp,那么进行处理    public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch)            throws IOException, ServletException  {        boolean new_context=false;        SContext old_context=null;        String old_context_path=null;        String old_servlet_path=null;        String old_path_info=null;        ClassLoader old_classloader=null;        Thread current_thread=null;        //获取当前的http请求        Request base_request=(request instanceof Request)?(Request)request:HttpConnection.getCurrentConnection().getRequest();        if( !isStarted() || _shutdown || (dispatch==REQUEST && base_request.isHandled()))            return;                old_context=base_request.getContext();  //获取servletContext,这里一般情况下都是null,因为新建的socket连接发起的http请求                // Are we already in this context?        if (old_context!=_scontext) {            new_context=true;            //如果有配置虚拟服务器名字,那么需要判断            if (_vhosts!=null && _vhosts.length>0)            {                String vhost = normalizeHostname( request.getServerName());                boolean match=false;                                // TODO non-linear lookup                for (int i=0;!match && i<_vhosts.length;i++)                {                    String contextVhost = _vhosts[i];                    if(contextVhost==null) continue;                    if(contextVhost.startsWith("*.")) {                        // wildcard only at the beginning, and only for one additional subdomain level                        match=contextVhost.regionMatches(true,2,vhost,vhost.indexOf(".")+1,contextVhost.length()-2);                    } else                        match=contextVhost.equalsIgnoreCase(vhost);                }                if (!match)                    return;            }                       //检查connector            if (_connectors!=null && _connectors.size()>0)            {                String connector=HttpConnection.getCurrentConnection().getConnector().getName();                if (connector==null || !_connectors.contains(connector))                    return;            }                        // Nope - so check the target.            if (dispatch==REQUEST)            {                if (_compactPath) {                    target=URIUtil.compactPath(target);                }                                if (target.equals(_contextPath)){ //如果这里http请求的path等于contextPath,那么表示真正的pathinfo是空的,例如/manager,(启动多个app的话,会自动用app的名字来作为contextPath的前缀)                    if (!_allowNullPathInfo && !target.endsWith(URIUtil.SLASH)) { //判断是否运行空的path                        base_request.setHandled(true);                        if (request.getQueryString()!=null)                            response.sendRedirect(URIUtil.addPaths(request.getRequestURI(),URIUtil.SLASH)+"?"+request.getQueryString());                        else                             response.sendRedirect(URIUtil.addPaths(request.getRequestURI(),URIUtil.SLASH));                        return;                    }                    if (_contextPath.length()>1)                    {                        target=URIUtil.SLASH;                        request.setAttribute("org.mortbay.jetty.nullPathInfo",target);                    }                } else if (target.startsWith(_contextPath) && (_contextPath.length()==1 || target.charAt(_contextPath.length())=='/')){                   //这里看http的请求path是否已当前的contextPaht开始                if (_contextPath.length()>1)                        target=target.substring(_contextPath.length());                } else  {                    //不是这个context该处理的,也就是不是这个webapplication该处理的                    return;                }            }        }                try        {            old_context_path=base_request.getContextPath();  //这里一般是空,因为刚开始没有设置            old_servlet_path=base_request.getServletPath();            old_path_info=base_request.getPathInfo();                        //设置servletContext            base_request.setContext(_scontext);  //为当前的http请求设置servletContext            if (dispatch!=INCLUDE && target.startsWith("/"))            {                if (_contextPath.length()==1)                    base_request.setContextPath("");                else                    base_request.setContextPath(_contextPath);  //设置contextPath                base_request.setServletPath(null);                base_request.setPathInfo(target);  //设置pathInfo            }            ServletRequestEvent event=null;            if (new_context) {                // Set the classloader            //设置当前的classLoader                if (_classLoader!=null)                {                    current_thread=Thread.currentThread();                    old_classloader=current_thread.getContextClassLoader();                    current_thread.setContextClassLoader(_classLoader);                }                                // Handle the REALLY SILLY request events!                if (_requestListeners!=null)                {                    event = new ServletRequestEvent(_scontext,request);                    for(int i=0;i<LazyList.size(_requestListeners);i++)                        ((ServletRequestListener)LazyList.get(_requestListeners,i)).requestInitialized(event);                }                //设置当前request的attributelistener                for(int i=0;i<LazyList.size(_requestAttributeListeners);i++)                    base_request.addEventListener(((EventListener)LazyList.get(_requestAttributeListeners,i)));            }                        // Handle the request            try  {                if (dispatch==REQUEST && isProtectedTarget(target))  //判断是否访问的是保护的资源,如果是的话,返回notfound,WEB-INF文件夹下面是保护资源                    throw new HttpException(HttpServletResponse.SC_NOT_FOUND);                //获取内部的handler来处理                Handler handler = getHandler();                //处理当前的http请求,这里其实是jetty.servlet.SessionHandler,不过最终还是调用ServletHandler                if (handler!=null)                    handler.handle(target, request, response, dispatch);            }            catch(HttpException e)            {                Log.debug(e);                response.sendError(e.getStatus(), e.getReason());            }            finally            {                // Handle more REALLY SILLY request events!                if (new_context)                {                    for(int i=LazyList.size(_requestListeners);i-->0;)                        ((ServletRequestListener)LazyList.get(_requestListeners,i)).requestDestroyed(event);                                        for(int i=0;i<LazyList.size(_requestAttributeListeners);i++)                        base_request.removeEventListener(((EventListener)LazyList.get(_requestAttributeListeners,i)));                }            }        }        finally        {            if (old_context!=_scontext)            {                // reset the classloader                if (_classLoader!=null)                {                    current_thread.setContextClassLoader(old_classloader);                }                                // reset the context and servlet path.                base_request.setContext(old_context);                base_request.setContextPath(old_context_path);                base_request.setServletPath(old_servlet_path);                base_request.setPathInfo(old_path_info);             }        }    }

这个方法就最终将会用于将http请求交给对应的servlet来处理。。。

这里的处理流程如下:

(1)判断当前的http请求的path与当前web程序的contextPath是否对应,如果不对应的话那么说明这个http请求不该由当前来处理。。。

(2)如果对应的话,那么设置当前这个request的servletContext,当前线程的classLoader等,

(3)调用内部的handler来处理这个request,这里一般情况下都是SessionHandler,不过SessionHandler其实最终也会调用ServletHandler来处理这个request,将其交给最终的servlet来处理。。。其实这也是jetty的链式调用的最重要利用。

(4)一些收尾的工作。。。。


好了,这里其实对整个ContextHandler的基本的一些了解就算差不多了。。。起码算是知道它用来干嘛的了。。至于说它的一些细节,就交给以后的运行分析再来说吧。。。

原创粉丝点击