loader是根据请求加载对应的类。jvm有自带的loader,而tomcat中没有直接用java自带loader,是为了安全和重载。
安全:jvm自带loader载入的servlet和类,能访问当前jvm的classpath环境变量下所有的类,这样不安全。一般只允许加载的类访问/WEB-INF/classes及其子目录和/WEB-INF/lib中的类。catalina中loader实现Loader接口。 classpath变量:告诉执行环境,在哪里可以找到执行的java程序需要的类或包。
重载:tomcat中记录class的时间戳,若class发生变化,则重新载入。要实现Reloader接口。 java本身的loader
jvm使用三种loader,引导类载入器(bootstrap class loader)、扩展类载入器(extension class loader)和系统类载入器(system class loader)。这三种加载器是继承关系,extension继承自bootstrap,system loader继承自extension。
引导类载入器用于引导jvm。当执行java命令时,引导类载入器开始工作,它负责载入启动jvm的类,以及载入java核心类。
扩展类载入器负责载入标准扩展目录下的类。程序员将jar包放到扩展目录,扩展类载入器会从中查找需要的类。
系统类载入器是系统默认载入器,从classpath下查找需要的类。
jvm如何使用这三个加载器呢?
jvm使用了代理模式。每次需要使用一个新类时,首先使用system loader,但system loader将任务交给extension loader,extension又将任务交给bootstrap。如果bootstrap无法载入这个类,就由extension来尝试载入;若extension无法载入,就由system loader来载入;若还是失败,就抛出ClassNotFoundException。
为什么要这样呢?
主要原因是安全性。如果有人也写了一个java.lang.Object的类,想做一些特殊操作。那么加载的过程中,bootstrap找到了标准java.lang.Object类,就不会再调用自定义的Object类啦。 * Tomcat中的loader *
public final class Bootstrap { public static void main(String[] args) { System.setProperty("catalina.base", System.getProperty("user.dir")); Connector connector = new HttpConnector(); Wrapper wrapper1 = new SimpleWrapper(); wrapper1.setName("Primitive"); wrapper1.setServletClass("PrimitiveServlet"); Wrapper wrapper2 = new SimpleWrapper(); wrapper2.setName("Modern"); wrapper2.setServletClass("ModernServlet"); Context context = new StandardContext(); // StandardContext's start method adds a default mapper context.setPath("/myApp"); context.setDocBase("myApp"); context.addChild(wrapper1); context.addChild(wrapper2); // context.addServletMapping(pattern, name); context.addServletMapping("/Primitive", "Primitive"); context.addServletMapping("/Modern", "Modern"); // add ContextConfig. This listener is important because it configures // StandardContext (sets configured to true), otherwise StandardContext // won't start LifecycleListener listener = new SimpleContextConfig(); ((Lifecycle) context).addLifecycleListener(listener); // here is our loader Loader loader = new WebappLoader(); // associate the loader with the Context context.setLoader(loader); connector.setContainer(context); try { connector.initialize(); ((Lifecycle) connector).start(); ((Lifecycle) context).start(); // make the application wait until we press a key. System.in.read(); ((Lifecycle) context).stop(); } catch (Exception e) { e.printStackTrace(); } }tomcat主要包括两个模块,connector和container,前者主要负责监听请求、创建processor,后者主要负责处理请求。 StandardContext是container的一个实现。其中包括一个loader,类型为WebappLoader,其定义主要内容如下。
public class WebappLoader implements Lifecycle, Loader, PropertyChangeListener, Runnable { /** * Construct a new WebappLoader with the specified class loader * to be defined as the parent of the ClassLoader we ultimately create. * * @param parent The parent class loader */ public WebappLoader(ClassLoader parent) { super(); this.parentClassLoader = parent; } /** * The number of seconds between checks for modified classes, if * automatic reloading is enabled. 每隔15s or 15ms 检查类是否有变更 */ private int checkInterval = 15; /** * The class loader being managed by this Loader component. 类加载器 */ private WebappClassLoader classLoader = null; /** * The Container with which this Loader has been associated. loader关联的container */ private Container container = null; /** * The debugging detail level for this component. 是啥我也不知道 */ private int debug = 0; /** * The DefaultContext with which this Loader is associated. */ protected DefaultContext defaultContext = null; /** * The "follow standard delegation model" flag that will be used to * configure our ClassLoader. */ private boolean delegate = false; /** * The lifecycle event support for this component. */ protected LifecycleSupport lifecycle = new LifecycleSupport(this); /** * The Java class name of the ClassLoader implementation to be used. * This class should extend WebappClassLoader, otherwise, a different * loader implementation must be used. 使用的类加载器的类名 */ private String loaderClass = "org.apache.catalina.loader.WebappClassLoader"; /** * The parent class loader of the class loader we will create. */ private ClassLoader parentClassLoader = null; /** * The reloadable flag for this Loader. */ private boolean reloadable = false; /** * The set of repositories associated with this class loader. */ private String repositories[] = new String[0]; /** * The string manager for this package. */ protected static final StringManager sm = StringManager.getManager(Constants.Package); /** * Has this component been started? */ private boolean started = false; /** * The property change support for this component. */ protected PropertyChangeSupport support = new PropertyChangeSupport(this); /** * The background thread. */ private Thread thread = null; /** * The background thread completion semaphore. */ private boolean threadDone = false; /** * Name to register for the background thread. */ private String threadName = "WebappLoader"; }WebappLoader中有个WebappClassLoader classLoader成员,作为类载入器。WebappLoader实现了Lifecycle接口,由container控制其启动/关闭。
1.创建container,设置其loader
Loader loader = new WebappLoader(); // associate the loader with the Context context.setLoader(loader);2.container.start()启动container,包括调用loader.start()、pipeline.start()等。start()主要逻辑如下:
//启动loader和logger if ((loader != null) && (loader instanceof Lifecycle)) ((Lifecycle) loader).start(); if ((logger != null) && (logger instanceof Lifecycle)) ((Lifecycle) logger).start(); // Unbinding thread unbindThread(oldCCL); // Binding thread oldCCL = bindThread(); if ((cluster != null) && (cluster instanceof Lifecycle)) ((Lifecycle) cluster).start(); if ((realm != null) && (realm instanceof Lifecycle)) ((Lifecycle) realm).start(); if ((resources != null) && (resources instanceof Lifecycle)) ((Lifecycle) resources).start(); // Start our Mappers, if any Mapper mappers[] = findMappers(); for (int i = 0; i < mappers.length; i++) { if (mappers[i] instanceof Lifecycle) ((Lifecycle) mappers[i]).start(); } // Start our child containers, if any Container children[] = findChildren(); for (int i = 0; i < children.length; i++) { if (children[i] instanceof Lifecycle) ((Lifecycle) children[i]).start(); } // Start the Valves in our pipeline (including the basic), // if any if (pipeline instanceof Lifecycle) ((Lifecycle) pipeline).start(); // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(START_EVENT, null); if ((manager != null) && (manager instanceof Lifecycle)) ((Lifecycle) manager).start();3.loader.start()逻辑如下,包括创建classloader、设置resource、repository、调用classloader.start()、为自动重载打开一个新线程(run方法判断是否需要重载)等操作。
//创建classloader classLoader = createClassLoader(); classLoader.setResources(container.getResources()); classLoader.setDebug(this.debug); classLoader.setDelegate(this.delegate); for (int i = 0; i < repositories.length; i++) { classLoader.addRepository(repositories[i]); } // Configure our repositories setRepositories(); setClassPath(); //设置访问权限 setPermissions(); if (classLoader instanceof Lifecycle) ((Lifecycle) classLoader).start(); // Binding the Webapp class loader to the directory context DirContextURLStreamHandler.bind ((ClassLoader) classLoader, this.container.getResources());WebappLoader.run()主要逻辑是,每隔15s判断类是否有变化(检查时间戳),若有变化则由其内部类WebappContextNotifier调用container.reload(),通知container做重载。
WebappLoader的功能:
管理repository、classpath、权限等控制类载入器检测reloadreload的实现:
WebappClassLoader.modified()检测类或资源是否有变化,具体方法是记录各个资源的最后更新时间来判断是否有变化,还要判断资源是否被移除或新增加。loader调用classloader判断有变更后,通知container做重载。container.reload()包括listener、loader等的stop+start。为什么由container来控制reload?
container是容器,控制loader、listener等的start/stop等。在reload的过程中,需要相关的所有模块都变更,因此需要container来控制,而不是由loader模块来控制。