当一个类的多个版本由不同的类加载器提供时,我如何引用特定的类?

时间:2014-09-19 22:21:03

标签: java intellij-idea

此问题源于IntelliJ GUI设计器,它似乎在包含其自己的旧版MigLayout的环境中加载自定义表单组件。

如果我创建使用MigLayout的自定义组件:

import net.miginfocom.layout.CC;
import net.miginfocom.layout.LC;
import net.miginfocom.swing.MigLayout;
import javax.swing.*;
public class ComponentUsingMigLayout extends JPanel {
   public ComponentUsingMigLayout() {
      try {
         setLayout(new MigLayout(new LC().insets("20")));
         add(new JButton("Hello"), new CC().cell(0, 0));
      }
      catch (Throwable t) {
         JOptionPane.showMessageDialog(null, t.toString());
      }
   }
}

然后创建一个GUI表单,执行"添加组件到调色板",然后选择ComponentUsingMigLayout,我看到我的消息对话框java.lang.NoSuchMethodError: net.miginfocom.layout.CC.cell([I)Lnet/miginfocom/layout/CC

我猜这是因为IntelliJ加载了自己的MigLayout版本,可用于表单组件,这是一个缺少可变参数cell()方法的旧版本。我知道由于依赖"com.miglayout" % "miglayout-core" % "4.2",运行时也应该有更新版本的MigLayout。

这是complete example including IDEA project files

有没有办法强制使用"com.miglayout" % "miglayout-core" % "4.2"

1 个答案:

答案 0 :(得分:0)

解决方案是检测GUI设计者正在加载类的情况,并剖析类加载器以删除父级:

package guidesignermiglayout;

import javax.swing.*;
import java.awt.*;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;

@SuppressWarnings({"unchecked", "ToArrayCallWithZeroLengthArrayArgument"})
public class ComponentUsingMigLayout extends JPanel {
   public ComponentUsingMigLayout() {
      try {
         ClassLoader cl = getClass().getClassLoader();

         ClassLoader localCL;
         if (cl.getClass().getName().contains("DesignTimeClassLoader")) {
            localCL = new URLClassLoader(((List<URL>) cl.getClass().getMethod("getUrls").invoke(cl)).toArray(new URL[]{}));
         }
         else {
            localCL = cl;
         }

         Class migLayout = localCL.loadClass("net.miginfocom.swing.MigLayout");

         Class lc = localCL.loadClass("net.miginfocom.layout.LC");
         Method insets = lc.getDeclaredMethod("insets", String.class);

         Class cc = localCL.loadClass("net.miginfocom.layout.CC");
         Method cell = cc.getDeclaredMethod("cell", int[].class);

         setLayout((LayoutManager) migLayout.getConstructor(lc).newInstance(
              insets.invoke(lc.newInstance(), "20")
         ));
         add(new JButton("Hello"), cell.invoke(cc.newInstance(), new Object[]{new int[]{0, 0}}));
      }
      catch (Throwable t) {
         JOptionPane.showMessageDialog(null, t.toString());
      }
   }
}