探索《How Tomcat Work》 心得(六) 加载器 下 Java中URL、tomcat加载器WebappLoader和WebappClassLoader

来源:互联网 发布:php获取对象的属性值 编辑:程序博客网 时间:2024/06/03 17:43

在上一篇文章中,主要介绍了jvm中类加载器的工作原理和小demo,在这篇文章中,介绍下Tomcat封装后的加载器。

Tomcat封装后的加载器,只能够加载指定库中的文件,这里,还拿第二章的程序中的加载器做个例子。

URLClassLoader loader = null;try{URL[] urls = new URL[1];     URLStreamHandler streamHandler = null;     File classPath = new File(Constants.WEB_ROOT);     String repository = (new URL("file", null,     classPath.getAbsolutePath() + File.separator)).toString();     urls[0] = new URL(null, repository, streamHandler);     loader = new URLClassLoader(urls);} catch (MalformedURLException e){e.printStackTrace();}
介绍下上面用到的URL的构造器, URL(URL context, String spec, URLStreamHandler handler),第一个参数,context,上下文,官方的注释:the context in which to parse the specification,从构造器中的代码的条件(((context != null) && ((newProtocol == null) || newProtocol.equalsIgnoreCase(context.protocol))))可以知道,当传入的上下文中的协议与传入参数中spec中的url协议一致的时候,构造器会使用我们传入的上下文(context)。下面介绍第二个参数,spec,官方的注释:the String  to parse as a URL,它就表示一个URL,通过上面的代码片段,这个参数使用URL的另一个构造器URL(String protocol, String host, String file),这个构造器我之后再说,这个参数所表示的url就是我们的类加载器所加载的类的路径。最后一个参数streamHandler,官方解释:the stream handler for the URL。如果这个参数不为空的话,Check for permission to specify a handler,在构造器中就会检查处理程序的权限。这个处理中的代表类的实际路径的spec参数是非空的,其他两个参数是可以为空的,这个构造器被Tomcat看中的点,就在于那两个参数,可以添加上下文和权限控制。

下面介绍先刚刚提到的构造器URL(String protocol, String host, String file),第一个参数protocol,官方解释:the name of the protocol to use.第二个参数:the name of the host.,第三个参数:the file on the host.当我们使用这个构造器的时候,他会调取该对象内的另一构造器:代码片段如下

public URL(String protocol, String host, String file)            throws MalformedURLException {        this(protocol, host, -1, file);    }
它又搞出来一个新的构造器URL(String protocol, String host, int port, String file),这个构造器比刚才的构造器多了一个端口的参数,当调用上面的构造器的方法的时候,他会使用传入的主机host在默认端口-1,中查找文件file。但是当我看这个构造器的源码的时候,这里并没有结束,它描述路径的方法:String file = (new File(Constants.WEB_ROOT)).getAbsolutePath() + File.separator ; 描述项目下的web_root文件夹的路径

public URL(String protocol, String host, int port, String file)        throws MalformedURLException    {        this(protocol, host, port, file, null);    }

这里的构造器URL(String protocol, String host, int port, String file,URLStreamHandler handler),这是一个最终的构造器。看了这个构造器,我就好奇了,这个东西已经很全了,这个方法不能给构造器添加上下文,其余的添加权限控制类似的东西,这里都可以搞定。通过最开始的demo可以看出,它使用两个url构造器构造一个url实例,为了后面的内容做铺垫呢,为url添加权限,和上下文。

在Tomcat中,加载器都需要实现Loader接口,回顾一下tomcat加载器的UML结构图:


从结构图中,可以看出,在Tomcat的加载器中生活在最底层的是WebappClassLoader,下面,介绍下WebappClassLoader对象与WebappLoader对象,WebappLoader可以控制WebAppClassLoader的生死。从上面的结构图中可以知道,它还继承了URLClassLoader类,从WebappLoader的start()方法的开始,Start this component, initializing our associated class loader.在开始的时候,日志组件和Lifecycle接口中做的事情就不累述了,我在后面的会把前面讲到的几个组件联合起来讲一遍,这里只说loader的事情了,看下面代码块:

1、为JNDI协议注册一个流管理工厂

  if (container.getResources() == null)            return; // Register a stream handler factory for the JNDI protocol URLStreamHandlerFactory streamHandlerFactory = new DirContextURLStreamHandlerFactory(); try {  URL.setURLStreamHandlerFactory(streamHandlerFactory); } catch (Throwable t) {  // Ignore the error here. }
2、创建classloader加载器,运用java中的反射创建classloader加载器,其中在下面代码块中的变量 loaderClass,是定义好了的常量,Tomcat默认加载器的类名:“org.apache.catalina.loader.WebappClassLoader”

        Class clazz = Class.forName(loaderClass);        WebappClassLoader classLoader = null;        if (parentClassLoader == null) {            // Will cause a ClassCast is the class does not extend WCL, but            // this is on purpose (the exception will be caught and rethrown)            classLoader = (WebappClassLoader) clazz.newInstance();        } else {            Class[] argTypes = { ClassLoader.class };            Object[] args = { parentClassLoader };            Constructor constr = clazz.getConstructor(argTypes);            classLoader = (WebappClassLoader) constr.newInstance(args);        }
3、装载有特色的classLoader,下面的各种设置中最主要的就是设置类库列表 addRepository,为classloader指定库,不仅如此,在设置完库之后,他又设置权限相关内容,使classLoader加载各种url中,按照与url匹配的协议加载,这样设置之后,对于首先的classLoader,用起来是安全的。

            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();

4、组件一旦启动起来,下面这个方法可能会经常调用,重载。WebappLoader 支持自动重载,如果 WEB-INF/classes 或者 WEB-INF/lib 目录被重新编译过,在不重启 Tomcat 的情况下必须自动重新载入这些类。为了实现这个目的,WebappLoader 有一个单独的线程每个 x 秒会检查源的时间戳。x 的值由checkInterval 变量定义,它的默认值是 15,也就是每隔 15 秒会进行一次检查是否需要自动重载。

if (reloadable) {  log(sm.getString("webappLoader.reloading"));  try {     threadStart();  } catch (IllegalStateException e) {     throw new LifecycleException(e);  }}
5、重载,下面就是重载线程所做的事情:检查classloader是否发生改变,如果发生改变了,这里会通知上下文,上下文会唤醒一个线程 WebappContextNotifier,这个线程运行,就是让容器重载: ((Context) container).reload();

            try {                // Perform our modification check                if (!classLoader.modified())                    continue;            } catch (Exception e) {                log(sm.getString("webappLoader.failModifiedCheck"), e);                continue;            }            // Handle a need for reloading            notifyContext();
6、缓存。

每一个可以被加载的类(放在 WEB-INF/classes 目录下的类文件或者 JAR 文件)都被当做一个源。一个源被 org.apache.catalina.loader.ResourceEntry 类表示。一个 ResourceEntry 实例保存一个 byte 类型的数组表示该类、最后修改的数据或者副本等等。所有缓存的源被存放在一个叫做 resourceEntries 的 HashMap 中(在WabappClassLoader中的元素),键值为源名,所有找不到的源都被放在一个名为 notFoundResources 的 HashMap 中。要注意,Tomcat没加载一个类,都会把它放到缓存中,加载器加载类的时候,也是首先在缓存中查找,找不到才会到库中去查找。


1 0
原创粉丝点击