WebAppContext即Web Application ContextHandler,表示一個Web應用的上下文,是上篇文章介紹的ContextHandler的一個子類,也是實際中應用的ContextHandler。先來看下類圖:
javascript
能夠看到在ContextHandler和WebAppContext中間還有一個ServletContxtHandler,下面就先從這個類開始分析。java
ServletContextHandler是ContextHandler的直接子類,具備ContextHandler的大部分特徵,不一樣的地方是ServletContextHandler中管理了三個Handler:ServletHandler、SessionHandler、SecurityHandler。web
protected SessionHandler _sessionHandler;
protected SecurityHandler _securityHandler;
protected ServletHandler _servletHandler;
ServletHandler對ContextHandler的擴展也主要集中在對三個Handler特別是ServletHandler的管理上。SessionHandler和SecurityHandler都是可選的,ServletHandler中定義了幾個常量用來表示是否須要相應的Handler。apache
public final static int SESSIONS=1; //須要SessionHandler
public final static int SECURITY=2; //須要SecurityHandler
public final static int NO_SESSIONS=0;//不須要SessionHandler
public final static int NO_SECURITY=0;//不須要SecurityHandler
經過 new ServletContext("/context",Context.SESSIONS|Context.NO_SECURITY);
能夠配置SessionHandler和SecurityHandler。對這幾個的Handler的組織是放在startContext()方法裏進行的:websocket
protected void startContext() throws Exception
{
//防止默寫Handler沒有正確配置,強行初始化Handler
getSessionHandler();
getSecurityHandler();
getServletHandler();
//下面是Handler鏈的組織過程
Handler handler = _servletHandler;
if (_securityHandler!=null)
{
_securityHandler.setHandler(handler);
handler=_securityHandler;
}
if (_sessionHandler!=null)
{
_sessionHandler.setHandler(handler);
handler=_sessionHandler;
}
// 跳過全部Wrapper類型的Handler
_wrapper=this;
while (_wrapper!=handler && _wrapper.getHandler() instanceof HandlerWrapper)
_wrapper=(HandlerWrapper)_wrapper.getHandler();
// if we are not already linked
if (_wrapper!=handler)
{
if (_wrapper.getHandler()!=null )
throw new IllegalStateException("!ScopedHandler");
_wrapper.setHandler(handler);
}
super.startContext();
//下面是ServletHandler初始化的過程
if (_servletHandler != null && _servletHandler.isStarted())
{
for (int i=_decorators.size()-1;i>=0; i--)
{
Decorator decorator = _decorators.get(i);
if (_servletHandler.getFilters()!=null)
for (FilterHolder holder:_servletHandler.getFilters())
decorator.decorateFilterHolder(holder);
if(_servletHandler.getServlets()!=null)
for (ServletHolder holder:_servletHandler.getServlets())
decorator.decorateServletHolder(holder);
}
_servletHandler.initialize();
}
}
上面最重要的工做就是造成了ServletContextHandler—>SessionHandler—>SecurityHandler—>ServletHandler的Handler鏈,經過這個Handler鏈,ServletContextHandler拿到請求以後就能依次傳遞給各個Handler處理,最後會經過ServletHandler轉交給具體的Servlet處理。最後再這個方法裏還對ServeltHandler進行了初始化。ServletHandler的initialize()方法中主要是對Filter和Servlet調用其start()方法進行啓動。
ServletContextHandler中還有一些添加Servlet和Filter的方法,但實際上都是調用ServletHandler的相應方法:session
/** conveniance method to add a servlet. */
public ServletHolder addServlet(Class<? extends Servlet> servlet,String pathSpec)
{
return getServletHandler().addServletWithMapping(servlet.getName(), pathSpec);
}
/** conveniance method to add a servlet. */
public void addServlet(ServletHolder servlet,String pathSpec)
{
getServletHandler().addServletWithMapping(servlet, pathSpec);
}
/** conveniance method to add a filter */
public void addFilter(FilterHolder holder,String pathSpec,EnumSet<DispatcherType> dispatches)
{
getServletHandler().addFilterWithMapping(holder,pathSpec,dispatches);
}
/** convenience method to add a filter */
public FilterHolder addFilter(Class<? extends Filter> filterClass,String pathSpec,EnumSet<DispatcherType> dispatches)
{
return getServletHandler().addFilterWithMapping(filterClass,pathSpec,dispatches);
}
主要的來講,ServletContextHandler就是重寫了父類的startContext()方法,將SessionHandler、SecurityHandler、ServletHandler組成一個Handler鏈來對請求進行處理,而且在startContext()方法中進行了ServletHandler的初始化。另外ServletContextHandler中還提供了一個內部類Context,它繼承了ContextHandler的內部類,也是ServletContext的一個具體實現。app
上篇文章分析ContextHandler的時候就提到過,做爲Web應用的上下文ContextHandler沒有實現對資源文件的處理,而WebAppContext做爲ContextHandler的子類正是對這一部分進行了擴展。
主要的啓動邏輯和資源加載邏輯都放在doStart()方法裏,下面來重點看下這個方法:eclipse
@Override
protected void doStart() throws Exception
{
try
{
_metadata.setAllowDuplicateFragmentNames(isAllowDuplicateFragmentNames());
preConfigure();
super.doStart();
postConfigure();
if (isLogUrlOnStart())
dumpUrl();
}
catch (Exception e)
{
//start up of the webapp context failed, make sure it is not started
LOG.warn("Failed startup of context "+this, e);
_unavailableException=e;
setAvailable(false);
if (isThrowUnavailableOnStartupException())
throw e;
}
}
super.start()會調用startContext()方法,而WebAppContext也重寫了這個方法:webapp
@Override
protected void startContext()
throws Exception
{
configure();
//resolve the metadata
_metadata.resolve(this);
super.startContext();
}
這裏面主要是調用configure()方法,結合上面doStart()方法中流程,能夠看到大概就是preConfigure()—>configure()—>postConfigure()這麼三個步驟。
先來看下preConfigure()方法:socket
public void preConfigure() throws Exception
{
// 設置配置類集合:configurations
loadConfigurations();
// 設置系統類class:system classes
loadSystemClasses();
// 設置應用服務本身須要的一些class:server classes
loadServerClasses();
// 爲應用建立一個ClassLoader,每一個web應用都須要一個本身的ClassLoader
_ownClassLoader=false;
if (getClassLoader()==null)
{
WebAppClassLoader classLoader = new WebAppClassLoader(this);
setClassLoader(classLoader);
_ownClassLoader=true;
}
if (LOG.isDebugEnabled())
{
ClassLoader loader = getClassLoader();
LOG.debug("Thread Context classloader {}",loader);
loader=loader.getParent();
while(loader!=null)
{
LOG.debug("Parent class loader: {} ",loader);
loader=loader.getParent();
}
}
// 調用每一個配置類的preConfigure()方法
for (int i=0;i<_configurations.length;i++)
{
LOG.debug("preConfigure {} with {}",this,_configurations[i]);
_configurations[i].preConfigure(this);
}
}
一開始就是三個load()方法,先看下loadConfigurations():
protected void loadConfigurations()
throws Exception
{
//若是_configurations屬性不爲空,說明已經初始化過了
if (_configurations!=null)
return;
//若是沒有設置過配置類,則使用默認提供的配置類,不然使用設置的
if (!_configurationClassesSet)
_configurationClasses=__dftConfigurationClasses;
//下面其實就是經過ClassLoader將_configurationClasses中指定的配置類加載到內存中並新建一個實例
_configurations = new Configuration[_configurationClasses.length];
for (int i = 0; i < _configurationClasses.length; i++)
{
_configurations[i]=(Configuration)Loader.loadClass(this.getClass(), _configurationClasses[i]).newInstance();
}
}
下面是jetty默認的配置類集合:
//每個配置類負責一部分配置文件的解析或class文件/jar包的導入
private static String[] __dftConfigurationClasses =
{
"org.eclipse.jetty.webapp.WebInfConfiguration",//對webinfo的處理,主要用於載入class文件以及jar包
"org.eclipse.jetty.webapp.WebXmlConfiguration",//負責web.xml的解析
"org.eclipse.jetty.webapp.MetaInfConfiguration",
"org.eclipse.jetty.webapp.FragmentConfiguration",
"org.eclipse.jetty.webapp.JettyWebXmlConfiguration"//,
//"org.eclipse.jetty.webapp.TagLibConfiguration"
} ;
上面的_configurations中就是存放了這些配置類的實例。
接下來的loadSystemClasses()、loadServerClasses()分別用來設置系統須要的classes 和應用服務本身須要的classes,這兩個類型的類在jetty中都有默認值:
// System classes are classes that cannot be replaced by
// the web application, and they are *always* loaded via
// system classloader.
public final static String[] __dftSystemClasses =
{
"java.", // Java SE classes (per servlet spec v2.5 / SRV.9.7.2)
"javax.", // Java SE classes (per servlet spec v2.5 / SRV.9.7.2)
"org.xml.", // needed by javax.xml
"org.w3c.", // needed by javax.xml
"org.apache.commons.logging.", // TODO: review if special case still needed
"org.eclipse.jetty.continuation.", // webapp cannot change continuation classes
"org.eclipse.jetty.jndi.", // webapp cannot change naming classes
"org.eclipse.jetty.plus.jaas.", // webapp cannot change jaas classes
"org.eclipse.jetty.websocket.WebSocket", // WebSocket is a jetty extension
"org.eclipse.jetty.websocket.WebSocketFactory", // WebSocket is a jetty extension
"org.eclipse.jetty.servlet.DefaultServlet" // webapp cannot change default servlets
} ;
// Server classes are classes that are hidden from being
// loaded by the web application using system classloader,
// so if web application needs to load any of such classes,
// it has to include them in its distribution.
public final static String[] __dftServerClasses =
{
"-org.eclipse.jetty.continuation.", // don't hide continuation classes
"-org.eclipse.jetty.jndi.", // don't hide naming classes
"-org.eclipse.jetty.plus.jaas.", // don't hide jaas classes
"-org.eclipse.jetty.websocket.WebSocket", // WebSocket is a jetty extension
"-org.eclipse.jetty.websocket.WebSocketFactory", // WebSocket is a jetty extension
"-org.eclipse.jetty.servlet.DefaultServlet", // don't hide default servlet
"-org.eclipse.jetty.servlet.listener.", // don't hide useful listeners
"org.eclipse.jetty." // hide other jetty classes
} ;
上面的load操做,其實就是從Server裏面拿到對應屬性的設置,而後爲其建立ClasspathPattern對象,若是從Server中沒有對其進行設置,則使用上面的默認值建立ClasspathPattern對象,下面是loadSystemClasses()方法:
protected void loadSystemClasses()
{
if (_systemClasses != null)
return;
//look for a Server attribute with the list of System classes
//to apply to every web application. If not present, use our defaults.
Server server = getServer();
if (server != null)
{
Object systemClasses = server.getAttribute(SERVER_SYS_CLASSES);
if (systemClasses != null && systemClasses instanceof String[])
_systemClasses = new ClasspathPattern((String[])systemClasses);
}
if (_systemClasses == null)
_systemClasses = new ClasspathPattern(__dftSystemClasses);
}
完成load操做後就是給當前應用建立一個ClassLoader而且對上面得到的每一個配置對象Configuration都調用一次PreConfigure()方法。每一個配置對象在這個階段作的事情都不太相同,下面是WebInfConfiguration的preConfigure方法,方法中一些代碼的具體做用見下面的註釋:
@Override
public void preConfigure(final WebAppContext context) throws Exception
{
// 查找工做目錄,若是不存在則建立一個
File work = findWorkDirectory(context);
if (work != null)
makeTempDirectory(work, context, false);
//Make a temp directory for the webapp if one is not already set
resolveTempDirectory(context);
//若是有必要解壓war包
unpack (context);
//肯定jar包的載入順序
String tmp = (String)context.getAttribute(WEBINF_JAR_PATTERN);
Pattern webInfPattern = (tmp==null?null:Pattern.compile(tmp));
tmp = (String)context.getAttribute(CONTAINER_JAR_PATTERN);
Pattern containerPattern = (tmp==null?null:Pattern.compile(tmp));
//Apply ordering to container jars - if no pattern is specified, we won't
//match any of the container jars
PatternMatcher containerJarNameMatcher = new PatternMatcher ()
{
public void matched(URI uri) throws Exception
{
context.getMetaData().addContainerJar(Resource.newResource(uri));
}
};
ClassLoader loader = context.getClassLoader();
while (loader != null && (loader instanceof URLClassLoader))
{
URL[] urls = ((URLClassLoader)loader).getURLs();
if (urls != null)
{
URI[] containerUris = new URI[urls.length];
int i=0;
for (URL u : urls)
{
try
{
containerUris[i] = u.toURI();
}
catch (URISyntaxException e)
{
containerUris[i] = new URI(u.toString().replaceAll(" ", "%20"));
}
i++;
}
containerJarNameMatcher.match(containerPattern, containerUris, false);
}
loader = loader.getParent();
}
//Apply ordering to WEB-INF/lib jars
PatternMatcher webInfJarNameMatcher = new PatternMatcher ()
{
@Override
public void matched(URI uri) throws Exception
{
context.getMetaData().addWebInfJar(Resource.newResource(uri));
}
};
List<Resource> jars = findJars(context);
//Convert to uris for matching
URI[] uris = null;
if (jars != null)
{
uris = new URI[jars.size()];
int i=0;
for (Resource r: jars)
{
uris[i++] = r.getURI();
}
}
webInfJarNameMatcher.match(webInfPattern, uris, true); //null is inclusive, no pattern == all jars match
}
總的來講就是對當前的web程序的war包進行處理,進行解壓,放到相應的文件夾下面,並處理WEB-INF下放置的依賴jar包。
preConfigure以後就是configure():
public void configure() throws Exception
{
// Configure webapp
for (int i=0;i<_configurations.length;i++)
{
LOG.debug("configure {} with {}",this,_configurations[i]);
_configurations[i].configure(this);
}
}
能夠看到主要是調用各個配置對象的configure()方法。
下面仍是來看下WebInfConfiguration的configure()方法:
@Override
public void configure(WebAppContext context) throws Exception
{
//cannot configure if the context is already started
if (context.isStarted())
{
if (LOG.isDebugEnabled())
LOG.debug("Cannot configure webapp "+context+" after it is started");
return;
}
Resource web_inf = context.getWebInf();
// Add WEB-INF classes and lib classpaths
if (web_inf != null && web_inf.isDirectory() && context.getClassLoader() instanceof WebAppClassLoader)
{
// Look for classes directory
Resource classes= web_inf.addPath("classes/");
if (classes.exists())
((WebAppClassLoader)context.getClassLoader()).addClassPath(classes);
// Look for jars
Resource lib= web_inf.addPath("lib/");
if (lib.exists() || lib.isDirectory())
((WebAppClassLoader)context.getClassLoader()).addJars(lib);
}
// Look for extra resource
@SuppressWarnings("unchecked")
List<Resource> resources = (List<Resource>)context.getAttribute(RESOURCE_URLS);
if (resources!=null)
{
Resource[] collection=new Resource[resources.size()+1];
int i=0;
collection[i++]=context.getBaseResource();
for (Resource resource : resources)
collection[i++]=resource;
context.setBaseResource(new ResourceCollection(collection));
}
}
這裏的邏輯是很簡單明瞭的,主要就是將WEB-INF目錄下的classe文件和依賴jar包加入到到classpath中去。至於最後的postConfigure,就是作些清除工做,這裏就不展開講了。
至此對於WebAppContext啓動時候所作的工做應該有了大體的瞭解。下面再總結下,其實主要的初始化工做是兩個方面:1是war包的解壓爲其建立工做目錄並將其中WEB-INF下的classes文件和依賴jar包加入到classpath中,還須要爲應用建立一個ClassLoader。2是將管理的SessionHandler、SecurityHandler、ServletHandler和WebAppContext構建成一個Handler鏈,用來處理請求,再將請求交給其它鏈中的其它節點處理以前,還須要對請求進行url和目標主機的校驗,若是校驗不經過則直接返回。這就是WebAppContext大體的工做。