是否可以加载类而不加载引用的类/导入? [英] Is it possible to load a class without loading referenced classes/imports?

查看:169
本文介绍了是否可以加载类而不加载引用的类/导入?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(是的,这是黑客,可能不是最佳实践,但它是最小的解决方案)



我有一个涉及几个罐子的项目 - 一个可运行的启动器,服务器,服务器的包装器和服务器的插件。



启动器通过启动一个新的未连接的进程,一个子进程或只是由实例化,取决于配置。对于这个问题,这应该是不重要的。



包装器使用URLClassLoader加载服务器jar并启动它,这很正常。



在启动服务器之前,包装器会查找包含服务器中通常使用的某些类路径/文件的插件,并加载它们,修补该类的正常版本。



问题是类加载器想要自动解析每个类,并导入插件补丁类文件,而且还没有加载服务器。



我需要阻止类加载器解析导入,或者在服务器之后加载类,并用已经加载的类替换它们。据我所知,第二个选项是不可能的,不稳定和字节码操作?

解决方案

首先,回答你的标题问题:


是否可以加载类而不加载引用的类/导入?


您可以选择不通过将false传递给类api / java / lang / ClassLoader.html#loadClass(java.lang.String,%20boolean)rel =nofollow> loadClass 。有关解析课程的内容的详细信息,请参见此处。然后,您可以通过调用 resolveClass





$ b $但是,如果这不能让你满意,我不知道这是否是唯一可能的解决方案,这当然是可怕的(而且从一开始就有更好的方法去处理),但是如果你这样做:



我将打电话给你的类作为补丁,并且必须在服务器预加载类/代码之前加载。我打算调用具有服务器依赖性的补丁的组件,但是必须延迟加载,直到服务器加载后加载类/代码。



对于每个插件:


  1. 将所有后加载的东西从补丁类中删除,并将其移动到不同的类,例如或者某些东西。 PluginImplementation 然后使这个实现类的一个实例成为你的插件类的一个成员,将任何必要的成员函数委托给它,但不要在蝙蝠之前实例化一个 PluginImplementation ,并使成员字段的类型为 Object (这通常被描述为不透明指针/ pimpl模式)。实质上,您将重构使用pimpl成语,您的预加载内容直接编码,您的后期加载的东西实际上被委托给隐藏在 Object 而不是立即初始化。 您的目标是从插件类本身中删除服务器类的所有依赖关系,将其更改为补丁只需加载所需的裸机,但是将所有的肉都移动到一个最终隐藏在不透明指针后面的实现类。


  2. 现在正常加载所有的插件。他们现在应该加载,并且由于所有具有服务器依赖关系的后加载东西已经从其中删除,所以服务器类将不会被加载。


  3. 现在,您的插件会显示某些 serverLoaded() initializePlugin()方法或类似的东西。 在服务器类加载之后,请在每个加载的插件上执行并调用这些。


  4. 最后,您可以使用 Class.forName()。newInstance() 将您的插件实例化为后加载类


所以,基本上,你将所有的后加载东西隐藏在一个不透明的指针后面,从而保持它隐藏在类加载器中,那么当它是时候,您动态实例化各种 PluginImplementation 类,从而使您的插件完全完成,但允许服务器相关的部件延迟加载。



缺点是这增加了一些限制,需要非常小心。您需要确保在调用服务器加载和初始化函数之前,不会调用 PluginImplementation -delegated方法,因为实现类不会被实例化但是,我确定可能有更好的选择,但这是我可以想到的那个,可能涉及到最不重要的工作量你已经有了你必须移动很多代码;一个像Eclipse这样的IDE,或者任何可以使代码轻松分配的代码(只是暂时使成员字段成为一个 PluginImplementation 来帮助IDE,然后可以将其更改回 Object 在生成所有代码之后),但它应该最小化*现有架构的基本*更改。






这是另一个想法,虽然我不确定它适合您当前的代码,或者甚至可能在您的情况下:



基本上,这里的目标是通过执行以下操作只是使插件不再依赖服务器本身


  1. 将您的服务器声明为界面

  2. 让具体的服务器实现实现该接口。

  3. 更改所有插件以在该接口上操作,而不是在服务器类本身的实例上。

  4. 确保您的服务器类不声明插件可能使用的任何内部类:将任何内部类分成顶层(IDE就像Eclipse可以为您做到这一点)。

现在你必须找出一种方式来告诉每个插件服务器的工作原理,但这个插件会将它存储在基础接口类型的成员中。



这样,加载插件将不会加载服务器本身,而只是基本界面。



我认为这个想法是一个地狱比上述更简单,更少黑客,我只是不知道它是否比以上更可行。






请注意,这两个选项不一定保证成功,但在实践中他们可能会工作。


(Yes, this is hacky and probably not best practice, but it is the least bulky solution)

I have a project that involves several jars - a runnable launcher, a server, a wrapper for the server, and plugins for the server.

The launcher runs the wrapper by starting a new unconnected process, a child process, or just by instantiating it, depending on config. This should be unimportant with regards to the issue.

The wrapper uses URLClassLoader to load the server jar and start it, which works fine.

Before starting the server, the wrapper looks for plugins that contain certain classpaths/files normally used in the server, and loads them, patching the normal version of the class.

The problem is that the classloader wants to automatically resolve every class and import in the plugin patch classfiles, and the server has not been loaded yet.

I need to either prevent the classloader from resolving imports, or load the classes after the server and replace the already-loaded classes with them. As far as i know the second option is impossible without instability and bytecode manipulation?

解决方案

First of all, answering your title question:

Is it possible to load a class without loading referenced classes/imports?

Well you could choose not to resolve the class by passing false to loadClass. Details about what resolving a class entails can be found here. You could then do the resolution in a separate step by calling resolveClass explicitly.


However, if that doesn't get you what you want, I don't know if this is the only possible solution, and it's certainly awful (and there were definitely better ways to approach this from the start), but what if you do something like this:

I'm going to call your classes that serve as patches and must be loaded before the server "pre-load" classes / code. I'm going to call the components of your patches that have server dependencies but whose loading must be delayed until after the server loads "post-load" classes / code.

For each of your plugins:

  1. Strip all the post-load stuff out of the patch class and move it to a different class, e.g. PluginImplementation or something. Then make an instance of that implementation class be a member of your plugin class, delegating any necessary member functions to it, but do not instantiate a PluginImplementation right off the bat, and make the member field be of type Object (this is often described as an opaque pointer / the pimpl pattern). Essentially you're refactoring to use the pimpl idiom, where your pre-load stuff is coded directly, and your post-load stuff is actually delegated to a different class hidden behind an Object and not initialized immediately. Your goal is to remove all dependencies on server classes from the plugin class itself, changing it to be the bare minimum necessary for the patch to just be loaded, but moving all the meat to an implementation class ultimately hidden behind an opaque pointer.

  2. Now load all of your plugins as normal. They should load now, and since all the post-load stuff that has server dependencies has been removed from them, the server classes won't be loaded.

  3. Now have your plugins expose some sort of serverLoaded(), or initializePlugin() method or something like that. After the server class loads, go through and call these on each loaded plugin.

  4. Finally, in the initialization method from the previous step, have your plugin instantiate the post-load classes using Class.forName().newInstance().

So, basically, you hide all your post-load stuff behind an opaque pointer, thus keeping it hidden from the class loader, then when it's time, you dynamically instantiate the various PluginImplementation classes, thus making your plugins "fully complete" but allowing the server-dependent parts to be delay loaded.

The downside is this adds a few limitations and requires a lot of care. You need to make sure that none of your PluginImplementation-delegated methods are called until after the server loads and the initialization functions are called, because the implementation classes won't have been instantiated yet.

I am sure there may be better options but this is the one I could think of that probably involves the least amount of work given what you already have. You'll have to move a lot of code around; an IDE like Eclipse or whatever can make spitting out delegates easy at least (just temporarily make the member field be a PluginImplementation to help the IDE along then you can change it back to Object after you generate all the code), but it should minimize *fundamental * changes to your existing architecture.


Here's another idea, although I'm not sure how well it fits in with your current code, or if it's even possible in your situation:

Basically, the goal here is to just make the plugins no longer be dependent on the server itself, by doing the following:

  1. Declare your server as an interface.
  2. Make your concrete server implementation implement that interface.
  3. Change all plugins to operate on that interface instead of on an instance of the server class itself.
  4. Make sure your server class doesn't declare any inner classes that the plugins may use: Break any inner classes out into the top level (IDE's like Eclipse can do this for you).

Now you'll have to figure out a way to tell each plugin the instance of the server it's working with, but the plugin will store this in a member with the type of the base interface.

This way, loading the plugins won't load the server itself, only the base interface.

I think this idea is a hell of a lot simpler and less hackier than the above, I just don't know if it's more or less feasible than the above.


Note that both of these options don't necessarily guarantee success, but in practice they'll probably work.

这篇关于是否可以加载类而不加载引用的类/导入?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆