Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/jsf-2/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Java中创建父last/child first类加载器,或者如何覆盖已加载到父CL中的旧Xerces版本?_Java_Classloader - Fatal编程技术网

如何在Java中创建父last/child first类加载器,或者如何覆盖已加载到父CL中的旧Xerces版本?

如何在Java中创建父last/child first类加载器,或者如何覆盖已加载到父CL中的旧Xerces版本?,java,classloader,Java,Classloader,我想创建一个父类最后/子类第一类加载器,例如,一个类加载器将首先在子类loder中查找类,然后才委托给它的父类加载器来搜索类 澄清: 我现在知道,要获得完整的类加载分离,我需要使用类似URLClassLoader的东西,将null作为其父类传递,这要感谢我前面的问题 但是,当前的问题有助于我解决这个问题: 我的代码+依赖JAR正在加载到现有系统中,使用一个类加载器将该系统的类加载器设置为其父类(URLClassLoader) 该系统使用的某些库的版本与我需要的版本不兼容(例如旧版本的Xerces

我想创建一个父类最后/子类第一类加载器,例如,一个类加载器将首先在子类loder中查找类,然后才委托给它的父类加载器来搜索类

澄清:

我现在知道,要获得完整的类加载分离,我需要使用类似URLClassLoader的东西,将null作为其父类传递,这要感谢我前面的问题

但是,当前的问题有助于我解决这个问题:

  • 我的代码+依赖JAR正在加载到现有系统中,使用一个类加载器将该系统的类加载器设置为其父类(URLClassLoader)

  • 该系统使用的某些库的版本与我需要的版本不兼容(例如旧版本的Xerces,不允许我运行代码)

  • 如果单独运行,我的代码运行得非常好,但如果从类加载器运行,代码将失败

  • 但是,我确实需要访问父类加载器中的许多其他类

  • 因此,我想允许我用我自己的类加载器覆盖父类加载器“JAR”:如果在子类加载器中找到我调用的类(例如,我用我自己的JAR提供了Xerces的更新版本,而不是加载我的代码和JAR的类加载器提供的用户版本)

  • 这是加载我的代码+JAR的系统代码(我无法更改此代码)

    以下是“我的”代码(完全取自飞索“Hello World”代码演示):

    这可以独立工作(运行main),但在通过父CL加载时失败,并出现此错误:

    org.w3c.dom.DOMException:NAMESPACE\u ERR:试图 以某种方式创建或更改对象 这是不正确的关于 名称空间

    可能是因为父系统使用的是旧版本的Xerces,即使我在/addOns文件夹中提供了正确的Xerces jar,因为父系统已经加载并使用了它的类,它不允许我自己的代码使用我自己的jar,因为委托的方向。我希望这能让我的问题更清楚,我确信有人问过
    之前。(也许我问的问题不对)

    通过阅读Jetty或Tomcat的源代码,这两个工具都提供了实现webapp语义的父类最后类装入器

    也就是说,通过覆盖
    ClassLoader
    类中的
    findClass
    方法。但是,既然可以偷,为什么还要重新发明轮子呢

    阅读您的各种更新,我发现您遇到了一些XML SPI系统的经典问题

    一般的问题是:如果创建一个完全隔离的类加载器,则很难使用它返回的对象。如果允许共享,则当父类包含错误版本的内容时,可能会出现问题

    OSGi的发明是为了应对所有这些疯狂行为,但这是一个需要吞下的大药丸


    即使在webapps中,类加载器也会免除某些包的“本地优先”处理,前提是容器和webapp必须就它们之间的API达成一致。

    今天是你的幸运日,因为我必须解决这个确切的问题。不过,我警告你,类加载的内部是一个可怕的地方。这样做让我觉得Java的设计者从来没有想到,您可能想要一个父类装入器

    /**
     * A parent-last classloader that will try the child classloader first and then the parent.
     * This takes a fair bit of doing because java really prefers parent-first.
     * 
     * For those not familiar with class loading trickery, be wary
     */
    private static class ParentLastURLClassLoader extends ClassLoader 
    {
        private ChildURLClassLoader childClassLoader;
    
        /**
         * This class allows me to call findClass on a classloader
         */
        private static class FindClassClassLoader extends ClassLoader
        {
            public FindClassClassLoader(ClassLoader parent)
            {
                super(parent);
            }
    
            @Override
            public Class<?> findClass(String name) throws ClassNotFoundException
            {
                return super.findClass(name);
            }
        }
    
        /**
         * This class delegates (child then parent) for the findClass method for a URLClassLoader.
         * We need this because findClass is protected in URLClassLoader
         */
        private static class ChildURLClassLoader extends URLClassLoader
        {
            private FindClassClassLoader realParent;
    
            public ChildURLClassLoader( URL[] urls, FindClassClassLoader realParent )
            {
                super(urls, null);
    
                this.realParent = realParent;
            }
    
            @Override
            public Class<?> findClass(String name) throws ClassNotFoundException
            {
                try
                {
                    // first try to use the URLClassLoader findClass
                    return super.findClass(name);
                }
                catch( ClassNotFoundException e )
                {
                    // if that fails, we ask our real parent classloader to load the class (we give up)
                    return realParent.loadClass(name);
                }
            }
        }
    
        public ParentLastURLClassLoader(List<URL> classpath)
        {
            super(Thread.currentThread().getContextClassLoader());
    
            URL[] urls = classpath.toArray(new URL[classpath.size()]);
    
            childClassLoader = new ChildURLClassLoader( urls, new FindClassClassLoader(this.getParent()) );
        }
    
        @Override
        protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
        {
            try
            {
                // first we try to find a class inside the child classloader
                return childClassLoader.findClass(name);
            }
            catch( ClassNotFoundException e )
            {
                // didn't find it, try the parent
                return super.loadClass(name, resolve);
            }
        }
    }
    
    要使用,只需提供一个URL列表,其中包含子类加载器中可用的类或JAR

    /**
     * A parent-last classloader that will try the child classloader first and then the parent.
     * This takes a fair bit of doing because java really prefers parent-first.
     * 
     * For those not familiar with class loading trickery, be wary
     */
    private static class ParentLastURLClassLoader extends ClassLoader 
    {
        private ChildURLClassLoader childClassLoader;
    
        /**
         * This class allows me to call findClass on a classloader
         */
        private static class FindClassClassLoader extends ClassLoader
        {
            public FindClassClassLoader(ClassLoader parent)
            {
                super(parent);
            }
    
            @Override
            public Class<?> findClass(String name) throws ClassNotFoundException
            {
                return super.findClass(name);
            }
        }
    
        /**
         * This class delegates (child then parent) for the findClass method for a URLClassLoader.
         * We need this because findClass is protected in URLClassLoader
         */
        private static class ChildURLClassLoader extends URLClassLoader
        {
            private FindClassClassLoader realParent;
    
            public ChildURLClassLoader( URL[] urls, FindClassClassLoader realParent )
            {
                super(urls, null);
    
                this.realParent = realParent;
            }
    
            @Override
            public Class<?> findClass(String name) throws ClassNotFoundException
            {
                try
                {
                    // first try to use the URLClassLoader findClass
                    return super.findClass(name);
                }
                catch( ClassNotFoundException e )
                {
                    // if that fails, we ask our real parent classloader to load the class (we give up)
                    return realParent.loadClass(name);
                }
            }
        }
    
        public ParentLastURLClassLoader(List<URL> classpath)
        {
            super(Thread.currentThread().getContextClassLoader());
    
            URL[] urls = classpath.toArray(new URL[classpath.size()]);
    
            childClassLoader = new ChildURLClassLoader( urls, new FindClassClassLoader(this.getParent()) );
        }
    
        @Override
        protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
        {
            try
            {
                // first we try to find a class inside the child classloader
                return childClassLoader.findClass(name);
            }
            catch( ClassNotFoundException e )
            {
                // didn't find it, try the parent
                return super.loadClass(name, resolve);
            }
        }
    }
    
    (有关我找到的解决方案的更新,请参见底部)

    AntClassLoader似乎支持父first/last(尚未测试)

    这里是一个片段

    /**
     * Creates a classloader for the given project using the classpath given.
     *
     * @param parent The parent classloader to which unsatisfied loading
     *               attempts are delegated. May be <code>null</code>,
     *               in which case the classloader which loaded this
     *               class is used as the parent.
     * @param project The project to which this classloader is to belong.
     *                Must not be <code>null</code>.
     * @param classpath the classpath to use to load the classes.
     *                  May be <code>null</code>, in which case no path
     *                  elements are set up to start with.
     * @param parentFirst If <code>true</code>, indicates that the parent
     *                    classloader should be consulted  before trying to
     *                    load the a class through this loader.
     */
    public AntClassLoader(
        ClassLoader parent, Project project, Path classpath, boolean parentFirst) {
        this(project, classpath);
        if (parent != null) {
            setParent(parent);
        }
        setParentFirst(parentFirst);
        addJavaLibraries();
    }
    
    更新:

    同样,当我在谷歌(ChildFirstURLClassLoader就是这样产生的)中作为最后手段开始猜测类名时,我也发现了这一点——但这似乎是不正确的

    更新2:

    第一个选项(AntClassLoader)与Ant非常耦合(需要一个项目上下文,并且不容易向其传递
    URL[]

    第二个选项(来自一个)不是我所需要的,因为它在系统类加载器之前搜索父类加载器(顺便说一句,Ant类加载器做得很正确)对于JDK1.4中没有但在1.5中添加的功能,作为父类最后一个类加载器(常规委托模型,例如URLClassLoader),这没有什么害处将始终首先加载JDK的类,但在这里,child first naive实现似乎揭示了父类加载程序中旧的冗余jar,隐藏了JDK/JRE自己的实现

    我还没有找到一个经过认证的、经过充分测试的、成熟的父级Last/Child First正确实现,它没有与特定的解决方案(Ant、Catalina/Tomcat)耦合

    更新3-我找到了! 我找错地方了

    我所做的只是添加了
    META-INF/services/javax.xml.transform.TransformerFactory
    ,并恢复了JDK的
    com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl
    ,而不是旧的xalan的
    org.apache.xalan.processor.TransformerFactoryImpl


    我还没有“接受我自己的答案”的唯一原因是,我不知道
    META-INF/services
    方法是否具有与常规类相同的类加载器委托(例如,是父级优先/子级最后还是父级最后/子级优先?)下面的代码就是我使用的代码。与其他答案相比,它的优点是不会破坏父链(您可以遵循
    getClassLoader().getParent()

    与tomcat的WebappClassLoader相比,它还有一个优势,即不需要重新发明轮子,也不依赖于其他对象
                    Class<?> loaded = super.findLoadedClass(name);
                    if( loaded != null )
                        return loaded;
    
    /**
     * Creates a classloader for the given project using the classpath given.
     *
     * @param parent The parent classloader to which unsatisfied loading
     *               attempts are delegated. May be <code>null</code>,
     *               in which case the classloader which loaded this
     *               class is used as the parent.
     * @param project The project to which this classloader is to belong.
     *                Must not be <code>null</code>.
     * @param classpath the classpath to use to load the classes.
     *                  May be <code>null</code>, in which case no path
     *                  elements are set up to start with.
     * @param parentFirst If <code>true</code>, indicates that the parent
     *                    classloader should be consulted  before trying to
     *                    load the a class through this loader.
     */
    public AntClassLoader(
        ClassLoader parent, Project project, Path classpath, boolean parentFirst) {
        this(project, classpath);
        if (parent != null) {
            setParent(parent);
        }
        setParentFirst(parentFirst);
        addJavaLibraries();
    }
    
    public class ChildFirstURLClassLoader extends URLClassLoader {
    
        private ClassLoader system;
    
        public ChildFirstURLClassLoader(URL[] classpath, ClassLoader parent) {
            super(classpath, parent);
            system = getSystemClassLoader();
        }
    
        @Override
        protected synchronized Class<?> loadClass(String name, boolean resolve)
                throws ClassNotFoundException {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                if (system != null) {
                    try {
                        // checking system: jvm classes, endorsed, cmd classpath, etc.
                        c = system.loadClass(name);
                    }
                    catch (ClassNotFoundException ignored) {
                    }
                }
                if (c == null) {
                    try {
                        // checking local
                        c = findClass(name);
                    } catch (ClassNotFoundException e) {
                        // checking parent
                        // This call to loadClass may eventually call findClass again, in case the parent doesn't find anything.
                        c = super.loadClass(name, resolve);
                    }
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    
        @Override
        public URL getResource(String name) {
            URL url = null;
            if (system != null) {
                url = system.getResource(name); 
            }
            if (url == null) {
                url = findResource(name);
                if (url == null) {
                    // This call to getResource may eventually call findResource again, in case the parent doesn't find anything.
                    url = super.getResource(name);
                }
            }
            return url;
        }
    
        @Override
        public Enumeration<URL> getResources(String name) throws IOException {
            /**
            * Similar to super, but local resources are enumerated before parent resources
            */
            Enumeration<URL> systemUrls = null;
            if (system != null) {
                systemUrls = system.getResources(name);
            }
            Enumeration<URL> localUrls = findResources(name);
            Enumeration<URL> parentUrls = null;
            if (getParent() != null) {
                parentUrls = getParent().getResources(name);
            }
            final List<URL> urls = new ArrayList<URL>();
            if (systemUrls != null) {
                while(systemUrls.hasMoreElements()) {
                    urls.add(systemUrls.nextElement());
                }
            }
            if (localUrls != null) {
                while (localUrls.hasMoreElements()) {
                    urls.add(localUrls.nextElement());
                }
            }
            if (parentUrls != null) {
                while (parentUrls.hasMoreElements()) {
                    urls.add(parentUrls.nextElement());
                }
            }
            return new Enumeration<URL>() {
                Iterator<URL> iter = urls.iterator();
    
                public boolean hasMoreElements() {
                    return iter.hasNext(); 
                }
                public URL nextElement() {
                    return iter.next();
                }
            };
        }
    
        @Override
        public InputStream getResourceAsStream(String name) {
            URL url = getResource(name);
            try {
                return url != null ? url.openStream() : null;
            } catch (IOException e) {
            }
            return null;
        }
    
    }
    
    
    /**
     * Always throws {@link ClassNotFoundException}. Is called if parent class loader
     * did not find class.
     */
    @Override
    protected final Class findClass(String name)
            throws ClassNotFoundException
    {
        throw new ClassNotFoundException();
    }
    
    @Override
    protected Class loadClass(String name, boolean resolve)
            throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)){
            /*
             * Check if we have already loaded this class.
             */
            Class c = findLoadedClass(name);
    
            if (c == null){
                try {
                    /*
                     * We haven't previously loaded this class, try load it now
                     * from SUPER.findClass()
                     */
                    c = super.findClass(name);
                }catch (ClassNotFoundException ignore){
                    /*
                     * Child did not find class, try parent.
                     */
                    return super.loadClass(name, resolve);
                }
            }
    
            if (resolve){
                resolveClass(c);
            }
    
            return c;
        }
    }