使用Classloader创建的对象无接口调用或反映Invoke
Use object created by Classloader without interface call or reflect invoke?
我正在使用java做一些与C 动态库使用相同的事情。
我没有找到直接使用同一类对象的方法,而没有反射调用样式代码。
这是我的动态库代码,我将其制作为jar。
package com.demo;
public class Logic {
public String doWork() {
System.out.println("Hello from Dll");
return "Dll";
}
}
在我的主要应用程序中,我可以通过urlclassloader创建实例,通过反射调用很好:
public class Main {
public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
File file = new File("C:\plugin.jar");
URL url = file.toURI().toURL();
URL[] urls = {url};
ClassLoader parentLoader = Thread.currentThread().getContextClassLoader();
ClassLoader loader = new URLClassLoader(urls, parentLoader);
Thread.currentThread().setContextClassLoader(loader);
Class<?> clazz = loader.loadClass("com.demo.Logic");
System.out.println("New Instance!!");
Object logic = clazz.newInstance();
Method method = logic.getClass().getMethod("doWork");
method.invoke(logic);
}
输出:
New Instance!!
Hello from Dll
但是,当我不使用反射Invoke而更改代码时:
public class Main {
public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
File file = new File("C:\plugin.jar");
URL url = file.toURI().toURL();
URL[] urls = {url};
ClassLoader parentLoader = Thread.currentThread().getContextClassLoader();
ClassLoader loader = new URLClassLoader(urls, parentLoader);
Thread.currentThread().setContextClassLoader(loader);
Class<?> clazz = loader.loadClass("com.demo.Logic");
Logic logic = (Logic)clazz.newInstance();
logic.doWork();
}
}
编译成功(使用外部模块编译),但是当我运行程序时,它在线路Logic logic = (Logic)clazz.newInstance();
例外:
Exception in thread "main" java.lang.NoClassDefFoundError: com/demo/Logic
at Main.main(Main.java:31)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
Caused by: java.lang.ClassNotFoundException: com.demo.Logic
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
... 6 more
有什么办法可以使它起作用?没有反射/接口。(在C 中,我可以轻松实现此目标,共享相同的结构/类声明,请确保使用相同的编译器编译两个部分。ImhoJava也会做到这一点)
)附加说明1
我想更改当前的classloader行为以使其识别动态加载类,这种尝试是简单而天真的,找不到其他方向:
ClassLoader parentLoader = Thread.currentThread().getContextClassLoader();
ClassLoader loader = new URLClassLoader(urls, parentLoader);
Thread.currentThread().setContextClassLoader(loader);
为了使这项工作,您必须从逻辑上将类分为三组:
- 您的主要班级
- 您的插件类
- 您的插件依赖类
创建新的类加载程序时,必须确保类#2 和#3的类都由同一类加载程序加载,因为urlclclassloader委托只朝着父级。这意味着加载主类的JVM应用程序类加载程序中的类无法在新类加载程序中看到类。为了像C这样的工作,您必须更新主要类的类路径,并且不支持这一点(这是可能的,但不是支持;我读过,我读过Java 9会删除此功能)。
实际上,您应该将主类分为两部分(#1和#3),然后使用反射来加载/调用插件依赖性类(一个反射调用,以及您的插件依赖性类别的类别可运行,您可以使用((Runnable)loadClass("PluginDependent").newInstance()).run()
减少)。但是,您需要确保您的UrlClassloader不会委派插件依赖类的负载,例如:
-
将您的应用程序拆分为上面列出的三个离散集(main.jar,plugin.jar和main-plugin dipendent.jar),并将所有这些设置列在urllclassloader上。
-
更改URLCLASSLOADER的创建以指定显式无效父母,以便它不会委派给JVM应用程序类加载程序,然后指定plugin.jar.jar.jar 和您的主罐子。
-
编写一个自定义的URLCLASSLOADER,该URLCLASSLOADER覆盖loadClass,以确保您的插件依赖类的类是由该类加载程序加载而不是委派给JVM应用程序类加载程序。
我用少量更改测试了您的代码(使用的默认类加载程序并将逻辑放置在我的类路径中)。这有效:
public class Main {
public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
ClassLoader parentLoader = Thread.currentThread().getContextClassLoader();
Class<?> clazz = parentLoader.loadClass("com.demo.Logic");
Logic logic = (Logic)clazz.newInstance();
logic.doWork();
}
}
您的问题在于从插件中检索课程。
更新:
我还尝试从第二个示例中的代码从jar中检索类。我将编译的Logic.Class放入com emo中,并用jar cvf plugin.jar .comdemoLogic.class
构建了罐子,并与您相同的路径。它也没有问题。
另外,您无需将当前的线程类加载程序设置为加载程序。至少不是出于此示例的目的。
您的插件实际上可能不包含类。
更新到:
其他说明1
我想更改当前的classloader行为以使其识别 动态加载类,这种尝试是简单而天真的,找不到 其他方向:
ClassLoader parentLoader = Thread.currentThread().getContextClassLoader(); ClassLoader loader = new URLClassLoader(urls, parentLoader); Thread.currentThread().setContextClassLoader(loader);
您正在使用子级装载机做正确的事情。
但是,如果要"更改当前的classloader行为",则应阅读此答案
classloader是不可变的;你应该不能 Willy-nilly在运行时添加类。
因此,您提出了一个子类装载机的解决方案。
这就是为什么我说"您不需要将当前的线程类加载程序设置为加载程序( Child class Loader )。"
- 什么时候调用组成单元对象的析构函数
- 对RValue对象调用的LValue ref限定成员函数
- 为什么使用 "this" 指针调用派生成员函数?
- 函数调用中参数的顺序重要吗
- 为什么使用 P/Invoke 调用 dll 时,某些计算机中的 LoadLibrary 失败?
- 无法通过 std::ref() 使用 auto& 参数调用 std::invoke()
- 为什么INVOKE总是取消引用数据成员,而不是在可能的情况下调用
- 使用 std::invoke 调用模板化函数
- 何时使用 std::invoke 而不是简单地调用可调用的?
- 使用Classloader创建的对象无接口调用或反映Invoke
- P/Invoke调用失败
- 关于从C++调用C#函数的问题,在windows phone上反向P/Invoke
- 修改后的std::invoke/std::apply,将可调用项作为void*-possible
- 如何使用P/Invoke从64位C#应用程序调用64位C++DLL
- 如何使用所需的BSTR*参数正确调用IDispatch::Invoke
- 如何使用IDispatch.Invoke调用接收浮点值的函数?
- 如何使用p/invoke终止从c#调用的c++函数
- 为什么要使用invoke helper而不是调用functor呢?
- visual Is . lib文件需要在c++中调用DLL,但不用于c# P/Invoke
- 通过P/Invoke包装器或在c#中运行进程调用本机代码的性能