WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见;
commonLoader、catalinaLoader和sharedLoader在Tomcat容器初始化的一开始,即调用Bootstrap的init方法时创建。catalinaLoader会被设置为Tomcat主线程的线程上下文类加载器,并且使用catalinaLoader加载Tomcat容器自身容器下的class。Bootstrap的init方法的部分代码见代码清单1。
代码清单1 Bootstrap的init方法的部分实现
/*** Initialize daemon.*/public void init()throws Exception{// Set Catalina pathsetCatalinaHome();setCatalinaBase();initClassLoaders();Thread.currentThread().setContextClassLoader(catalinaLoader);SecurityClassLoad.securityClassLoad(catalinaLoader);// 省略后边的代码
代码清单1中,我们首先关注initClassLoaders方法的实现,见代码清单2.initClassLoaders方法用来初始化commonLoader、catalinaLoader、sharedLoader。
代码清单2 initClassLoaders方法的实现
private void initClassLoaders() {try {commonLoader = createClassLoader("common", null);if( commonLoader == null ) {// no config file, default to this loader - we might be in a 'single' envmonLoaderClass().getClassLoader();}catalinaLoader = createClassLoader("server", commonLoader);sharedLoader = createClassLoader("shared", commonLoader);} catch (Throwable t) {("Class loader creation threw exception", t);it(1);}}
从代码清单2中看到创建类加载器是通过调用createClassLoader方法实现的,createClassLoader的实现见代码清单3.
代码清单3 createClassLoader方法的实现
private ClassLoader createClassLoader(String name, ClassLoader parent)throws Exception {String value = Property(name + ".loader");if ((value == null) || (value.equals("")))return parent;ArrayList<String> repositoryLocations = new ArrayList<String>();ArrayList<Integer> repositoryTypes = new ArrayList<Integer>();int i;StringTokenizer tokenizer = new StringTokenizer(value, ",");while (tokenizer.hasMoreElements()) {String repository = Token();// Local repositoryboolean replace = false;String before = repository;while ((i=repository.indexOf(CATALINA_HOME_TOKEN))>=0) {replace=true;if (i>0) {repository = repository.substring(0,i) + getCatalinaHome() + repository.substring(i+CATALINA_HOME_TOKEN.length());} else {repository = getCatalinaHome() + repository.substring(CATALINA_HOME_TOKEN.length());}}while ((i=repository.indexOf(CATALINA_BASE_TOKEN))>=0) {replace=true;if (i>0) {repository = repository.substring(0,i) + getCatalinaBase() + repository.substring(i+CATALINA_BASE_TOKEN.length());} else {repository = getCatalinaBase() + repository.substring(CATALINA_BASE_TOKEN.length());}}if (replace && log.isDebugEnabled())log.debug("Expanded " + before + " to " + repository);// Check for a JAR URL repositorytry {new URL(repository);repositoryLocations.add(repository);repositoryTypes.add(ClassLoaderFactory.IS_URL);continue;} catch (MalformedURLException e) {// Ignore}if (dsWith("*.jar")) {repository = repository.substring(0, repository.length() - "*.jar".length());repositoryLocations.add(repository);repositoryTypes.add(ClassLoaderFactory.IS_GLOB);} else if (dsWith(".jar")) {repositoryLocations.add(repository);repositoryTypes.add(ClassLoaderFactory.IS_JAR);} else {repositoryLocations.add(repository);repositoryTypes.add(ClassLoaderFactory.IS_DIR);}}String[] locations = Array(new String[0]);Integer[] types = Array(new Integer[0]);ClassLoader classLoader = ateClassLoader(locations, types, parent);// Retrieving MBean serverMBeanServer mBeanServer = null;if (MBeanServerFactory.findMBeanServer(null).size() > 0) {mBeanServer = MBeanServerFactory.findMBeanServer(null).get(0);} else {mBeanServer = PlatformMBeanServer();}// Register the server classloaderObjectName objectName =new ObjectName("Catalina:type=ServerClassLoader,name=" + name);isterMBean(classLoader, objectName);return classLoader;}
createClassLoader方法的执行步骤如下:
我们回头看看代码清单1中的SecurityClassLoad.securityClassLoad(catalinaLoader)的实现,见代码清单4.这说明加载Tomcat容器本身的类资源的确是使用catalinaLoader来完成的。
代码清单4 securityClassLoad的实现
public static void securityClassLoad(ClassLoader loader)throws Exception {if( SecurityManager() == null ){return;}loadCorePackage(loader);loadLoaderPackage(loader);loadSessionPackage(loader);loadUtilPackage(loader);loadJavaxPackage(loader);loadCoyotePackage(loader); loadTomcatPackage(loader);}
securityClassLoad方法主要加载Tomcat容器所需的class,包括:
我们以加载Tomcat核心class的loadCorePackage方法为例,其实现见代码清单5所示。
代码清单5 loadCorePackage的实现
private final static void loadCorePackage(ClassLoader loader)throws Exception {String basePackage = "org.apache.catalina.";loader.loadClass(basePackage +"core.ApplicationContextFacade$1");loader.loadClass(basePackage +"core.ApplicationDispatcher$PrivilegedForward");loader.loadClass(basePackage +"core.ApplicationDispatcher$PrivilegedInclude");loader.loadClass(basePackage +"core.AsyncContextImpl");loader.loadClass(basePackage +"core.AsyncContextImpl$AsyncState");loader.loadClass(basePackage +"core.AsyncContextImpl$DebugException");loader.loadClass(basePackage +"core.AsyncContextImpl$1");loader.loadClass(basePackage +"core.AsyncContextImpl$2");loader.loadClass(basePackage +"core.AsyncListenerWrapper");loader.loadClass(basePackage +"core.ContainerBase$PrivilegedAddChild");loader.loadClass(basePackage +"core.DefaultInstanceManager$1");loader.loadClass(basePackage +"core.DefaultInstanceManager$2");loader.loadClass(basePackage +"core.DefaultInstanceManager$3");loader.loadClass(basePackage +"core.DefaultInstanceManager$4");loader.loadClass(basePackage +"core.DefaultInstanceManager$5");loader.loadClass(basePackage +"core.ApplicationHttpRequest$AttributeNamesEnumerator");}
至此,有关commonLoader、catalinaLoader和sharedLoader三个类加载器的初始化以及使用catalinaLoader加载Tomcat容器自身类资源的内容已经介绍完了,但是我们还没有看到WebappClassLoader。启动StandardContext的时候会创建WebappLoader,根据《Tomcat7.0源码分析——生命周期管理 》一文的内容,我们知道启动StandardContext时会最终调用其startInternal方法,其实现见代码清单6.
代码清单6 StandardContext的startInternal方法
/*** Start this component and implement the requirements* of {@link LifecycleBase#startInternal()}.** @exception LifecycleException if this component detects a fatal error* that prevents this component from being used*/@Overrideprotected synchronized void startInternal() throws LifecycleException {// 省略前边的代码 if (getLoader() == null) {WebappLoader webappLoader = new WebappLoader(getParentClassLoader());webappLoader.setDelegate(getDelegate());setLoader(webappLoader);}// 省略中间的代码 // Start our subordinate components, if anyif ((loader != null) && (loader instanceof Lifecycle))((Lifecycle) loader).start(); // 省略后边的代码 }
从代码清单6看到首先创建WebappLoader实例,然后调用WebappLoader的start方法,start又调用了startInternal方法,WebappLoader的startInternal的实现见代码清单7.
代码清单7 WebappLoader的startInternal实现
/*** Start associated {@link ClassLoader} and implement the requirements* of {@link LifecycleBase#startInternal()}.** @exception LifecycleException if this component detects a fatal error* that prevents this component from being used*/@Overrideprotected void startInternal() throws LifecycleException {// Register a stream handler factory for the JNDI protocolURLStreamHandlerFactory streamHandlerFactory =new DirContextURLStreamHandlerFactory();if (first) {first = false;try {URL.setURLStreamHandlerFactory(streamHandlerFactory);} catch (Exception e) {// Log and continue anyway, this is ("Error registering jndi stream handler", e);} catch (Throwable t) {// This is likely a dual registrationlog.info("Dual registration of jndi stream handler: " + t.getMessage());}}// Construct a class loader based on our current repositories listtry {classLoader = createClassLoader();classLoader.Resources());classLoader.setDelegate(this.delegate);classLoader.setSearchExternalFirst(searchExternalFirst);if (container instanceof StandardContext) {classLoader.setAntiJARLocking(((StandardContext) container).getAntiJARLocking());classLoader.setClearReferencesStatic(((StandardContext) container).getClearReferencesStatic());classLoader.setClearReferencesStopThreads(((StandardContext) container).getClearReferencesStopThreads());classLoader.setClearReferencesStopTimerThreads(((StandardContext) container).getClearReferencesStopTimerThreads());classLoader.setClearReferencesThreadLocals(((StandardContext) container).getClearReferencesThreadLocals());}for (int i = 0; i < repositories.length; i++) {classLoader.addRepository(repositories[i]);}
我们看到代码清单7中通过调用createClassLoader来创建类加载器,并且设置其资源路径为当前Webapp下某个context的类资源。最后我们看看createClassLoader的实现,见代码清单8.
代码清单8 createClassLoader的实现
/*** Create associated classLoader.*/private WebappClassLoader createClassLoader()throws Exception {//loaderClass即字符串org.apache.catalina.loader.WebappClassLoaderClass<?> clazz = Class.forName(loaderClass);WebappClassLoader classLoader = null;if (parentClassLoader == null) {parentClassLoader = ParentClassLoader();}Class<?>[] argTypes = { ClassLoader.class };Object[] args = { parentClassLoader };Constructor<?> constr = Constructor(argTypes);classLoader = (WebappClassLoader) wInstance(args);return classLoader;}
这里loaderClass的值是字符串org.apache.catalina.loader.WebappClassLoader,通过反射来实例化WebappClassLoader。由于每个Webapp下的类资源由不同的WebappClassLoader负责加载,因此Webapp下各个Context的类资源是独立的。至此,整个Tomcat的类加载体系构建完毕。
此外每个jsp为了实现热替换,会有专门的类加载器负责加载。
后记:个人总结整理的《深入理解Spark:核心思想与源码分析》一书现在已经正式出版上市,目前京东、当当、天猫等网站均有销售,欢迎感兴趣的同学购买。
京东:.html
当当:.html
本文发布于:2024-02-01 02:40:58,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170672646033286.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |