Java类加载器如何为“常规”环境工作(非明确使用类加载器)
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了Java类加载器如何为“常规”环境工作(非明确使用类加载器),小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含6710字,纯文字阅读大概需要10分钟。
内容图文
![Java类加载器如何为“常规”环境工作(非明确使用类加载器)](/upload/InfoBanner/zyjiaocheng/823/d423168aaa8d4cabb755962be0031836.jpg)
我正在研究类路径的动态修改.我找到了one solution that works nicely,但它使用addURL()的显式调用. (大概是在启动时)
但是,如果默认的类加载器似乎无法找到它,我想在运行时拦截类加载过程以找到类.我尝试将子类化为ClassLoader,因此它只是将findClass()和loadClass()委托给默认值,并打印出一个调试行告诉我这些方法已被调用,但是当我的类通过隐式类加载使用依赖类时它们似乎永远不会被调用,例如
// regular object instantiation with 'new'
BrowserLauncher launcher;
launcher = new BrowserLauncher();
// static methods
Foobar.doSomethingOrOther();
// Class.forName()
Class cl = Class.forName("foo.bar.baz");
// reflection on a Class object obtained statically
Class<Foobar> cl = Foobar.class;
// do something with cl, like call static methods or newInstance()
在这些情况下,类加载如何工作? (相对于显式调用Classloader.loadClass()的简单情况)
这是我在下面的自定义类加载器的尝试.如果我使用带有{“some.package.SomeClass”,“foo”,“bar”,“baz”}和some.package.SomeClass的参数列表的DynClassLoader0.main()引用外部.jar文件中找到的其他类,使用上面列出的方法之一,为什么不调用我的DynClassLoader0的findClass()和loadClass()? loadClass被调用的唯一时间是在下面的main()函数中对loadClass的显式调用.
package com.example.test.classloader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class DynClassLoader0 extends ClassLoader {
public DynClassLoader0()
{
super();
}
public DynClassLoader0(ClassLoader parent)
{
super(parent);
}
public void runMain(String classname, String[] args) throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException
{
// [***] here we explicitly use our classloader.
Class<?> cl = loadClass(classname);
Method main = cl.getMethod("main", String[].class);
main.invoke(null, new Object[] {args});
}
@Override protected Class<?> findClass(String name) throws ClassNotFoundException
{
System.out.println("findClass("+name+")");
return super.findClass(name);
}
@Override public Class<?> loadClass(String name) throws ClassNotFoundException
{
System.out.println("loadClass("+name+")");
return super.loadClass(name);
}
static public void main(String[] args)
{
// classname, then args
if (args.length >= 1)
{
String[] classArgs = new String[args.length-1];
System.arraycopy(args, 1, classArgs, 0, args.length-1);
ClassLoader currentThreadClassLoader
= Thread.currentThread().getContextClassLoader();
DynClassLoader0 classLoader = new DynClassLoader0(currentThreadClassLoader);
// Replace the thread classloader - assumes
// you have permissions to do so
Thread.currentThread().setContextClassLoader(classLoader);
try {
classLoader.runMain(args[0], classArgs);
}
catch (Exception e) {
e.printStackTrace();
}
}
else
{
System.out.println("usage: DynClassLoader {classname} [arg0] [arg1] ...");
}
}
}
编辑:我已经查看了这些问题:
> How do you change the CLASSPATH within Java?
> Is it possible to “add” to classpath dynamically in java?
> Adding files to java classpath at runtime.
编辑:我认为kdgregory下面说的是正确的,一旦我明确地使用我的类加载器(参见代码中的行[***]作为注释),从该类执行的所有代码将导致从同一个类的隐式类加载类加载器.然而,除了在最外层的显式调用期间,我的DynClassLoader0.loadClass()永远不会被调用.
解决方法:
The methods and constructors of
objects created by a class loader may
reference other classes. To determine
the class(es) referred to, the Java
virtual machine invokes the loadClass
method of the class loader that
originally created the class.
换句话说,一旦加载了一个类,该类就会尝试通过加载它的类加载器加载其他类.在普通的Java应用程序中,即系统类加载器,它表示传递给JVM的类路径,或用于加载JVM运行时的引导类加载器.
根据您的需要,有一个Class.forName()的变体,它将一个类加载器作为参数.如果使用它来加载特定类,则该类中的引用应使用指定的类加载器.
编辑:我开始追踪你的例子,但决定让自己更容易.如果您要编写自己的类加载器,我建议从现有的URLClassLoader开始,因为它处理了很多幕后的东西.
因此,MyClassLoader采用单个JARfile /目录并仅为该目录加载类.我已经重写了调用加载类的三个方法,并简单地记录它们的调用(使用System.err因为它不缓冲输出,与System.out不同).
我的例子使用了我正在处理的库;它很方便,但你可以选择你想要的任何库,只要它不在你的类路径中.
main()方法通过MyLoader加载一个类.然后我在该类上调用一个方法,我知道这个方法会抛出一个也是库的一部分的异常.请注意,我通过反射调用该方法:由于该库不在我的Eclipse类路径中,因此我无法使用显式引用对其进行编译.
当我运行这个程序时(在Sun JDK 1.5 for Linux下),我看到很多对loadClass()的调用,包括我的库中的类和类路径上的类.这是预期的:ParseUtil类引用了许多其他类,并将使用MyLoader(即其类加载器)来加载它们.对于MyLoader无法在本地找到的那些类,它会委托加载器树.
抛出异常,当我打印出它的类加载器时,我发现它与我创建的MyLoader实例相同.我还打印出Exception.class的加载器,它是null – Class.getClassLoader()的JavaDoc表示启动类加载器.
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
public class ClassLoaderExample
{
private static class MyClassLoader
extends URLClassLoader
{
public MyClassLoader(String path)
throws Exception
{
super(new URL[] { new File(path).toURL() });
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException
{
System.err.println("findClass(" + name + ")");
return super.findClass(name);
}
@Override
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
System.err.println("loadClass(" + name + "," + resolve + ")");
return super.loadClass(name, resolve);
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException
{
System.err.println("loadClass(" + name + ")");
return super.loadClass(name);
}
}
public static void main(String[] argv)
throws Exception
{
ClassLoader myLoader = new MyClassLoader("/home/kgregory/Workspace/PracticalXml-1.1/target/classes/");
System.out.println("myLoader = " + myLoader);
Class<?> parseUtilKlass = myLoader.loadClass("net.sf.practicalxml.ParseUtil");
Method parseMethod = parseUtilKlass.getDeclaredMethod("parse", String.class);
try
{
parseMethod.invoke(null, "not at all valid XML");
}
catch (InvocationTargetException e)
{
Throwable ee = e.getCause();
System.out.println("exception:" + ee);
System.out.println("exception loader = " + ee.getClass().getClassLoader());
System.out.println("Exception.class loader = " + Exception.class.getClassLoader());
}
}
}
编辑#2,根据今天的评论.
在尝试自己完成请求之前,类加载器应该将请求委托给其父级(这在ClassLoader JavaDoc中).这种做法有几个好处,最重要的是你不会无意中加载同一类的不兼容实例.
J2EE类加载器修改了这个模型:用于加载WAR的类加载器将尝试在包含EAR的加载器之前解析类,而后者又尝试在容器的类加载器之前解析类.这里的目标是隔离:如果WAR和它的EAR都包含相同的库,那可能是因为这两个版本需要不同的版本(或者它们有一个草率的构建过程).即使在J2EE的情况下,我相信容器类加载器以标准方式委托.
内容总结
以上是互联网集市为您收集整理的Java类加载器如何为“常规”环境工作(非明确使用类加载器)全部内容,希望文章能够帮你解决Java类加载器如何为“常规”环境工作(非明确使用类加载器)所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。