Jetty中Web程序的创建与启动

在前面的文章中就已经提到过,在jetty中,我们部署的每一个web应用程序都对应着一个webAppContext。。。

因此在jetty中,web应用程序的创建与启动说白了就是WebAppContext的创建于启动。。。

在前面我们还分析过ContextHandlerCollection这个类型,它可以看成是WebAppContext的容器,我们常见的部署方法就是在jetty中部署多个web程序,那么这个collection就是用于维护这些context,并且会对http请求进行路由,交给相应的webAppContext来处理。。。

这里用一张图来做表现一下他们之间的关系:



首先请求会先到server,然后server交给内部的contextHandlerCollection来处理。。然后其进行一下路由。。交给对应的WebAppContext来处理。。

上面就将整个http请求的大的处理流程就描述的差不多了。。。当然交给WebAppContext之后,其实还涉及到再一次的路由,将其交给对应的servlet来处理。。。当然这个就是后话了。。。


这里分析WebAppContext的创建一般情况下都是先来看看一个工具类的实现:WebAppDeployer,他就是专门用于建立WebAppContext的,并为其设置一些必须的参数:

那么我们来看看他的doStart方法吧:

[java] view plaincopy
  1. public void doStart() throws Exception {  
  2.     _deployed=new ArrayList();  //创建app的数组,也就是WebAppContext的数组   
  3.     scan();  
  4. }  

这里可以看到,先是创建了一个数组,这个数组就适用于存放待会将会创建的所有WebAppContext。。

接着调用scan方法,这个方法的作用其实从名字就看出来了,很明显,扫描。。。扫描要部署的app所在的目录,然后依次为这些app创建相应的webAppContext。。。

[java] view plaincopy
  1. public void scan() throws Exception  {  
  2.     if (_contexts==null)  //这个是在创建WebAppDployer的时候会传进来额  ,webcontextHandler的容器  
  3.         throw new IllegalArgumentException("No HandlerContainer");  
  4.   
  5.     Resource r=Resource.newResource(_webAppDir);  //用于存放app的文件夹,获取它的resource引用  
  6.       
  7.     if (!r.exists())  
  8.         throw new IllegalArgumentException("No such webapps resource "+r);  
  9.   
  10.     if (!r.isDirectory())  
  11.         throw new IllegalArgumentException("Not directory webapps resource "+r);  
  12.   
  13.     String[] files=r.list();  //当前文件夹下面的所有文件  
  14.     //遍历当前文件夹下面的所有文件  
  15.     files: for (int f=0; files!=null&&f<files.length; f++)  
  16.     {  
  17.         String context=files[f];  //文件的名字,这里其实也就可以知道默认是按照app的文件名字来命名其的context的名字,也就是用这个来进行路由  
  18.         if (context.equalsIgnoreCase("CVS/")||context.equalsIgnoreCase("CVS")||context.startsWith("."))  
  19.             continue;  
  20.   
  21.         //获取当前app的资源的resource引用  
  22.         Resource app=r.addPath(r.encode(context));  
  23.         //判断当前app的类型,是war或者jar  
  24.         if (context.toLowerCase().endsWith(".war")||context.toLowerCase().endsWith(".jar")) {  
  25.             context=context.substring(0,context.length()-4);  //将后缀去掉  
  26.             Resource unpacked=r.addPath(context);  
  27.             if (unpacked!=null&&unpacked.exists()&&unpacked.isDirectory())  
  28.                 continue;  
  29.         } else if (!app.isDirectory()) {  //连文件夹都不是。。什么扯淡的玩意。。直接跳过  
  30.             continue;  
  31.         }  
  32.         if (context.equalsIgnoreCase("root")||context.equalsIgnoreCase("root/")) {  
  33.             context=URIUtil.SLASH;  
  34.         } else {  
  35.             context="/"+context;  //相当于为context添加成从根目录开始  
  36.         }  
  37.         if (context.endsWith("/")&&context.length()>0) {  //如果是文件夹路径,那么需要将最后那个/去掉  
  38.             context=context.substring(0,context.length()-1);  
  39.         }  
  40.         // Check the context path has not already been added or the webapp itself is not already deployed  
  41.         //检查当前的的app是否已经部署了,或者有重名的context  
  42.         if (!_allowDuplicates)  //检查是否有冲突的  
  43.         {  
  44.             Handler[] installed=_contexts.getChildHandlersByClass(ContextHandler.class);  
  45.             for (int i=0; i<installed.length; i++)  
  46.             {  
  47.                 //这里一般都进不来  
  48.                 ContextHandler c=(ContextHandler)installed[i];  
  49.       
  50.                 if (context.equals(c.getContextPath()))  
  51.                     continue files;  
  52.                   
  53.                String path;  
  54.                if (c instanceof WebAppContext)  
  55.                    path = ((WebAppContext)c).getWar();  
  56.                else  
  57.                    path = (c.getBaseResource()==null?"":c.getBaseResource().getFile().getAbsolutePath());  
  58.   
  59.                 if (path.equals(app.getFile().getAbsolutePath()))  
  60.                     continue files;  
  61.             }  
  62.         }  
  63.   
  64.         // create a webapp  
  65.         WebAppContext wah=null;  //创建一个webappcontext  
  66.         if (_contexts instanceof ContextHandlerCollection &&   
  67.             WebAppContext.class.isAssignableFrom(((ContextHandlerCollection)_contexts).getContextClass())) {  
  68.             try {    
  69.                   
  70.                 wah=(WebAppContext)((ContextHandlerCollection)_contexts).getContextClass().newInstance();  
  71.             } catch (Exception e) {  
  72.                 throw new Error(e);  
  73.             }  
  74.         } else  {  
  75.             wah=new WebAppContext();  //创建一个webapplicationcontext  
  76.         }  
  77.           
  78.         // configure it  
  79.         wah.setContextPath(context);   //设置context的路径例如:/manager  
  80.         if (_configurationClasses!=null) {  
  81.             wah.setConfigurationClasses(_configurationClasses);  //设置默认的配置类  
  82.         }  
  83.         if (_defaultsDescriptor!=null)  
  84.             wah.setDefaultsDescriptor(_defaultsDescriptor);  
  85.         wah.setExtractWAR(_extract);  
  86.         wah.setWar(app.toString());  //设置war包的路径  
  87.         wah.setParentLoaderPriority(_parentLoaderPriority);  //class的加载是否是从父类加载器优先  
  88.         // add it  
  89.         _contexts.addHandler(wah);  //为当前的contexts添加一个contextHandler  
  90.         _deployed.add(wah);    //添加已经部署过的webAppContext  
  91.         //刚开始的话这里其实是不会启动的,因为会交给server来启动所有的context  
  92.         if (_contexts.isStarted())  { //ContextHandlerCollection如果已经启动,那么这里再启动一次。。  
  93.             _contexts.start();  // TODO Multi exception  
  94.         }  
  95.     }  
  96. }  

这个方法的定义还挺长的,不过其实代码还是比较容易理解的。。。

(1)遍历要部署的app所在的目录,这里一般情况下都是webapps目录。。。

(2)对于webapps目录下的所有文件,首先获取它的名字,然后判断当前文件的类型,如果是war,jar或者文件夹啥的,那就没问题了。。。如果是单个的文件的话,那就直接忽略掉了。。

(3)对名字进行预处理,为其首部加上“/”,处理之后的名字将会成为这个web程序的contextPath,ContextHandlerCollection将会用这个名字来进行路由。。

(4)判断是否有重名的app部署。。。

(5)创建WebAppContext,设置它的contextPath属性,app文件所在的路径,然后再将其加入到ContextHandlerCollection里面去。。。


好了。。到这里WebAppContext的创建过程就差不多了。。。。那么接下来就可以来看看WebAppContext的启动过程了。。。

[java] view plaincopy
  1. //这里可以理解为启动这个app,app都需要创建自己的classLoader  
  2. protected void doStart() throws Exception {  
  3.     try {  
  4.         loadConfigurations();  //加载需要用到的加载类对象,这些对象会被保存到configurations数组里面去  
  5.           
  6.             //设置他们的context  
  7.         for (int i=0;i<_configurations.length;i++) {  
  8.             _configurations[i].setWebAppContext(this);  
  9.         }  
  10.   
  11.         // Configure classloader  
  12.         _ownClassLoader=false;  
  13.         if (getClassLoader()==null) {  
  14.             WebAppClassLoader classLoader = new WebAppClassLoader(this);  //创建classLoader。这个classLoader将会专属于这个web应用,每个webApp都有一个自己专属的classLoader  
  15.             setClassLoader(classLoader);  //设置当前的classLoader  
  16.             _ownClassLoader=true//表示使用自己的classLoader  
  17.         }  
  18.   
  19.         if (Log.isDebugEnabled())  {  
  20.             ClassLoader loader = getClassLoader();  
  21.             Log.debug("Thread Context class loader is: " + loader);  
  22.             loader=loader.getParent();  
  23.             while(loader!=null)  
  24.             {  
  25.                 Log.debug("Parent class loader is: " + loader);   
  26.                 loader=loader.getParent();  
  27.             }  
  28.         }  
  29.         //这个里面的操作会加压war文件  
  30.         for (int i=0;i<_configurations.length;i++) {  
  31.             _configurations[i].configureClassLoader();  //这些configure对象的classLoader的配置  
  32.         }  
  33.         getTempDirectory();  
  34.           
  35.         super.doStart();  //父类的doStart方法  
  36.   
  37.         if (isLogUrlOnStart())   
  38.             dumpUrl();  
  39.     }  
  40.     catch (Exception e)  
  41.     {  
  42.         //start up of the webapp context failed, make sure it is not started  
  43.         Log.warn("Failed startup of context "+this, e);  
  44.         _unavailableException=e;  
  45.         _unavailable = true;  
  46.     }  
  47. }  


这里代码还是相对比较简单的吧,起码比较容易理解的

(1)首先调用了loadConfigurations方法,这个方法用创建一些默认的配置类的对象。然后将他们保存在configurations数组中。。。这些配置类有如下:   

"org.mortbay.jetty.webapp.WebInfConfiguration",   //对webinfo的处理,主要用于载入class文件以及jar包
        "org.mortbay.jetty.webapp.WebXmlConfiguration",    //这个主要是对web.xml的处理
        "org.mortbay.jetty.webapp.JettyWebXmlConfiguration",
        "org.mortbay.jetty.webapp.TagLibConfiguration" 

(2)创建WebAppClassLoader,这个classLoader用于加载当前web程序class文件。。。每一个web程序都会有一个自己的classLoader。。。他的作用就是为了实现各个程序之间的隔离,以及web程序与服务器资源的隔离。。

(3)调用每一个配置类对象的configureClassLoader方法,这个方法用于利用当前的classLoader进行一些预处理。。例如load当前app的jar包,class文件之类的。。。

(4)调用父类的doStart方法。。。


好了,在接着看父类的doStart方法之前。。。我们先来看看其中WebInfConfiguration的configureClassLoader方法做了什么事情吧。。WebInfConfiguration这个类型是干嘛的。看名字应该还挺清楚的。。。webinf相关的配置。。好了。。我们来看看它都干了什么时候吧。。。

[java] view plaincopy
  1. public  void configureClassLoader() throws Exception  {  
  2.     //cannot configure if the context is already started  
  3.     if (_context.isStarted())  
  4.     {  
  5.         if (Log.isDebugEnabled()){Log.debug("Cannot configure webapp after it is started");}  
  6.         return;  
  7.     }  
  8.     //这里用于获取web的基本信息,将会促使去解压当前的war包等,然后这里其实就相当于获取部署的那个文件夹下的WEB-INF文件夹  
  9.     Resource web_inf=_context.getWebInf();  
  10.   
  11.     // Add WEB-INF classes and lib classpaths  
  12.     if (web_inf != null && web_inf.isDirectory() && _context.getClassLoader() instanceof WebAppClassLoader)  
  13.     {  
  14.         //获取webinf目录下的classes文件夹的resource引用,这个里面就是用户自己定义的源代码的class文件。。  
  15.         Resource classes= web_inf.addPath("classes/");  
  16.         //添加class文件的loader  
  17.         if (classes.exists()) {  //用当前的classLoader来加载classes下面的class文件  
  18.             ((WebAppClassLoader)_context.getClassLoader()).addClassPath(classes.toString());  
  19.         }  
  20.         //这里当然还需要将那些jar包导进来了  
  21.         Resource lib= web_inf.addPath("lib/");  
  22.         if (lib.exists() || lib.isDirectory())  
  23.             ((WebAppClassLoader)_context.getClassLoader()).addJars(lib);  
  24.     }  
  25.       
  26.  }  

这个方法是干嘛用的看代码应该很清楚了吧。。。而且它的重要性应该也很明白了。。用于加载当前应用程序的webinf目录下面的资源。。例如classes文件夹下的class文件,以及lib文件夹下面的jar。。。。


好了。。那么接着回到webAppContext的启动。。。那么接下来应该看父类(ContextHandler)的doStart方法了:

[java] view plaincopy
  1. //用于组件的启动  
  2. protected void doStart() throws Exception {  
  3.     if (_contextPath==null)  
  4.         throw new IllegalStateException("Null contextPath");  
  5.       
  6.     _logger=Log.getLogger(getDisplayName()==null?getContextPath():getDisplayName());  
  7.     ClassLoader old_classloader=null;   //classloader  
  8.     Thread current_thread=null;  
  9.     SContext old_context=null;  
  10.   
  11.     _contextAttributes=new AttributesMap();  //属性map  
  12.     try {  
  13.         //设置classLoader  
  14.         if (_classLoader!=null) {  
  15.             current_thread=Thread.currentThread();  
  16.             old_classloader=current_thread.getContextClassLoader();  //存储以前 的classLoader  
  17.             current_thread.setContextClassLoader(_classLoader);  //将当前这个webContext的classLoader保存到线程变量里面  
  18.         }  
  19.         if (_mimeTypes==null)  
  20.             _mimeTypes=new MimeTypes();  //mimetype  
  21.           
  22.         old_context=(SContext)__context.get();  //获取当前的线程servletcontext  
  23.         __context.set(_scontext);  //设置线程变量,保存当前的servletContext  
  24.           
  25.         if (_errorHandler==null)  
  26.             setErrorHandler(new ErrorHandler());  
  27.   
  28.         //这里会中子类中的startContext一层一层的向上调用  
  29.         startContext();  //启动当前的context  
  30.          
  31.     }  
  32.     finally {  
  33.         __context.set(old_context);  
  34.           
  35.         // reset the classloader  
  36.         if (_classLoader!=null) {  
  37.             current_thread.setContextClassLoader(old_classloader);  
  38.         }  
  39.     }  
  40. }  

父类的启动也还蛮简单的吧。。首先获取当前的线程classLoader,然后将其设置为当前WebAppContext的classloader,而且还要设置当前的线程变量context,将其设置为当前webAppContext的servletContext。。。

好饿了,最后就是执行startContext方法了。。。

那么接下来来看看WebAppContext的startContext方法的定义吧:

[java] view plaincopy
  1. protected void startContext()  
  2.     throws Exception  
  3. {  
  4.     // 一些默认的configure  
  5.     for (int i=0;i<_configurations.length;i++)  
  6.         _configurations[i].configureDefaults();  
  7.       
  8.     //获取webinf文件夹的resource引用  
  9.     Resource web_inf=getWebInf();  
  10.     if (web_inf!=null)  
  11.     {  
  12.         Resource work= web_inf.addPath("work");  
  13.         if (work.exists()  
  14.                         && work.isDirectory()  
  15.                         && work.getFile() != null  
  16.                         && work.getFile().canWrite()  
  17.                         && getAttribute(ServletHandler.__J_S_CONTEXT_TEMPDIR) == null)  
  18.             setAttribute(ServletHandler.__J_S_CONTEXT_TEMPDIR, work.getFile());  
  19.     }  
  20.       
  21.     // Configure webapp 这个里面会读取默认的jetty的xml定义以及当前的web的xml的定义,创建servlet等  
  22.     for (int i=0;i<_configurations.length;i++)  
  23.         _configurations[i].configureWebApp();  
  24.   
  25.       
  26.     super.startContext();  
  27. }  

这里其实无非就是执行默认的配置类对象的一些方法,首先是configureDefaults方法,

这里就来看看WebXmlConfiguration的configureDefaults方法做了什么事情吧:

[java] view plaincopy
  1. //这里是读取默认的jetty的webdefault.xml,一些default的servlet啥的  
  2. public void configureDefaults() throws Exception {  
  3.     if (_context.isStarted()) { //如果都已经启动了,那么需要报错  
  4.         if (Log.isDebugEnabled()){Log.debug("Cannot configure webapp after it is started");}  
  5.         return;  
  6.     }  
  7.     String defaultsDescriptor=getWebAppContext().getDefaultsDescriptor();  //获取webdefault.xml文件的地址  
  8.     if(defaultsDescriptor!=null&&defaultsDescriptor.length()>0)  
  9.     {  
  10.         Resource dftResource=Resource.newSystemResource(defaultsDescriptor);  
  11.         if(dftResource==null) {  
  12.             dftResource=Resource.newResource(defaultsDescriptor);  //webdefault.xml的引用;  定义了一些基本的servlet,filter一类的  
  13.         }  
  14.         configure(dftResource.getURL().toString());  //这个里面会创建里面定义的filter,servlet一类的  
  15.         _defaultWelcomeFileList=_welcomeFiles!=null;  
  16.     }  
  17. }  

代码其实也看不出来什么东西,不过注释已经说的很清楚了,对于webdefault.xml这个文件,应该搞过jetty的都知道它是干嘛的吧。。那么这个方法具体做了啥应该就很清楚了吧。。。。

接下来来看看WebXmlConfiguration的configureWebApp干了什么事情吧:

[java] view plaincopy
  1. //处理当前的webapp的web.xml  
  2. public void configureWebApp() throws Exception {  
  3.     //cannot configure if the context is already started  
  4.     if (_context.isStarted())  
  5.     {  
  6.         if (Log.isDebugEnabled())  
  7.             Log.debug("Cannot configure webapp after it is started");  
  8.         return;  
  9.     }  
  10.   
  11.     URL webxml=findWebXml();  //获取webinf目录下的web.xml  
  12.     if (webxml!=null) {  
  13.         configure(webxml.toString());  //处理这个xml  
  14.     }  
  15.      
  16.     String overrideDescriptor=getWebAppContext().getOverrideDescriptor(); //覆盖的web描述在,这里一个web程序可能会定义多个web描述文件吧。。  
  17.     if(overrideDescriptor!=null&&overrideDescriptor.length()>0)  
  18.     {  
  19.         Resource orideResource=Resource.newSystemResource(overrideDescriptor);  
  20.         if(orideResource==null)  
  21.             orideResource=Resource.newResource(overrideDescriptor);  
  22.         _xmlParser.setValidating(false);  
  23.         configure(orideResource.getURL().toString());  
  24.     }  
  25. }  

这里注释应该也说的很清楚吧。。。用于处理webinf目录下的web.xml文件。。这里主要是调用configure方法来处理这个文件。。这个方法做的事情很重要。。我觉得有必要以后来具体的说明。。。

不过可以粗略的说一下这个方法做了什么事情。。。它用于处理xml文件。。例如当处理到servlet的申明的时候,会根据申明的参数创建相应的servletholder,并将其保存到servlethandler里面去。。。。


好了。。WebAppContext的的startContext方法就差不多了。。那么来看看他父类定义的此方法的定义:

[java] view plaincopy
  1. //这里主要是初始化servletHandler  
  2. protected void startContext() throws Exception  
  3. {  
  4.     super.startContext();  
  5.       
  6.     // OK to Initialize servlet handler now  
  7.     if (_servletHandler != null && _servletHandler.isStarted()) {  //servlethandler的初始化  
  8.         _servletHandler.initialize();  //这里会启动里面的所有servlet,包括获取servlet的class,如果需要startup的话还要新建servlet的对对象然后init  
  9.     }  
  10. }  

好吧。没啥意思。。。首先调用了父类的相应的方法,然后再调用了servletHanlder的initlize方法,它将会启动当前定义的servlet,包括从classLoader中获取class文件。。如果需要的话实例化servlet等。。

接下来看看父类中的这个方法做了什么事情吧:

[java] view plaincopy
  1. //启动当前的context,其实这里最重要的就是通知contextListener,然后对属性进行设置  
  2. protected void startContext()  
  3.     throws Exception {  
  4.     super.doStart();  //这个其实是用于启动内部的handler,这里一把都是_sessionHandler  
  5.   
  6.     if (_errorHandler!=null)  
  7.         _errorHandler.start();  
  8.       
  9.     // 调用当前的contextListener的contextInitialized方法  
  10.     if (_contextListeners != null ) {  
  11.         ServletContextEvent event= new ServletContextEvent(_scontext);  
  12.         for (int i= 0; i < LazyList.size(_contextListeners); i++) {  
  13.             ((ServletContextListener)LazyList.get(_contextListeners, i)).contextInitialized(event);  //相当于向这些contextListener发送事件  
  14.         }  
  15.     }  
  16.   
  17.     String managedAttributes = (String)_initParams.get(MANAGED_ATTRIBUTES);  
  18.     if (managedAttributes!=null)  
  19.     {  
  20.         _managedAttributes=new HashSet();  
  21.         String[] attributes = managedAttributes.toString().split(",");  
  22.  for (int  i=0;i<attributes.length;i++)  
  23.             _managedAttributes.add(attributes[i]);  
  24.   
  25.         Enumeration e = _scontext.getAttributeNames();  
  26.         while(e.hasMoreElements())  
  27.         {  
  28.             String name = (String)e.nextElement();  
  29.             Object value = _scontext.getAttribute(name);  
  30.             setManagedAttribute(name,value);  
  31.         }  
  32.     }         
  33. }  

其实蛮简单的,最主要的就是调用了contextListener的contextInitialized 方法。。。


好了。。到这里整个WebAppContext的创建和启动就差不多啦。。。。


总结一下整个过程:

(1)创建WebAppContext对象,设置它的contextPath,资源文件路径啥的。

(2)创建配置类对象。。。以及当前context的classLoader。。。

(3)解压当前的程序war包什么的,并用classLoader来载入里面的源文件jar包什么的。。

(4)处理webdefault.xml,例如里面声明的默认servlet,filter啥的。。

(5)处理用户的webinf里面的web.xml(默认是它),根据里面的servlet,listener啥的定义,创建相应的servletholder啥的。。。。

(6)调用contextListener的contextInitialized方法,接着还要获取用户定义的initParams,将他们保存在当前servletContext里面去。。

(7)初始化servletHandler,例如从classLoader里面获取servlet的class文件,如果配置了startup的话,还需要立即创建servlet对象,而且调用init方法。。。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值