类/资源加载优先顺序:web-inf/classes下优先:
这里的类是.class文件,资源是指配置文件,比如在web.xml文件中配置的spring配置文件:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
<!-- classpath:spring/spring-nacos.xml -->
classpath:spring/spring-dao.xml
classpath:spring/spring-service.xml
classpath:spring/spring-dubbo*.xml
classpath:spring/spring-quartz*.xml
classpath:cis/spring-nacos.xml
</param-value>
</context-param>
tomcat类加载机制打破了jvm规范中的双亲委托机制,tomcat下各应用下的类而是由WebappClassLoader率先加载。
public class WebappClassLoader extends WebappClassLoaderBase {
.....
}
WebappClassLoader类继承自WebappClassLoaderBase,在WebappClassLoaderBase的getResourceAsStream方法中展现了tomcat的加载机制:
@Override
public InputStream getResourceAsStream(String name) {
if (log.isDebugEnabled())
log.debug("getResourceAsStream(" + name + ")");
checkStateForResourceLoading(name);
InputStream stream = null;
//从缓存加载
// (0) Check for a cached copy of this resource
stream = findLoadedResource(name);
if (stream != null) {
if (log.isDebugEnabled())
log.debug(" --> Returning stream from cache");
return (stream);
}
boolean delegateFirst = delegate || filter(name);
//由父加载器加载
// (1) Delegate to parent if requested
if (delegateFirst) {
if (log.isDebugEnabled())
log.debug(" Delegating to parent classloader " + parent);
stream = parent.getResourceAsStream(name);
if (stream != null) {
// FIXME - cache???
if (log.isDebugEnabled())
log.debug(" --> Returning stream from parent");
return (stream);
}
}
//由自己加载
// (2) Search local repositories
if (log.isDebugEnabled())
log.debug(" Searching local repositories");
URL url = findResource(name);
if (url != null) {
// FIXME - cache???
if (log.isDebugEnabled())
log.debug(" --> Returning stream from local");
stream = findLoadedResource(name);
try {
if (hasExternalRepositories && (stream == null))
stream = url.openStream();
} catch (IOException e) {
// Ignore
}
if (stream != null)
return (stream);
}
//由父加载器加载
// (3) Delegate to parent unconditionally
if (!delegateFirst) {
if (log.isDebugEnabled())
log.debug(" Delegating to parent classloader unconditionally " + parent);
stream = parent.getResourceAsStream(name);
if (stream != null) {
// FIXME - cache???
if (log.isDebugEnabled())
log.debug(" --> Returning stream from parent");
return (stream);
}
}
// (4) Resource was not found
if (log.isDebugEnabled())
log.debug(" --> Resource not found, returning null");
return (null);
}
由上边的源码可以看出:tomcat的WebappClassLoader加载器的加载机制:
①从缓存加载
②缓存找不到时,由父加载器加载(这里也不是全部)
③父加载器加载不到时,由自己加载
④自己加载不到时,再由父加载器加载
在第②种,提到"不是全部",这里有个判断:
protected final Matcher packageTriggersDeny = Pattern.compile(
"^javax(\\.|/)el(\\.|/)|" +
"^javax(\\.|/)servlet(\\.|/)|" +
"^javax(\\.|/)websocket(\\.|/)|" +
"^org(\\.|/)apache(\\.|/)(catalina|coyote|el|jasper|juli|naming|tomcat)(\\.|/)"
).matcher("");
/**
* Regular expression of package names which are allowed to be loaded from a
* webapp class loader without delegating first and override any set by
* {@link #packageTriggersDeny}.
*/
protected final Matcher packageTriggersPermit =
Pattern.compile("^javax(\\.|/)servlet(\\.|/)jsp(\\.|/)jstl(\\.|/)|" +
"^org(\\.|/)apache(\\.|/)tomcat(\\.|/)jdbc(\\.|/)").matcher("");
protected synchronized boolean filter(String name) {
if (name == null)
return false;
// Looking up the package
String packageName = null;
int pos = name.lastIndexOf('.');
if (pos != -1)
// Package names in the filters include the last '.'
packageName = name.substring(0, pos + 1);
else
return false;
packageTriggersPermit.reset(packageName);
if (packageTriggersPermit.lookingAt()) {
return false;
}
packageTriggersDeny.reset(packageName);
if (packageTriggersDeny.lookingAt()) {
return true;
}
return false;
}
以上源码说明:javax.servlet.jsp.jstl包和org.apache.tomcat.jdbc包下的类由WebappClassLoader加载,而javax.el包,javax.servlet包,javax.websocket包,org.apache.(catalina|coyote|el|jasper|juli|naming|tomcat)——(这里包太多不在分开写,“|”表示或)包下的类由其父加载器加载。
关于tomcat类加载器的文章:
https://www.cnblogs.com/aspirant/p/8991830.html
https://mp.weixin.qq.com/s/U16h0njbAk6aMC27S_XDZA